ICE09 . playing with java, scala, groovy and spring .

Playing with Spring

Archive for the ‘Spring’ Category

Integrating Spring 3 and MyBatis 3 using the Spring JDBC namespace

Posted by ice09 on July 25, 2011

Sources to this blog post are available here. These can be used with Maven 3 and are best suitable for the SpringSource Tool Suite 2.7.1 (just import the unzipped project as “Existing Maven project”)

There are several tutorials about the integration of MyBatis 3 in Spring 3 applications, which is kind of special. Even more tutorials are available about the usage of the Spring-JDBC-namespace, mostly together with an embedded database for for unit tests.

This post will show how to combined these mechanisms using the SpringSource Tool Suite 2.7.1.
There is an explanation of the project artifacts first. Afterwards, a step-by-step guide shows the main steps for the creation of a skeleton of this sample project in the SpringSource Tool Suite.

The project structure

The unit test IntegrationTests.java

@ContextConfiguration
@RunWith(SpringJUnit4ClassRunner.class)
public class IntegrationTests {
	
	@Autowired
	private UserService service;

	@Test
	public void testGetFirstUser() throws Exception {
		assertNotNull(service);
		assertEquals("Customer1", service.getFirstUser());
	}
	@Test
	public void testFindUserByName() throws Exception {
		assertNotNull(service);
		assertEquals("0", service.findUserByName("Customer1"));
		assertEquals("1", service.findUserByName("Customer2"));
	}
}

The corresponding configuration file IntegrationTests-context.xml

<beans>
	<import resource="classpath:/META-INF/spring/app-context.xml"/> 

	<jdbc:embedded-database id="dataSource">
		<jdbc:script location="classpath:/de/ice09/blog/init-sql/schema.sql" />
		<jdbc:script location="classpath:/de/ice09/blog/init-sql/data.sql" />
	</jdbc:embedded-database>
</beans>

The referenced DDL-SQL for the (embedded test) database (hsqldb)

drop table customer if exists;
create table customer (id integer identity primary key, name varchar(50) not null );

The referenced SQL for initialization of the (embedded) database

insert into customer (name) values ('Customer1');
insert into customer (name) values ('Customer2');

Referenced app-context.xml – the main configuration file

<beans>
	<import resource="classpath:/META-INF/spring/dao-context.xml" />
	<context:component-scan base-package="de.ice09.blog" />
</beans>

The included dao-context.xml infrastructure and DAO configuration file

<beans>
	<!-- infrastructure -->
	<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
		<property name="dataSource" ref="dataSource" />
	</bean>
	<!-- MyBatis Mapper -->	
	<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
		<property name="mapperInterface" value="de.ice09.blog.dao.mybatis.UserMapper" />
		<property name="sqlSessionFactory" ref="sqlSessionFactory" />
	</bean>
</beans>

The service interface

public interface UserService {
	String getFirstUser();
	String findUserByName(String name);
}

The UserService implementation with injected DAO-Mapper

@Component
public class UserServiceImpl implements UserService {
	
	@Autowired
	private UserMapper userMapper;
	
	public String getFirstUser() {
		List<User> users = userMapper.getUsers();
		return users.get(0).getLastName();	
	}
	public String findUserByName(String name) {
		return userMapper.findUserByName(name).getId();
	}
}

The mapper with two possible configuration, the getUsers annotated config and the findUsersByName configuration in XML (see below)

public interface UserMapper {
	@Select("SELECT id, name as lastname FROM customer")
	List<User> getUsers();
	
	User findUserByName(String name);
}

The sample XML configuration as an alternative for the annotation configuration (for findUserByName)

<mapper namespace="de.ice09.blog.dao.mybatis.UserMapper">
	<resultMap id="userResultMap" type="de.ice09.blog.domain.User">
	    <id property="id" column="id" />
	    <result property="lastname" column="name"/>
	</resultMap>
 
	<select id="findUserByName" parameterType="string" resultMap="userResultMap"> 
    	    select id, name from customer where name = #{value}
  	</select>
</mapper>

Creating the Project – Step by Step

Create a new Spring Template Project in STS

