Spring FactoryBean-managed wiring

For my first post I’ll cover a client’s request on one of my projects, and how I was able to implement it.
First I’ll explain some background information as to how I got to the subject, and I’ll then explain the technicalities with some example code.

Background information

I had to migrate a project’s security mechanism from a LDAP directory to a user DAO.
Application security was configured with Acegi Security for Spring. The project’s POM file specified Acegi Security 1.0.3 and the Spring Framework 1.2.8.

The configuration of the user DAO security strategy was finished quite quickly after some research on the subject. But the client had an extra request.
For one departmental agency that used the application, the new DAO security strategy has to be used. For another departmental agency, a separately configured LDAP directory still had to be used.
Other configuration logic in the application was already based on a property key ‘location‘ which specified the departmental agency that the application had to be configured for. The client wanted to use the same property key to determine the security configuration.

I had to use the existing Acegi Security ProviderManager configuration that had a list of AuthenticationProviders and had to add the correct AuthenticationProvider for the specified departmental agency.

I wanted to find a solution for dynamically creating a Spring bean and wiring it inside the ProviderManager. Thanks to a colleague of mine, I got myself directed to the FactoryBean feature of Spring.

Hello FactoryBean!

Of course I won’t handle the exact configuration and details I used on the project itself, but i’ll explain the details using a HelloWorld-style example.

What is FactoryBean and what is it meant for? As quoted from the Spring Framework 3.0 reference:

You implement the org.springframework.beans.factory.FactoryBean interface for objects that are themselves factories.

The FactoryBean interface is a point of pluggability into the Spring IoC container’s instantiation logic. If you have complex initialization code that is better expressed in Java as opposed to a (potentially) verbose amount of XML, you can create your own FactoryBean, write the complex initialization inside that class, and then plug your custom FactoryBean into the container.

Imagine:
You have a HelloWorld application for two companies.
The two companies run exactly the same application, both on separate servers. You only want to maintain the application once, and want to be able to change the configuration at deployment time using a properties file. You can use Maven profiles to select the properties file for company A, or the properties file for company B. You would be able to use Maven profiles for all other Spring configuration files too, but when the configuration changes for all companies you’ll have to modify all the XML files instead of just one.
You want to use only one Spring ApplicationContext XML file for the entire Spring Container configuration. You don’t want to have to maintain separate files for every different company.

This is where the FactoryBean comes in.
You can let the FactoryBean create or instantiate a specific implementation of a Bean and return it to inject it into another Bean that will use it as a dependency.

Implementation

I have two services, HelloWorldServiceCompanyAImpl and HelloWorldServiceCompanyBImpl.

public class HelloWorldServiceCompanyAImpl implements HelloWorldService {
    @Override
    public String getHelloWorld() {
        return "Hello Company A!";
    }
}
public class HelloWorldServiceCompanyBImpl implements HelloWorldService {
    @Override
    public String getHelloWorld() {
        return "Hello Company B!";
    }
}

I have a properties file containing the property on which is decided what service implementation will be wired when running the application.

# ''company_a'' for the HelloWorldServiceCompanyAImpl
# ''company_b'' for the HelloWorldServiceCompanyBImpl
service_type=company_a

At the moment I use XML for configuring the Spring container.
In the FactoryBean, I want to be able to get the two pre-configured Spring beans from the Spring ApplicationContext. Normally you could instantiate the Java beans yourself by just using the new operator, but in my client’s case I had to use a pre-configured LdapAuthenticationProvider or DaoAuthenticationProvider. Constructing and configuring them in code wasn’t the way I wanted to go.

I implemented a FactoryBean that is also ApplicationContextAware. This way, the ApplicationContext will be available in the BeanFactory’s implementation, provided you generated the necessary getter and setter for the ApplicationContext instance. From this ApplicationContext instance, I retrieve the service beans in the FactoryBean getObject() method.
Both service beans are plain simple Spring beans with no complex XML configuration or anything special.

This is the relevant code for instantiating or getting the correct service bean in the FactoryBean:

