Spring FactoryBean-managed wiring: Part 2

I finally succeeded in getting rid of the XML configuration for the Spring container, and managed to wire everything using Spring annotations.
This post will describe what changes I had to make and what issues I experienced when converting the configuration to annotations.

XML vs. Annotations

Different opinions exist about XML configuration or annotations. I won’t go too far in those.
I like annotations because it makes your configuration readable directly in your source code, and you don’t have to go find some configured bean in one of the huge XML files.
On the other hand, configuring a Spring container using XML is much more verbose, but everything can be found in one file – or at least multiple files in one location – and there’s no magic involved. When using annotations, it might seem like dark magic when a bean can’t be injected, just because you forgot to give it a proper name or use the proper qualifier. Also, using separate versions of configuration files and Maven profiles is also only possible using XML.
So, just to tag along with the crowd and new way of thinking, I chose to get rid of the XML completely for this small HelloWorld project and only use Spring annotations to configure the application.
Of course, it depends on the circumstances whether or not you want to use XML or annotations, or even both.

Bye Bye XML

The first thing I did was delete the application-config.xml file. Of course I then had to change the main application class to instantiate the ApplicationContext using the AnnotationConfigApplicationContext.

The new main class now looks like this:

public class FactoryBeanDemo {
    ...
    public static void main(String[] args) {
        ApplicationContext applicationContext = new AnnotationConfigApplicationContext("be.stesch");

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

Of course, running this application will spawn an Exception immediatly, because when scanning the base-package no Spring configured beans will be found any longer.
You will get the following Exception:

Exception in thread "main" org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named ''helloWorldService'' is defined
	at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBeanDefinition(DefaultListableBeanFactory.java:527)
        ...

So I had to take some more steps to complete the configuration.

Basic Spring annotations

First, I have to declare the service beans that I’m going to use in the FactoryBean to be Spring beans.
To make a Java bean a Spring-managed service bean, you use the @Component or @Service annotation.
You also have several other annotations – called stereotypes – to do the same thing, but they have a slightly different meaning and will be used in different contexts. See the link Classpath scanning and managed components for more information.

  • @Component: a generic stereotype for any Spring-managed component.
  • @Repository: used in the persistence layer to declare a Spring-managed DAO component.
  • @Service: used in the service layer to declare a Spring-managed business service facade.
  • @Controller: used in the presentation layer to declare a Spring-managed controller, for example a web controller.

Then I configure the FactoryBean itself to be able to let it create and instantiate the correct HelloWorldService object.
You have to declare the HelloWorldServiceFactory bean to be a @Component.
Trying basically to convert the XML configuration straight to annotations, I annotated the getObject() method with the @Bean annotation.
What these annotations should do is say: I have a factory-bean HelloWorldServiceFactoryBean which I want to be a Spring-managed bean. It’s purpose it to create another Spring-managed bean of type HelloWorldService by using the factory-method getObject().
You have to pass the name attribute to the @Bean annotation to make sure the returned service instance will be called ‘helloWorldService‘.

@Component
public class HelloWorldServiceFactoryBean implements FactoryBean<HelloWorldService>, ApplicationContextAware {
    ...
    @Override
    @Bean(name = "helloWorldService")
    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;
    }
    ...
}

You might think you’re finished now, but running the main application will present you the next Exception.

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name ''helloWorldService'' defined in class path resource [be/stesch/demo/factorybean/HelloWorldServiceFactoryBean.class]: factory-bean ''helloWorldServiceFactoryBean'' returned null
	at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:355)
        ...

It seems a little bit weird that no class of your own application is mentioned in the stacktrace, apart from the main application class. Time to debug the application.
When you put a breakpoint in the getObject() method of the HelloWorldServiceFactoryBean you’ll notice that the serviceType field, that used to come from a properties file, is null. Of course – we forgot to declare a Spring bean for the PropertyPlaceholderConfigurer.

The idea is to get rid of XML completely, and only use annotations. This way, we cannot use XML to declare a PropertyPlaceholderConfigurer spring-bean, the way I did it in part 1. The property value somehow has to get injected in the FactoryBean to be able to use it in the getObject() method.

What I did was creating an ApplicationConfig class that is responsible for creating Spring beans like the PropertyPlaceHolderConfigurer. I got my inspiration from a DZone Javalobby article: Spring and Hibernate application with zero XML.
The ApplicationConfig class has to be annotated with @Component, and the method creating the PropertyPlaceHolderConfigurer gets the @Bean annotation indicating that it will create a Spring-managed bean. Previously the @Bean and also @Configuration annotations were part of the JavaConfig project. As of version 3.0, JavaConfig got migrated into the core Spring framework.