Choose Spring Utility Template

Add namespace to Spring XML configuration

If this blog post was useful to you, you can tip me. Maybe you do not want to tip, but please inform yourself about Bitcoin and applications of Bitcoin like Youttipit.

Advertisements

Posted in MyBatis, Spring | 4 Comments »

Spring 3, REST, XML – running on the Google App Engine

Posted by ice09 on March 15, 2010

Note: The complete sourcecode is available on Google Code here. The projects can be imported as Eclipse 3.5.x or SpringSource Tool Suite 2.3.x projects (the Google Eclipse Plugin has to be installed for the gae project).

Introduction

Having already used Spring 3 in combination with REST and JSON on the Google App Engine, I will sum it up once again to…

  1. …explore the changes between the Spring Milestones and the Release (3.0.1)
  2. …use a specified format (using XML Schema) for data exchange
  3. …experiment with XML, data persistence on the GAE and Spring 3 REST capabilities

Intention

We will create a simple REST service which takes User objects, which itself include Location and Buddy references. The format will be XML, since we want to specify the format explicitly with means of XML Schema. Usual REST mechanisms will be used, therefore HTTP-GET and HTTP-POST is supported.

The User Model

The User XML Schema

<?xml version="1.0" encoding="UTF-8"?>
<schema xmlns="http://www.w3.org/2001/XMLSchema" targetNamespace="http://www.example.org/User"
	xmlns:tns="http://www.example.org/User" elementFormDefault="qualified">

	<element name="User">
		<complexType>
			<sequence>
				<element name="id" type="long" />
				<element name="firstName" type="string"></element>
				<element name="lastName" type="string"></element>
				<element name="birthday" type="dateTime"></element>
				<element name="buddies" type="tns:BuddyType" />
				<element name="locations" type="tns:LocationType" />
			</sequence>
		</complexType>
	</element>

	<complexType name="BuddyType">
		<sequence>
			<element name="id" type="long" />
			<element name="buddyid" type="long" minOccurs="0" maxOccurs="unbounded" />
		</sequence>
	</complexType>

	<complexType name="LocationType">
		<sequence>
			<element name="id" type="long" />
			<element name="location">
				<complexType>
					<sequence>
						<element name="longitude" type="int" minOccurs="1" maxOccurs="1" />
						<element name="latitude" type="int" minOccurs="1" maxOccurs="1" />
						<element name="timestamp" type="dateTime" minOccurs="1" maxOccurs="1" />
					</sequence>
				</complexType>
			</element>
		</sequence>
	</complexType>

</schema>

After a POST request has been sent, it will respond the submitted object, together with a Location attribute in the respond header, which identifies the new resource. It can be retrieved with a corresponding GET request.

The client will be using the RestTemplate. Furthermore, soapUI will be configured for client requests.
Finally, the generated project will be migrated to the Google App Engine.

Preparation

  1. Download either Eclipse IDE for Java EE Developers 3.5.2 or the SpringSource Tool Suite 2.3.1 (STS). Sample are done in STS, however the Eclipse JEE edition should be perfectly alright for these samples.
  2. Install the Google Plugin for Eclipse. This will enable Google App Engine (GAE) deployment later on.
  3. If you are not using the STS, you will have to install the m2eclipse plugin.

