- Dependency injection via XML: though this makes configured classes independent of Spring API, the nature of XML makes it hard to navigate through the dependency relationship among classes when the application’s code grows and becomes more complex.
- Dependency injection using annotations: this makes the configured classes tightly coupled with Spring API - the @Component, @Service, @Autowired… annotations.
Therefore, Spring provides Java config approach for dependency injection, which can eliminate the drawbacks of XML and annotations: configured classes are not polluted by Spring annotations and they are easily navigable.Let’s see how dependency injection can be done with Java configuration in Spring.package net.codejava; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class AppConfig { @Bean("client1") public Client getClient1(Service service1) { return new ClientImpl(service1); } @Bean("service1") public Service getService1() { return new ServiceImpl1(); } }Here, the AppConfig class is marked with the@Configuration annotation indicates that this class will be processed by Spring IoC (Inversion of Control) container to generate bean instances based on the@Bean methods. You can see:
@Bean("client1") public Client getClient1(Service service1) { return new ClientImpl(service1); }This getClient1() method is marked with the @Bean annotation - tells Spring that it returns a bean to be managed by the application context. This bean is an instance of the Client interface and has name client1. The Client interface is defined as follows:
package net.codejava; public interface Client { void doSomething(); }And the ClientImpl class:
package net.codejava; public class ClientImpl implements Client { private Service service; public ClientImpl(Service b) { this.service = b; } @Override public void doSomething() { String info = service.getInfo(); System.out.println(info); } }
@Bean("service1") public Service getService1() { return new ServiceImpl1(); }This method creates a bean instance of type Service with name service1 - the return type and the name match the parameter of the getClient1() method so Spring injects the bean service1 to the bean client1. And these two beans are managed in application context.The Service interface is defined as follows:
package net.codejava; public interface Service { String getInfo(); }And the ServiceImpl1 class:
package net.codejava; public class ServiceImpl1 implements Service { @Override public String getInfo() { return "Service 1's Info"; } }That’s basically how to configure dependency injection in Spring using Java config - and the technique above called inter-bean references. You see, the configured classes are really pure - they are not polluted by Spring annotations. And you can easily navigate the code of the configuration class AppConfig to understand the dependency among classes. More about bean name resolution:If you omit the name in the @Bean annotation, Spring will use the method name as bean name. This may helpful in reducing the boiler-plate code and use the defaults. For example:
@Bean public Client client2() { return new ClientImpl(service2()); } @Bean public Service service2() { return new ServiceImpl2(); }Here, the method client2() registers a bean named client2 and the method service2() registers a bean named service2.A bean can have multiple names like this:
@Bean({"service", "srv", "SRV"}) public Service service3() { return new ServiceImpl3(); }Here, the bean returned by this method has a primary name and two aliases. Of course you can get this bean by using any name listed.
<dependencies> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>5.0.7.RELEASE</version> </dependency> </dependencies>At least, you need to use this dependency to use dependency injection with Spring framework. Then create Java classes as describe above: Client, ClientImpl, Service, ServiceImpl1, ServiceImpl2 and ServiceImpl3 under the package net.codejava in src/main/java folder.And write the configuration class as follows:
package net.codejava; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class AppConfig { @Bean("client1") public Client getClient1(Service service1) { return new ClientImpl(service1); } @Bean("service1") public Service getService1() { return new ServiceImpl1(); } @Bean public Client client2() { return new ClientImpl(service2()); } @Bean public Service service2() { return new ServiceImpl2(); } @Bean({"service", "srv", "SRV"}) public Service service3() { return new ServiceImpl3(); } }That’s all for the dependency injection configuration.
ApplicationContext appContext = new AnnotationConfigApplicationContext(AppConfig.class);And then you can obtain a bean from the application context like this:
Client client1 = (Client) appContext.getBean("client1"); client1.doSomething();In case you have multiple configuration classes, you can specify them like this (using var-arg constructor):
ApplicationContext appContext = new AnnotationConfigApplicationContext(AppConfig1.class, AppConfig2.class, AppConfig3.class);You can also use the register() method, but then you have to refresh the application context. For example:
AnnotationConfigApplicationContext appContext = new AnnotationConfigApplicationContext(); appContext.register(AppConfig.class, AppConfig2.class, AppConfig3.class); appContext.refresh();Therefore, create class with main method named SpringDependencyInjectionExample as follows:
package net.codejava; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class SpringDependencyInjectionExample { public static void main(String[] args) { ApplicationContext appContext = new AnnotationConfigApplicationContext(AppConfig.class); Client client1 = (Client) appContext.getBean("client1"); client1.doSomething(); Client client2 = (Client) appContext.getBean("client2"); client2.doSomething(); Service service = (Service) appContext.getBean("srv"); System.out.println(service.getInfo()); } }Run this program and you can see the following output:
Service 1's Info Service 2's Info Service 3's InfoThat’s how to configure dependency injection with Java config in Spring. We hope you have found this tutorial helpful to get started with Spring framework.