1. Introduction
In this tutorial, we’ll discuss the Java based container configuration in Spring.
Two central artifacts in Spring’s Java configuration support are @Configuration
annotated classes and @Bean
annotated classes.
2. @Bean annotation
The @Bean
annotation signifies that a method creates, configures, and initializes a new object to be managed by the Spring IoC container. The @Bean
annotation plays the same role as the element.
@Bean
annotated methods are most often used with @Configuration
beans.
A class annotated with @Configuration
indicates that its main purpose is to serve as a source of bean definitions. @Configuration
classes let inter-bean dependencies be defined by calling other @Bean
methods in the same class.
Here is an example:
@Configuration public class AppConfig { @Bean public MyServiceImpl myService() { return new MyServiceImpl(); } }
In common scenarios, @Bean
methods are to be declared within @Configuration
classes. This ensures that the entire class is processed and cross-method references therefore get redirected to the container’s lifecycle management.
This ensures that the @Bean
method is not inadvertently called through a standard Java method call, helping to minimize subtle and difficult-to-detect bugs. When @Bean
methods are declared within classes that not annotated with @Configuration
, they are referred to as being processed in a ‘lite’ mode.
A direct Java call to such a method will bypass the container’s interception, behaving like a regular method call. This results in a new instance being created each time, instead of reusing an existing singleton (or scoped) instance of the bean. Such a @Bean
method therefore never needs to invoke other @Bean
methods.
2. @Configuration annotation
@Configuration
is used on a class and indicates that an object is a source of bean definitions. @Configuration
classes declare beans through @Bean
-annotated methods. You can also use calls to @Bean
methods within @Configuration
classes to establish dependencies between beans.
2.1 Injecting Inter-bean Dependencies
When one bean depends on another, you can easily express this dependency by having one @Bean
method call another, as demonstrated in the following example:
@Configuration public class AppConfig { @Bean public BeanOne beanOne() { return new BeanOne(beanTwo()); } @Bean public BeanTwo beanTwo() { return new BeanTwo(); } }
In the preceding example, beanOne
receives a reference to beanTwo
through constructor injection. This approach to defining inter-bean dependencies is effective only when the @Bean
method is declared within a @Configuration
class. Inter-bean dependencies cannot be specified using plain @Component
classes.
2.2 @Configuration and CGLIB
Consider the following example, which shows a @Bean
annotated method being called twice:
@Configuration public class AppConfig { @Bean public MyService myService1() { MyServiceImpl myService = new MyServiceImpl(); myService.setMyDao(myDao()); return myService; } @Bean public MyService myService2() { MyServiceImpl myService = new MyServiceImpl(); myService.setMyDao(myDao()); return myService; } @Bean public MyDao myDao() { return new MyDaoImpl(); } }
myDao()
has been called once in myService1()
and once in myService2()
. Since this method creates and returns a new instance of MyDaoImpl
, you would typically expect to have two separate instances (one for each service). This is going to be an issue as Spring instantiated beans have ‘singleton’ scope.
All @Configuration
classes are subclassed at startup-time with CGLIB. In the subclass, the child method checks the container first for any cached (scoped) beans before it calls the parent method and creates a new instance. The behavior could be different according to the scope of the bean. CGLIB dynamically adds features at startup-time this leads to few restrictions. In particular, @Configuration
classes can not be final. However, default constructors are allowed, including the use of @Autowired
.
To avoid the limitations imposed by CGLIB, you can declare your @Bean
methods in non-@Configuration
classes, such as plain @Component
classes. Alternatively, you can annotate your configuration class with @Configuration(proxyBeanMethods = false)
. In this setup, cross-method calls between @Bean
methods are not intercepted, so you must rely solely on constructor or method-level dependency injection.
2.3 @Import annotation
The @Import
annotation allows for loading @Bean
definitions from another configuration class.
@Configuration public class ConfigA { @Bean public A a() { return new A(); } } @Configuration @Import(ConfigA.class) public class ConfigB { @Bean public B b() { return new B(); } }
Now, instead of specifying both ConfigA.class
and ConfigB.class
when creating the context, you only need to explicitly provide ConfigB
, as illustrated in the following example:
public static void main(String[] args) { ApplicationContext ctx = new AnnotationConfigApplicationContext(ConfigB.class); // now both beans A and B will be available... A a = ctx.getBean(A.class); B b = ctx.getBean(B.class); }
This approach simplifies container instantiation by requiring you to handle only one class, rather than remembering and managing a potentially large number of @Configuration
classes during setup.
3. Conclusion
In conclusion, Java-based container configuration in Spring provides a robust and flexible approach to defining beans and their dependencies. By leveraging annotations such as @Configuration
and @Bean
, developers can create maintainable and easily readable configuration classes. This method also allows for fine-grained control over bean instantiation and lifecycle management. Understanding and utilizing these Java-based configuration techniques enable the creation of scalable, efficient, and well-organized Spring applications, ensuring that your codebase remains clean and manageable as it grows.