120

Following the question Understanding Spring @Autowired usage I wanted to create a complete knowledge base for the other option of spring wiring, the @Configuration class.

Let's assume I have a spring XML file that 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-3.0.xsd">

  <import resource="another-application-context.xml"/>

  <bean id="someBean" class="stack.overflow.spring.configuration.SomeClassImpl">
    <constructor-arg value="${some.interesting.property}" />
  </bean>

  <bean id="anotherBean" class="stack.overflow.spring.configuration.AnotherClassImpl">
    <constructor-arg ref="someBean"/>
    <constructor-arg ref="beanFromSomewhereElse"/>
  </bean>
</beans>

How can I use @Configuration instead? Does it have any affect on the code itself?

Avi
  • 21,182
  • 26
  • 82
  • 121

1 Answers1

171

Migrating XML to @Configuration

It is possible to migrate the xml to a @Configuration in a few steps:

  1. Create a @Configuration annotated class:

    @Configuration
    public class MyApplicationContext {
    
    }
    
  2. For each <bean> tag create a method annotated with @Bean:

    @Configuration
    public class MyApplicationContext {
    
      @Bean(name = "someBean")
      public SomeClass getSomeClass() {
        return new SomeClassImpl(someInterestingProperty); // We still need to inject someInterestingProperty
      }
    
      @Bean(name = "anotherBean")
      public AnotherClass getAnotherClass() {
        return new AnotherClassImpl(getSomeClass(), beanFromSomewhereElse); // We still need to inject beanFromSomewhereElse
      }
    }
    
  3. In order to import beanFromSomewhereElse we need to import it's definition. It can be defined in an XML and the we'll use @ImportResource:

    @ImportResource("another-application-context.xml")
    @Configuration
    public class MyApplicationContext {
      ...  
    }
    

    If the bean is defined in another @Configuration class we can use the @Import annotation:

    @Import(OtherConfiguration.class)
    @Configuration
    public class MyApplicationContext {
      ...
    }
    
  4. After we imported other XMLs or @Configuration classes, we can use the beans they declare in our context by declaring a private member to the @Configuration class as follows:

    @Autowired
    @Qualifier(value = "beanFromSomewhereElse")
    private final StrangeBean beanFromSomewhereElse;
    

    Or use it directly as parameter in the method which defines the bean that depends on this beanFromSomewhereElse using @Qualifier as follows:

    @Bean(name = "anotherBean")
    public AnotherClass getAnotherClass(@Qualifier (value = "beanFromSomewhereElse") final StrangeBean beanFromSomewhereElse) {
      return new AnotherClassImpl(getSomeClass(), beanFromSomewhereElse);
    }
    
  5. Importing properties is very similar to importing bean from another xml or @Configuration class. Instead of using @Qualifier we'll use @Value with properties as follows:

    @Autowired
    @Value("${some.interesting.property}")
    private final String someInterestingProperty;
    

    This can be used with SpEL expressions as well.

  6. In order to allow spring to treat such classes as beans containers we need to mark this in our main xml by putting this tag in the context:

    <context:annotation-config/>
    

    You can now import @Configuration classes exactly the same as you would create a simple bean:

    <bean class="some.package.MyApplicationContext"/>
    

    There are ways to avoid spring XMLs altogether but they are not in the scope of this answer. You can find out one of these options in my blog post on which I'm basing my answer.


The advantages and disadvantages of using this method

Basically I find this method of declaring beans much more comfortable than using XMLs due to a few advantages I see:

  1. Typos - @Configuration classes are compiled and typos just won't allow compilations
  2. Fail fast (compile time) - If you forget to inject a bean you'll fail on compile time and not on run-time as with XMLs
  3. Easier to navigate in IDE - between constructors of beans to understand the dependency tree.
  4. Possible to easily debug configuration startup

The disadvantages are not many as I see them but there are a few which I could think of:

  1. Abuse - Code is easier to abuse than XMLs
  2. With XMLs you can define dependencies based on classes that are not available during compile time but are provided during run-time. With @Configuration classes you must have the classes available at compile time. Usually that's not an issue, but there are cases it may be.

Bottom line: It is perfectly fine to combine XMLs, @Configuration and annotations in your application context. Spring doesn't care about the method a bean was declared with.

Avi
  • 21,182
  • 26
  • 82
  • 121
  • 3
    One possible downsides is the loss of configuration. Say you have a class that mocks some functionality in development, then you want to swap it out for another class in the UAT environment. Using XML, then its just a matter of changing the config and allowing the application to run/restart. With these new class configurations, the classes would have to be recompiled. – Jose Jul 17 '14 at 17:35
  • 6
    @JoseChavez - That's a great argument that I've already heard a couple of times. And I tried to do some statistic research in which I couldn't find any app or system that uses XMLs outside of its jars/wars. The practical meaning of it is that you need to either unzip the jar and change the XML (which I couldn't find anybody who does that) or rebuild your jars (which is what everyone I talked to said they've done so far). So, bottom line - as it may be a considerable argument, it's usually not important in real life. – Avi Jul 17 '14 at 18:04
  • 8
    That's what @Profile annotation, and the "${env.value}" syntax, are for. With @Profile("someName") you can tag an entire configuration for use only when the profile is active. In your application.properties (or .yml) file, then you may set spring.profiles.active=someName,default... To set it dynamically based on environment variables, use the ${SOME_ENV_VAR} syntax as the value for spring.active.profiles and set the environment variable. Spring now recommends using java config - http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#using-boot-configuration-classes – Jack Viers Dec 22 '14 at 18:54
  • 1
    What is alternative than defining each bean as method in config file? – Asif Mushtaq Sep 12 '18 at 15:34
  • 1
    @AsifMushtaq - You can use the autoscan feature and each class that has `@Component` `@Service` or other such annotations would be automatically made into a bean (but that wasn't the focus of this question) – Avi Sep 12 '18 at 17:41
  • is thera any way we can create bean in loop i mean i have one bean to intialize but by iterating loop and assigning values from list one by one to the object – Mahesh Nighut Jun 07 '21 at 08:54