Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

Spring Java Configuration Advantages

DZone's Guide to

Spring Java Configuration Advantages

Free Resource

Modernize your application architectures with microservices and APIs with best practices from this free virtual summit series. Brought to you in partnership with CA Technologies.

The debate on Spring XML config vs Java config is very common, perhaps beaten only by the “Git vs Mercurial” one or “Eclipse vs Intellij”. For this reason I wanted to write this article to explain when and how you should use Spring Java Configuration.

Spring Java configuration briefly

A the beginning, Spring offered only the XML based configuration. This may seems fantastic cause you have separation between the business logic (java spring beans) and the configuration of these objects, expressed in XML. You can eventually modify the behavior of the whole application without recompile it.

The Spring XML syntax is quite easy, so why the Spring Java config was introduced?

01. //A simple Spring Java Configuration class
02. @Configuration
03. public class DAOConfiugration {
04.  
05.    @Bean
06.    public ReservationDAO reservationDAO() {
07.        return new ReservationDAO();
08.    }
09.  
10.    @Bean
11.    public InvoiceDAO invoiceDAO() {
12.        return new InvoiceDAO();
13.    }
14. }
15.                                     
1. <!-- The correspondig XML configuration -->
2. <?xml version="1.0" encoding="UTF-8"?>
4.       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
6.    <bean id="InvoiceDAO" class="com.castronu.examples.dao.InvoiceDAO"></bean>
7.    <bean id="reservationDAO" class="com.castronu.examples.dao.ReservationDAO"></bean>
8. </beans>

Compile time check

The first advantage of the Java config is the compile time check.
Lets see this simple example:

1. <!-- dao-configuration.xml -->
2. <bean id="invoiceDAO" class="com.castronu.examples.dao.InvoiceDAO"></bean>
3. <bean id="reservationDAO" class="com.castronu.examples.dao.ReservationDAO"></bean>
1. <!-- service-configuration.xml -->
2. <import resource="dao-configuration.xml"></import>
3. <bean id="invoiceService" class="com.castronu.examples.service.InvoiceService">
4.   <constructor-arg ref="reservationDAO"/>
5. </bean>
And the class InvoiceService:
01. public class InvoiceService {
02.  
03.    private final InvoiceDAO invoiceDAO;
04.  
05.    public InvoiceService(InvoiceDAO invoiceDAO) {
06.        this.invoiceDAO = invoiceDAO;
07.    }
08.  
09.    public Invoice generateInvoice(Reservation reservation){
10.        Invoice invoice = new Invoice(reservation);
11.        //Generate invoice...
12.        invoiceDAO.save(invoice);
13.        return invoice;
14.    }
15. }

You can compile this code without problems. But at runtime you get an UnsatisfiedDependencyException (cause InvoiceService need an InvoiceDao and not a ReservationDao).

I know that modern IDE can make this kind of check on Spring XML files, but in a big Spring project with 30-50 Maven modules this is useless, because normally you always build your project exploiting maven aggregation system.

The corresponding Java configuration version doesn't have this problem:

01. @Configuration
02. @Import(DAOConfiugration.class)
03. public class ServiceConfiguration {
04.  
05.    @Autowired
06.    private DAOConfiugration daoConfiugration;
07.  
08.    @Bean
09.    public InvoiceService invoiceService() {
10.        InvoiceService invoiceService = new InvoiceService(
11.           daoConfiugration.reservationDAO() //Compilation error at this line
12.        );
13.        return invoiceService;
14.    }
15. }

You cannot compile it.

Refactoring

Refactoring is the key of modern software development. Business, frameworks and languages change rapidly and your code need to be ready to be changed. Without refactoring your project can turn in a Rat King.

Refactoring a big Spring project (30-50 maven modules) can be frustrating due to the lack of compile time check. If you want to speed up the refactoring process you have to follow two simple rules using the Java Configuration:

  • Autowire only @Configuration classes (Navigable configuration pattern)

  • In every Spring Bean define others beans final and Inject by constructor

If you don’t follow these rules using the Java Configuration over the XML could be self-defeating.

The navigation configuration pattern is well described in this interesting article. It consist in import and autowire another @Configuration class and use it like a factory when creating beans. You will have the IDE support on class navigation or when you want to list all the method to find the needed bean:


Making all the fields of a Spring bean final and injecting by constructor make the compile time check of the configuration stronger and will avoid “no qualifying bean of type” errors at runtime.

Spring bean classes following this pattern can be made easily immutable with all advantages of the case. They will be easy to test (you will not need Mockito.injectMock anymore) and easy to maintain.

Tests flexibility

Combining Spring Java configuration, JUnit and Mockito you can write flexible integration tests mocking only some Spring Beans. This is very useful in a complex system with many external component (LDAP, Solr, external web service).

When all Spring Beans of the system are well unit-tested, you would like to write an integration test.
If you have external component that you want to mock (e.g. LDAP server), you can override the production bean with a mock directly in the test class. In a big project with 20-30 spring context with import depth index bigger the 3 o 4, doing the same with XML configuration could be very complex and can lead to an explosion of stub classes and stub contexts in the test scope.
The dummy example below illustrates this technique:

01. @RunWith(SpringJUnit4ClassRunner.class)
02. @ContextConfiguration(classes = AppIntegrationTestConfiguration.class)
03. public class AppIntegrationTest {
04.  
05.    @Autowired
06.    private BookingService bookingService;
07.  
08.    @Test
09.    public void bookTicketTest() {
10.  
11.        bookingService.bookTicket(new User("Jhon"), new Event("Festival"));
12.  
13.    }
14.  
15. }
16.  
17. @Configuration
18. @Import({ServiceConfiguration.class, DAOConfiugration.class})
19. class AppIntegrationTestConfiguration {
20.  
21.    //This mock will override the real bean defined in <span style="font-family: Menlo, Monaco, monospace, sans-serif; font-size: 12.1499996185303px; line-height: 1.45em;">DAOConfiugration</span>
22.    @Bean
23.    public ReservationDAO reservationDAO() {
24.        ReservationDAO mock = Mockito.mock(ReservationDAO.class);
25.        when(mock.save(any(Reservation.class))).thenReturn(99);
26.        return mock;
27.    }
28.  
29. }

Remember that you can always mix Spring XML and Java configuration, but to exploit this technique the bean to override should be defined in a @Configuration class.

Conclusion

If you work on a big Spring project (more than 10 maven modules), the Java Configuration is definitely better than XML configuration, but you need to respect the navigable configuration pattern and the constructor injection.
If the project is small enough to keep low the number of Spring context and import depth, then you can keep using the XML configuration.

All the code examples are available on GitHub: https://github.com/castronu/spring-java-config-examples

The Integration Zone is proudly sponsored by CA Technologies. Learn from expert microservices and API presentations at the Modernizing Application Architectures Virtual Summit Series.

Topics:

Published at DZone with permission of Diego Castronuovo. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}