Setup

  1. If you want to use the Eclipse WTP features, like “Run on server…”, you will have to prepare your environment for a coexistence of Eclipse/WTP and Maven 2. Create a new project as described in this howto.
  2. Optionally:If you are having problems with Maven errors due to wring JRE settings, try setting the -vm argument in the eclipse.ini (or sts.ini) file as mentioned here.
  3. Depending on the setup of the maven-webapp-archetype, you might have to set the project compliance to Java 5 and create the src/main/java folder as well as add it to the classpath.

    The dependencies and plugin configurations in the main pom.xml

    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    	<modelVersion>4.0.0</modelVersion>
    	<groupId>org.example</groupId>
    	<artifactId>blogrestsample</artifactId>
    	<packaging>war</packaging>
    	<version>0.0.1-SNAPSHOT</version>
    	<name>blogrestsample Maven Webapp</name>
    	<repositories>
    		<repository>
    			<id>maven2-repository.dev.java.net</id>
    			<name>Java.net Maven 2 Repository</name>
    			<url>http://download.java.net/maven/2</url>
    		</repository>
    		<repository>
    			<id>maven-repository.dev.java.net</id>
    			<name>Java.net Maven 1 Repository (legacy)</name>
    			<url>http://download.java.net/maven/1</url>
    			<layout>legacy</layout>
    		</repository>
    		<repository>
    			<id>springsource maven repo</id>
    			<url>http://maven.springframework.org/milestone</url>
    		</repository>
    	</repositories>
    	<pluginRepositories>
    		<pluginRepository>
    			<id>maven2-repository.dev.java.net</id>
    			<url>http://download.java.net/maven/2</url>
    		</pluginRepository>
    		<pluginRepository>
    			<id>maven-repository.dev.java.net</id>
    			<url>http://download.java.net/maven/1</url>
    			<layout>legacy</layout>
    		</pluginRepository>
    	</pluginRepositories>
    	<dependencies>
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>org.springframework.web.servlet</artifactId>
    			<version>3.0.0.RC1</version>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>org.springframework.oxm</artifactId>
    			<version>3.0.0.RC1</version>
    		</dependency>
    		<dependency>
    			<groupId>commons-collections</groupId>
    			<artifactId>commons-collections</artifactId>
    			<version>3.2</version>
    		</dependency>
    		<dependency>
    			<groupId>javax.xml.bind</groupId>
    			<artifactId>jaxb-api</artifactId>
    			<version>2.0</version>
    		</dependency>
    		<dependency>
    			<groupId>javax.servlet</groupId>
    			<artifactId>servlet-api</artifactId>
    			<version>2.5</version>
    			<scope>provided</scope>
    		</dependency>
    	</dependencies>
    	<build>
    		<plugins>
    			<plugin>
    				<artifactId>maven-compiler-plugin</artifactId>
    				<configuration>
    					<source>1.5</source>
    					<target>1.5</target>
    				</configuration>
    			</plugin>
    			<plugin>
    				<groupId>org.mortbay.jetty</groupId>
    				<artifactId>maven-jetty-plugin</artifactId>
    				<version>6.1.10</version>
    				<configuration>
    					<scanIntervalSeconds>10</scanIntervalSeconds>
    					<stopKey>foo</stopKey>
    					<stopPort>9999</stopPort>
    				</configuration>
    				<executions>
    					<execution>
    						<id>start-jetty</id>
    						<phase>pre-integration-test</phase>
    						<goals>
    							<goal>run</goal>
    						</goals>
    						<configuration>
    							<scanIntervalSeconds>0</scanIntervalSeconds>
    							<daemon>true</daemon>
    						</configuration>
    					</execution>
    					<execution>
    						<id>stop-jetty</id>
    						<phase>post-integration-test</phase>
    						<goals>
    							<goal>stop</goal>
    						</goals>
    					</execution>
    				</executions>
    			</plugin>
    			<plugin>
    				<groupId>org.jvnet.jaxb2.maven2</groupId>
    				<artifactId>maven-jaxb2-plugin</artifactId>
    				<version>0.7.1</version>
    				<executions>
    					<execution>
    						<goals>
    							<goal>generate</goal>
    						</goals>
    					</execution>
    				</executions>
    				<configuration>
    					<schemaDirectory>${basedir}/src/main/resources</schemaDirectory>
    				</configuration>
    			</plugin>
    		</plugins>
    	</build>
    </project>
    
  4. Put the User.xsd into the /src/main/resources folder and generate the Java source code from the XML Schema with Maven.
  5. Add the generated sources in folder /target/generated-sources/xjc/ to the classpath.
  6. Add the web.xml to the /src/main/webapp/WEB-INF/.
  7. <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xmlns="http://java.sun.com/xml/ns/javaee" xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    	xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
    	id="WebApp_ID" version="2.5">
    	<display-name>rest-blog-sample</display-name>
    	<servlet>
    		<servlet-name>rest</servlet-name>
    		<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    		<load-on-startup>1</load-on-startup>
    	</servlet>
    
    	<servlet-mapping>
    		<servlet-name>rest</servlet-name>
    		<url-pattern>/*</url-pattern>
    	</servlet-mapping>
    
    </web-app>
    
  8. In the web.xml the servlet-name is set to rest, ie. by CoC a config file rest-servlet.xml is included.
  9. <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
    	xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xmlns:context="http://www.springframework.org/schema/context"
    	xmlns:oxm="http://www.springframework.org/schema/oxm"
    	xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
    		http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd
    		http://www.springframework.org/schema/oxm http://www.springframework.org/schema/oxm/spring-oxm-3.0.xsd">
    
    	<oxm:jaxb2-marshaller id="marshaller" contextPath="org.example.user"/>
    	
    	<context:annotation-config/>
    	<context:component-scan base-package="org.example.rest"/>
    	
    	<bean class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter">
    		<property name="messageConverters">
    			<list>
    				<bean class="org.springframework.http.converter.xml.MarshallingHttpMessageConverter">
    					<constructor-arg ref="marshaller"/>
    				</bean>
    			</list>
    		</property>
    	</bean>
    	
    	<bean class="org.springframework.web.servlet.view.BeanNameViewResolver"/>
    	
    	<bean name="user" class="org.springframework.web.servlet.view.xml.MarshallingView">
    		<constructor-arg ref="marshaller"/>
    	</bean>
    	
    </beans>
    
  10. The application context file rest-servlet.xml is self-explanatory, a marshaller is created and registered in the BeanNameViewResolver as well as the AnnotationMethodHandlerAdapter. The corresponding Controller has to declare the to-be-unmarshalled object User with the @RequestBody annotation to use the AnnotationMethodHandlerAdapter:
    package org.example.rest.controller;
    
    import static org.springframework.web.bind.annotation.RequestMethod.GET;
    import static org.springframework.web.bind.annotation.RequestMethod.POST;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    import org.example.user.ObjectFactory;
    import org.example.user.User;
    import org.springframework.http.HttpStatus;
    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    import org.springframework.web.bind.annotation.ResponseStatus;
    import org.springframework.web.servlet.ModelAndView;
    
    @Controller
    public class UserController {
    	
    	@RequestMapping(value="/post/", method=POST)
    	@ResponseStatus(HttpStatus.CREATED)
    	public ModelAndView post(@RequestBody User userRequest, HttpServletRequest request, HttpServletResponse response) {
    		User userResponse = new ObjectFactory().createUser();
    		response.addHeader("Location", determineLocationUri(request, "/user/" + userRequest.getId()));
    		return new ModelAndView("user").addObject(userResponse);
    	}
    
    	@RequestMapping(value="/user/{id}", method=GET)
    	@ResponseStatus(HttpStatus.CREATED)
    	public ModelAndView get(@PathVariable String id, HttpServletRequest request, HttpServletResponse response) {
    		User userResponse = new ObjectFactory().createUser();
    		userResponse.setId(Long.valueOf(id));
    		return new ModelAndView("user").addObject(userResponse);
    	}
    	
    	String determineLocationUri(HttpServletRequest request,	String newPathInfo) {
    		StringBuffer url = request.getRequestURL();
    		url.replace(url.indexOf(request.getPathInfo()), url.length(), newPathInfo);
    		return url.toString();
    	}	
    
    }
    
  11. Depending on the project settings: “Web Project Settings”, the context root is set.

Migrating to a Google App Engine project

  1. Run the Maven goal mvn war:war in the project folder.
  2. From the generated war in folder target, copy the jars from the /WEB-INF/lib folder inside the jar.
  3. Copy the web.xml and rest-servlet.xml to the GAE project.
  4. Note:Use complex type as embedded XML Element, otherwise JAXBElement is used as marshalling type, which includes some string coupling where it does not have to be.
  5. Note:I wanted to use XMLBeans for this, however, I came across this “stream closed” error, which rendered XMLBeans useless for me.

Starting

You can start the application by either (1) running mvn jetty:run for the simple project, run out of eclipse (2) with “Run as…/Run on Server” or start the GAE project (3) with “Run as…/Web Application”. Depending on what way you choose, the URL changes (1: http://localhost:8080/post, 2: http://localhost:8080/…/post, 3: http://localhost:8888/post).

Testing

You can test the application in two ways:

  1. Using soapUI. It has REST support now, Using the sample project, you have to setup the project as follows:
  2. Using the RestTemplate. The pom.xml for the client project looks like this:
  3. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    	xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
    	<modelVersion>4.0.0</modelVersion>
    	<groupId>org.examples</groupId>
    	<artifactId>resttemplate</artifactId>
    	<packaging>jar</packaging>
    	<version>1.0-SNAPSHOT</version>
    	<name>Spring 3 RestTemplate Sample</name>
    	<build>
    		<plugins>
    			<plugin>
    				<artifactId>maven-compiler-plugin</artifactId>
    				<configuration>
    					<source>1.5</source>
    					<target>1.5</target>
    				</configuration>
    			</plugin>
    		</plugins>
    	</build>
    	<dependencies>
    		<dependency>
    			<groupId>org.springframework.ws</groupId>
    			<artifactId>spring-xml</artifactId>
    			<version>2.0.0-M1</version>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-web</artifactId>
    			<version>3.0.1.RELEASE</version>
    		</dependency>
    		<dependency>
    			<groupId>org.springframework</groupId>
    			<artifactId>spring-oxm</artifactId>
    			<version>3.0.1.RELEASE</version>
    		</dependency>
    	</dependencies>
    </project>
    
    package org.example.rest.resttemplate;
    
    import java.io.BufferedInputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.StringReader;
    import java.net.URI;
    import java.util.ArrayList;
    import java.util.List;
    
    import javax.xml.transform.Source;
    import javax.xml.transform.stream.StreamSource;
    
    import org.example.user.User;
    import org.springframework.http.converter.HttpMessageConverter;
    import org.springframework.http.converter.xml.MarshallingHttpMessageConverter;
    import org.springframework.http.converter.xml.SourceHttpMessageConverter;
    import org.springframework.oxm.XmlMappingException;
    import org.springframework.oxm.jaxb.Jaxb2Marshaller;
    import org.springframework.web.client.RestTemplate;
    
    public class UserClient {
    
        public static void main(String[] args) throws XmlMappingException, IOException {
        	for (int i=1; i<1000; i++) {
       			postForLocation(String.valueOf(i));
        	}
        }
    
    	private static void postForLocation(String id) throws IOException {
    		RestTemplate restTemplate = new RestTemplate();
        	List<HttpMessageConverter<?>> list = new ArrayList<HttpMessageConverter<?>>();
        	list.add(new SourceHttpMessageConverter<Source>());
        	restTemplate.setMessageConverters(list);
        	String content = readFileAsString("src/main/resources/user-instance.xml").replaceAll("ID", id);
        	URI resultUrl = 
        		restTemplate.postForLocation("http://localhost:8888/post/", 
        			new StreamSource(new StringReader(content)));
        	getObject(resultUrl);
    	}
    	
    	private static void getObject(URI resultUrl) {
    		RestTemplate restTemplate = new RestTemplate();
        	List<HttpMessageConverter<?>> list = new ArrayList<HttpMessageConverter<?>>();
        	Jaxb2Marshaller jaxb2 = new Jaxb2Marshaller();
        	jaxb2.setContextPath("org.example.user");
        	list.add(new MarshallingHttpMessageConverter(jaxb2));
        	restTemplate.setMessageConverters(list);
    		User user = restTemplate.getForObject(resultUrl, User.class);
    		System.out.println(user.getId());
    	}
    
    	private static String readFileAsString(String filePath) throws java.io.IOException{
    	    byte[] buffer = new byte[(int) new File(filePath).length()];
    	    BufferedInputStream f = new BufferedInputStream(new FileInputStream(filePath));
    	    f.read(buffer);
    	    return new String(buffer);
    	}	
    
    }
    

Posted in Google App Engine, Spring | Tagged: , | 6 Comments »

Spring Quicktip: Selecting beans during runtime

Posted by ice09 on February 13, 2010

Sometimes you want to select an actual bean implementation during runtime depending on the availability of other beans.

Example

You have a bean accessor which accesses some external component and is used in several beans all over your application in several application contexts.
Now lets assume you want to change the actual implementation of the accessor bean depending on the availability of some other bean extended-accessor, which might or might not be present during runtime. This selection should be transparent to the existing components, ie. if the bean extended-accessor is not available in the application context, the fallback is using accessor instead.

Implementation

There might be different solutions to this problem, to me the best one is using the magical BeanFactoryPostProcessor.

import org.springframework.beans.BeansException;
import org.springframework.beans.MutablePropertyValues;
import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;

public class AccessorBeanFactoryPostProcessor implements BeanFactoryPostProcessor {
	
	protected String extendedTemplate;
	protected String accessorTemplate;
	protected String accessorTemplateFactory;
	
	public void setExtendedTemplate(String extendedTemplate) {
		this.extendedTemplate = extendedTemplate;
	}

	public void setAccessorTemplate(String accessorTemplate) {
		this.accessorTemplate = accessorTemplate;
	}

	public void setAccessorTemplateFactory(String accessorTemplateFactory) {
		this.accessorTemplateFactory = accessorTemplateFactory;
	}
	
	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		BeanDefinition beanDefinition = beanFactory.getBeanDefinition(accessorTemplateFactory);
		MutablePropertyValues pvalues = beanDefinition.getPropertyValues();
		Template accessorClass = (Template) (beanFactory.containsBean(extendedTemplate) ? beanFactory.getBean(extendedTemplate) : (Template) beanFactory.getBean(accessorTemplate));   

                //Set the property (the selected bean implementation) now during runtime
		pvalues.addPropertyValue("accessorTemplate", accessorClass);
	}

Now, this bean must be instantiated – it will be passed the bean definition of all beans in the application context. It retrieves the factory bean first, which itself will be dynamically instantiated with the extendedTemplate if it exists and accessorTemplate otherwise.
Together with a factory bean, this looks like this:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	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.5.xsd">

        <!-- Here the external definition can be imported (if it is, and the 
               bean extendedTemplate is defined, it will be passed to the factory bean) -->
	<import resource="classpath*:/extension.xml"/>
	
        <!-- Here the actual BeanFactoryPostProcessor is defined, the properties are 
               the Strings which identify the several beans -->
	<bean class="AccessorBeanFactoryPostProcessor">
		<property name="accessorTemplateFactory" value="accessorTemplateFactory"/>
		<property name="extendedTemplate" value="extendedTemplate"/>
		<property name="accessorTemplate" value="accessorTemplate"/>
	</bean>

        <!-- This is the actual bean which is used the the dependent beans - it is created 	
               by the factory bean, which is itself configured by BeanFactoryPostProcessor -->
        <bean id="actualTemplate" factory-bean="accessorTemplateFactory" factory-method="createAccessorTemplate" />

        <!-- This is the actual factory, which itself gets configured by the BeanFactoryPostProcessor -->
	<bean id="accessorTemplateFactory" class="AccessorTemplateFactory">
		<!-- will be modified during runtime -->
	</bean>

	<!-- This is the original accessor template bean, which is selected 
               if the extended bean is not present -->
        <bean id="accessorTemplate" class="AccessorTemplate" />

</beans>

Of course, the beans must implement the same interface. The included extension.xml is quite simple:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
	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.5.xsd">

	<bean id="extendedTemplate" class="ExtendedTemplate" />

</beans>

Finally, the factory bean is simple as well, it is used to have a single point of creation – the property accessorBean is set by the BeanFactoryPostProcessor during runtime:

public class AccessorTemplateFactory {

	public Template bean;

	public void setAccessorTemplate(Template arg0) {
		this.bean = arg0;
	}

	public Template createAccessorTemplate() {
		return bean;
	}

}

Posted in BeanFactoryPostProcessor, Spring | 2 Comments »