Tapestry/Spring Integration

Provides integration between Tapestry and Spring, allowing beans defined by Spring to be injected into Tapestry IoC services, and into Tapestry components.

Version

This module is compiled and tested against Spring 1.2.8. However, Spring 2.0 is fully backwards compatible to Spring 1.2.8.

This module uses the Maven scope "provided" for the dependencies on Spring. This means that in your own POM, you will need to specify your own dependency to Spring, including the correct version. Example:

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-web</artifactId>
  <version>1.2.8</version>
</dependency>    

With the default Maven scope, the Spring JARs and dependencies will be packaged into your application's WAR file.

Usage

The integration is designed to be a very thin layer on top of Spring's normal configuration for a web application.

Detailed instructions are available in the Spring 1.2.x documentation.

web.xml changes

The short form is that you must make two small changes to your application's web.xml.

First, a special filter is used in replace of the standard TapestryFilter:

  <filter>
    <filter-name>app</filter-name>
    <!-- Special filter that adds in a T5 IoC module derived from the Spring WebApplicationContext. -->
    <filter-class>org.apache.tapestry5.spring.TapestrySpringFilter</filter-class>
  </filter>

Secondly, you must add the normal Spring configuration, consisting of a <listener> element, and (optionally) a <context-param> identifying which Spring bean configuration file(s) to load:

<context-param>
  <param-name>contextConfigLocation</param-name>
  <param-value>/WEB-INF/daoContext.xml /WEB-INF/applicationContext.xml</param-value>
</context-param>

<listener>
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

The <context-param> lists the Spring bean configuration file. It is optional and defaults to just /WEB-INF/applicationContext.xml if omitted.

The ContextLoaderListener is responsible for reading the bean configuration file (or files) and storing the result in the application context, where the Tapestry-Spring integration code can make use of it.

Injecting beans

Inside your component classes, you may use the Inject annotation. Typically, just the field type is sufficient to identify the Spring bean to inject:

  @Inject
  private UserDAO userDAO;

If you have multiple beans that implement the same interface (for instance, if you have wrapped your bean using a transaction interceptor), you must disambiguate. The easiest way to accomplish this is to add a Service annotation to identify the name of the Spring bean:

  @Inject
  @Service("UserDAO")
  private UserDAO userDAO;

Injection of Spring beans via service builder methods or autobuilding occurs just the same: the Spring beans masquerade as Tapestry IoC services and all is well.

Case Insensitivity

Spring beans names are treated exactly as Tapestry IoC service ids. Since service ids are case insensitive, access to Spring beans by bean name will also be case insensitive.

WebApplicationContext Service

The Spring WebApplicationContext is also added as a service, in addition to any services defined within the context.

Limitations

The names of beans are obtained at application start up. If new beans are programattically added to the Spring application context at runtime, these will not be visible for injection.

Only the bean name is used, not any of the bean's aliases.

No check is made for name clashes that would occur when two beans have names that differ only in terms of capitalization. If you're going to go around naming beans "userDAO" and "UserDao", you're just asking for trouble.

Non-singleton beans are not handled properly. Tapestry will request the beans from the application context in a manner unsuitable for their lifecycle. For the moment, you should consider the non-singleton beans to be not-injectable. Instead, inject the WebApplicationContext service and obtain the non-singleton beans as needed.