Spring is, among other things, an Inversion of Control framework. As of WebWork 2.2, it is the recommended IoC container. You can find out more about Spring at http://www.springframework.org.

This section covers the only supported Spring integration technique. However, there are many other ways to tie in to Spring with WebWork. Please see Other Spring Integration for more info. Note that none of the other methods are currently supported and could change at any time!

Enabling Spring Integration

Turning on Spring support in WebWork is simply a matter of installing the latest Spring jars in to your classpath and then adding the following entry to webwork.properties:

webwork.objectFactory = spring

If you want to change from the default autowiring mode, which is to auto-wire by name (i.e. to look for beans defined in Spring with the same name as your bean property), then you'll also need a setting for this in your webwork.properties:

webwork.objectFactory.spring.autoWire = type

Options for this setting are:

name Auto-wire by matching the name of the bean in Spring with the name of the property in your action. This is the default
type Auto-wire by looking for a bean registered with Spring of the same type as the property in your action. This requires you to have only one bean of this type registered with Spring
auto Spring will attempt to auto-detect the best method for auto-wiring your action
constructor Spring will auto-wire the parameters of the bean's constructor

At this point, all objects will at least try to get created by Spring. If they cannot be created by Spring, then WebWork will create the object itself. Next, you'll need to turn on the Spring listener in web.xml:

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
More ApplicationContext configuration files needed?

Since the Spring integration uses a standard Listener, it can be configured to support configuration files other than applicationContext.xml.
Adding the following to your web.xml will cause Spring's ApplicationContext to be inititalized from all files matching the given pattern:

<!-- Context Configuration locations for Spring XML files -->
<context-param>
    <param-name>contextConfigLocation</param-name>
    <param-value>/WEB-INF/applicationContext-*.xml,classpath*:applicationContext-*.xml</param-value>
</context-param>

See the Spring documentation for a full description of this parameter.

Sample Spring Configuration

At this point, you can add the standard Spring configuration at WEB-INF/applicationContext.xml. An example of this configuration is:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans default-autowire="autodetect">
    <bean id="personManager" class="com.acme.PersonManager"/>
    ...
</beans>

Switching from Builtin IoC to Spring

Switching is quite easy. Spring setup is done as described above. To complete migration, you will have to

  1. transfer your configured components from components.xml to applicationContext.xml appropriately. You can safely delete components.xml afterwards.
  2. remove the Component Interceptor from your interceptor stack in xwork.xml. Although it does not hurt to leave it there, it is simply redundant from now on.
    Session Scope & Spring
    Spring <= 1.3 does not support session scoped components. Spring 2.0 release will add support for this, and tests with Spring 2.0M3 (beta) are reported to work quite well. Please refer to Spring Session Components Workarounds to read more about this topic.

Initializing Actions from Spring

Normally, in xwork.xml you specify the class for each action. When using the SpringObjectFactory (configured as shown above) WebWork will ask Spring to create the action and wire up dependencies as specified by the default auto-wire behavior. The SpringObjectFactory will also apply all bean post processors to do things like proxy your action for transactions, security, etc. which Spring can automatically determine without explicit configuration. For most usages, this should be all you need for configuring your actions to have services and dependencies applied.

We strongly recommend that you find declarative ways of letting Spring know what to provide for your actions. This includes making your beans able to be autowired by either naming your dependent properties on your action the same as the bean defined in Spring which should be provided (to allow for name-based autowiring), or using autowire-by-type and only having one of the required type registered with Spring. It also can include using JDK5 annotations to declare transactional and security requirements rather than having to explicitly set up proxies in your Spring configuration. If you can find ways to let Spring know what it needs to do for your action without needing any explicit configuration in the Spring applicationContext.xml, then you won't have to maintain this configuration in both places.

However, sometimes you might want the bean to be completely managed by Spring. This is useful, for example, if you wish to apply more complex AOP or Spring-enabled technologies, such as Acegi, to your beans. To do this, all you have to do is configure the bean in your Spring applicationContext.xml and then change the class attribute from your WebWork action in the xwork.xml to use the bean name defined in Spring instead of the class name.

Your xwork.xml file would then have the action class attributes changed, leaving it like this:

<!DOCTYPE xwork PUBLIC "-//OpenSymphony Group//XWork 1.0//EN" "http://www.opensymphony.com/xwork/xwork-1.1.dtd">

<xwork>
    <include file="webwork-default.xml"/>

    <package name="default" extends="webwork-default">
        <action name="foo" class="com.acme.Foo">
            <result>foo.ftl</result>
        </action>
    </package>

    <package name="secure" namespace="/secure" extends="default">
        <action name="bar" class="bar">
            <result>bar.ftl</result>
        </action>
    </package>
</xwork>

Where you have a Spring bean defined in your applicationContext.xml named "bar". Note that the com.acme.Foo action did not need to be changed, because it can be autowired.

A typical spring configuration for bar could look as following.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN//EN" "http://www.springframework.org/dtd/spring-beans.dtd">
<beans default-autowire="autodetect">
    <bean id="bar" class="com.my.BarClass" singleton="false"/>
    ...
</beans>

or if you are using Spring version 2.0,

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE beans PUBLIC "-//SPRING//DTD BEAN 2.0//EN" "http://www.springframework.org/dtd/spring-beans-2.0.dtd">
<beans default-autowire="autodetect">
    <bean id="bar" class="com.my.BarClass" scope="prototype"/>
    ...
</beans>

Note the id attribute in the spring configuration corresponds to the class attribute in the xwork configuration. Also note that in the spring configuration, the singleton attribute is set to false. This would generally be the case that is desired as Webwork creates a new action class upon each request. Hence when Spring integration is used, this would be the desired behaviour. Making Springs singleton attribute false would allow this.

Remember: this is not required. This is only needed if you wish to override the default behavior when the action is created in WebWork by decorating it with Spring-enabled interceptors and IoC that cannot be automatically determined by Spring. Keep in mind that WebWork's Spring integration will do standard IoC, using whatever auto-wiring you specify, even if you don't explicitely map each action in Spring. So typically you don't need to do this, but it is good to know how this can be done if you need to.

Spring AOP and WebWork Actions

By default Spring relies on you using interface based proxying. As you are likely to not have interfaces for your actions you will need to use CGLib for proxying.

Turn on class proxying in your Spring context.

<aop:config proxy-target-class="true" />

Also ensure CGLib is then on your classpath. This will apply CGLib proxying to all classes. Consider delegating to a service layer.