public class HelloWorldServiceFactoryBean implements FactoryBean<HelloWorldService>, ApplicationContextAware {
    private static final Logger LOGGER = LoggerFactory.getLogger(HelloWorldServiceFactoryBean.class);
    private ApplicationContext applicationContext;
    private String serviceType;
    ...
    @Override
    public HelloWorldService getObject() throws Exception {
        HelloWorldService helloWorldService = null;

        if (ServiceConstants.COMPANY_A.equalsIgnoreCase(serviceType)) {
            LOGGER.debug("Getting bean ''{}''", ServiceConstants.COMPANY_A_SERVICE);
            helloWorldService = (HelloWorldService) applicationContext.getBean(ServiceConstants.COMPANY_A_SERVICE);
        } else if (ServiceConstants.COMPANY_B.equalsIgnoreCase(serviceType)) {
            LOGGER.debug("Getting bean ''{}''", ServiceConstants.COMPANY_B_SERVICE);
            helloWorldService = (HelloWorldService) applicationContext.getBean(ServiceConstants.COMPANY_B_SERVICE);
        }

        return helloWorldService;
    }
    ....
}

The FactoryBean is configured like this:

    ...
   <bean class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
        <property name="location" value="classpath:application-config.properties" />
    </bean>

    <bean id="helloWorldServiceFactory"
        class="be.stesch.demo.factorybean.HelloWorldServiceFactoryBean">
        <property name="serviceType" value="${service_type}" />
    </bean>
    ...

The HelloWorldService bean is configured in the ApplicationContext XML without specifying the class attribute. I don’t want Spring itself to instantiate the bean, but I want to use a FactoryBean to do so. This requires some special configuration.
When you just enter the FactoryBean’s bean id for the factory-bean attribute, the factory-method will not be found. This is because the factory-method will be searched in the bean that is created by the FactoryBean – in this case, the HelloWorldService. To refer to the FactoryBean itself, the bean id must be prefixed by an &, or more specifically an &amp; in the XML file itself.

When you need to ask a container for an actual FactoryBean instance itself, not the bean it produces, you preface the bean id with the ampersand symbol & (without quotes) when calling the getBean() method of the ApplicationContext. So for a given FactoryBean with an id of myBean, invoking getBean("myBean") on the container returns the product of the FactoryBean, and invoking getBean("&myBean") returns the FactoryBean instance itself.

This leads us to the following HelloWorldService bean configuration:

    ...
    <bean id="helloWorldService" factory-bean="&amp;helloWorldServiceFactory"
        factory-method="getObject" />
    ...

In my Main class, I get the HelloWorldService instance from the ApplicationContext and print out it’s message.
Depending on the ‘service_type‘ property in the application-config properties file, it will print out ‘Hello Company A!’ or ‘Hello Company B!’.

public class FactoryBeanDemo {
    ...
    public static void main(String[] args) {
        ApplicationContext applicationContext = new ClassPathXmlApplicationContext("application-context.xml");

        HelloWorldService helloWorldService = (HelloWorldService) applicationContext.getBean("helloWorldService");
        LOGGER.debug("Printing {}", helloWorldService.getHelloWorld());
        System.out.println(helloWorldService.getHelloWorld());
    }
    ...
}

You can find the complete source code, including the Maven POM file in my DropBox public folder, on the following link: http://dl.dropbox.com/u/33979885/Blog%20Source%20Code/factorybean-xml-demo.zip


In the next part, I hope to be able to get rid of all the XML configuration, and configure the Spring container using annotations.
I tried to do so already, but I got an Exception when running the application. I also got it using XML for configuration, but there it got resolved by prefixing the FactoryBean with &amp;.
As far as I can tell from the @Bean javadoc, the same thing should happen automatically when annotating the getObject() method.

The @Bean annotation may be used on any methods in an @Component class, in which case they will get processed in a configuration class ‘lite’ mode where they will simply be called as plain factory methods from the container (similar to factory-method declarations in XML). The containing component classes remain unmodified in this case, and there are no unusual constraints for factory methods.

I get the following Exception:

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name ''createHelloWorldService'' defined in class path resource [be/stesch/demo/factorybean/HelloWorldServiceFactoryBean.class]: No matching factory method found: factory bean ''helloWorldServiceFactoryBean''; factory method ''createHelloWorldService()''. Check that a method with the specified name exists and that it is non-static.
	at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:528)
	at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:983)

If anyone would know a solution to this, please be so kind to post your comments to this topic.
I’ll try to describe it further in the next part.

Share
Posted in Java | Tagged , , | Leave a comment

Leave a Reply

Your email address will not be published. Required fields are marked *

*


*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>