The Spring integration module allows easy migration of Spring-based projects to Seam and allows Spring applications to take advantage of key Seam features like conversations and Seam's more sophisticated persistence context management.
Seam's support for Spring provides the ability to:
inject Seam component instances into Spring beans
inject Spring beans into Seam components
turn Spring beans into Seam components
allow Spring beans to live in any Seam context
Injecting Seam component instances into Spring beans is accomplished using the <seam:instance/> namespace handler. To enable the Seam namespace handler, the Seam namespace must be added to the Spring beans definition file:
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:seam="http://jboss.com/products/seam/spring" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-2.0.xsd http://jboss.com/products/seam/spring http://jboss.com/products/seam/spring-1.1.xsd">
Now any Seam component may be injected into any Spring bean:
<bean id="someSpringBean" class="SomeSpringBeanClass" scope="prototype"> <property name="someProperty"> <seam:instance name="someComponent"/> </property> </bean>
An EL expression may be used instead of a component name:
<bean id="someSpringBean" class="SomeSpringBeanClass" scope="prototype"> <property name="someProperty"> <seam:instance name="#{someExpression}"/> </property> </bean>
Seam component instances may even be made available for injection into Spring beans by a Spring bean id.
<seam:instance name="someComponent" id="someSeamComponentInstance"/> <bean id="someSpringBean" class="SomeSpringBeanClass" scope="prototype"> <property name="someProperty" ref="someSeamComponentInstance"> </bean>
Now for the caveat!
Seam was designed from the ground up to support a stateful component model with multiple contexts. Spring was not. Unlike Seam bijection, Spring injection does not occur at method invocation time. Instead, injection happens only when the Spring bean is instantiated. So the instance available when the bean is instantiated will be the same instance that the bean uses for the entire life of the bean. For example, if a Seam CONVERSATION-scoped component instance is directly injected into a singleton Spring bean, that singleton will hold a reference to the same instance long after the conversation is over! We call this problem scope impedance. Seam bijection ensures that scope impedance is maintained naturally as an invocation flows through the system. In Spring, we need to inject a proxy of the Seam component, and resolve the reference when the proxy is invoked.
The <seam:instance/> tag lets us automatically proxy the Seam component.
<seam:instance id="seamManagedEM" name="someManagedEMComponent" proxy="true"/> <bean id="someSpringBean" class="SomeSpringBeanClass"> <property name="entityManager" ref="seamManagedEM"> </bean>
This example shows one way to use a Seam-managed persistence context from a Spring bean. (A more robust way to use Seam-managed persistence contexts as a replacement for the Spring OpenEntityManagerInView filter will be provided in a future release)
It is even easier to inject Spring beans into Seam component instances. Actually, there are two possible approaches:
inject a Spring bean using an EL expression
make the Spring bean a Seam component
We'll discuss the second option in the next section. The easiest approach is to access the Spring beans via EL.
The Spring DelegatingVariableResolver is an integration point Spring provides for integrating Spring with JSF. This VariableResolver makes all Spring beans available in EL by their bean id. You'll need to add the DelegatingVariableResolver to faces-config.xml:
<application> <variable-resolver> org.springframework.web.jsf.DelegatingVariableResolver </variable-resolver> </application>
Then you can inject Spring beans using @In:
@In("#{bookingService}") private BookingService bookingService;
The use of Spring beans in EL is not limited to injection. Spring beans may be used anywhere that EL expressions are used in Seam: process and pageflow definitions, working memory assertions, etc...
The <seam:component/> namespace handler can be used to make any Spring bean a Seam component. Just place the <seam:component/> tag within the declaration of the bean that you wish to be a Seam component:
<bean id="someSpringBean" class="SomeSpringBeanClass" scope="prototype"> <seam:component/> </bean>
By default, <seam:component/> will create a STATELESS Seam component with class and name provided in the bean definition. Occasionally, such as when a FactoryBean is used, the class of the Spring bean may not be the class appearing in the bean definition. In such cases the beanClass should be explicitly specified. A Seam component name may be explicitly specified in cases where there is potential for a naming conflict.
The scope attribute of <seam:component/> may be used if you wish the Spring bean to be managed in a particular Seam scope. The Spring bean must be scoped to prototype if the Seam scope specified is anything other than STATELESS. Pre-existing Spring beans usually have a fundamentally stateless character, so this attribute is not usually needed.
The Seam integration package also lets you use Seam's contexts as Spring 2.0 style custom scopes. This lets you declare any Spring bean in any of Seam's contexts. However, note once again that Spring's component model was never architected to support statefulness, so please use this feature with great care. In particular, clustering of session or conversation scoped Spring beans is deeply problematic, and care must be taken when injecting a bean or component from a wider scope into a bean of a narrower scope.
By specifying <seam:configure-scopes/> once in a Spring bean factory configuration, all of the Seam scopes will be available to Spring beans as custom scopes. To associate a Spring bean with a particular Seam scope, specify the Seam scope in the scope attribute of the bean definition.
<!-- Only needs to be specified once per bean factory--> <seam:configure-scopes/> ... <bean id="someSpringBean" class="SomeSpringBeanClass" scope="seam.CONVERSATION"/>
The prefix of the scope name may be changed by specifying the prefix attribute in the configure-scopes definition. (The default prefix is seam.)
Seam-scoped Spring beans defined this way can be injected into other Spring beans without the use of <seam:instance/>. However, care must be taken to ensure scope impedance is maintained. The normal approach used in Spring is to specify <aop:scoped-proxy/> in the bean definition. However, Seam-scoped Spring beans are not compatible with <aop:scoped-proxy/>. So if you need to inject a Seam-scoped Spring bean into a singleton, <seam:instance/> must be used:
<bean id="someSpringBean" class="SomeSpringBeanClass" scope="seam.CONVERSATION"/> ... <bean id="someSingleton"> <property name="someSeamScopedSpringBean"> <seam:instance name="someSpringBean" proxy="true"/> </property> </bean>