This is the ApplicationConfig class and the getPropertyPlaceholderConfigurer() method:

@Component
public class ApplicationConfig {
    @Bean
    public PropertyPlaceholderConfigurer getPropertyPlaceholderConfigurer() {
        PropertyPlaceholderConfigurer propertyPlaceholderConfigurer = new PropertyPlaceholderConfigurer();
        propertyPlaceholderConfigurer.setLocation(new ClassPathResource("application-config.properties"));

        return propertyPlaceholderConfigurer;
    }
}

Then I inject the ‘service_type‘ property value into the HelloWorldServiceFactoryBean‘s serviceType field using the @Value annotation.
I also found that way of injecting properties in the Spring and Hibernate DZone article mentioned above.

@Component
public class HelloWorldServiceFactoryBean implements FactoryBean<HelloWorldService>, ApplicationContextAware {
    ...
    @Value("${service_type}")
    private String serviceType;
    ...
}

All Spring beans previously configured in XML are now configured by annotations.
Everything is annotated the way I configured it in XML and the way I thought the configuration should work.

And then the truth came out!

More code change necessary!

I got a very weird Exception that I did not expect to see:

Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name ''helloWorldService'' defined in class path resource [be/stesch/demo/factorybean/HelloWorldServiceFactoryBean.class]: No matching factory method found: factory bean ''helloWorldServiceFactoryBean''; factory method ''getObject()''. 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)
    ...

The factory-method getObject() is not found in the declared FactoryBean. What the …?
It really is there, it exists.
The method returns a HelloWorldService instance, it exists in the HelloWorldServiceFactoryBean which is annotated with the @Component annotation and is thus Spring-managed, and the method itself is annotated with the @Bean annotation and thus should create the ‘helloWorldService‘ Spring-managed bean.
I was completely baffeled, and it took me a while to scourge around the Internet searching for people with the same problem and some solutions.

I did not find the exact same problem, but I came up with a solution using some examples I’ve found on the Internet.
I used the same ApplicationConfig class where I created the PropertyPlaceholderConfigurer to also create the HelloWorldService.
To do this, I’ll have to access the HelloWorldServiceBeanFactory‘s getObject() method and return the created instance as a Spring-managed bean. To be able to get the FactoryBean, I’ll need access to the ApplicationContext. I made the ApplicationConfig class implement ApplicationContextAware so that the ApplicationContext would be injected into the ApplicationConfig class.

This is the source code:

@Component
public class ApplicationConfig implements ApplicationContextAware {
    private ApplicationContext applicationContext;

    public ApplicationContext getApplicationContext() {
        return applicationContext;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
    }

    ...

    @Bean(name = "helloWorldService")
    public HelloWorldService getHelloWorldService() throws Exception {
        HelloWorldService helloWorldService = applicationContext.getBean(HelloWorldServiceFactoryBean.class)
                .getObject();
        return helloWorldService;
    }
}

Also, do not forget to remove the @Bean annotation from the getObject() factory-method in the FactoryBean. Otherwise the same Exception will keep occuring and the application won’t work.

When you run the application now, you’ll see that the correct HelloWorldService instance is created – depending on the ‘service_type‘ property value, and that the application works correctly.

Why annotating the same getObject() factory-method with the @Bean annotation as it was configured in XML doesn’t work, I do not know.
In XML you have to prefix the factory-bean attribute value with an &amp; but I did not find any possibity to do so with annotations.
Why doing the same thing with more or less the same code from another class does work, I do not know either.
But perhaps, someone else might find the solution to a problem like this thanks to my blog post.

If anyone knows and cares to explain the why’s and how’s about this issue, or has other opinions about the solution, please be so kind to post your comments underneath this blog post.


Conclusion

As a conclusion, I can say that it is possible to get rid of the XML entirely. The whole Spring container can be configured by annotating your classes and annotating methods that create beans.
What I had hoped was that I could just use the source code of my previous post about the XML configuration and annotate the existing code.
But that wasn’t completely the case. I had to write some extra code to create a PropertyPlaceholderConfigurer to be able to inject property values in other Spring-managed beans. I also had to move the factory-method – or at least the annotation that I thought would mark getObject() as a factory-method – to another class so there too extra code had to be written.
Of course the changes were minimal, but still they had to be done.

All in all, I learned something about Spring configuration with annotations and I’m eager to read your comments and gain some more knowledge about the subject.

Again, you can find the complete source code and Maven POM file in my Public DropBox folder: http://dl.dropbox.com/u/33979885/Blog%20Source%20Code/factorybean-annotation-demo.zip

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>