1. Overview
The Spring framework lets us configure the application context in different ways which include XML-based, Java-based and annotation-based approaches.
In this tutorial, we're going to investigate Spring's Java-based configuration.
2. @Bean Annotation
Let's start with creating beans.
For this purpose, we can use the @Bean annotation. More specifically, we should annotate instance returning methods with the @Bean annotation to create beans. When Spring finds a @Bean annotated method, it registers the returned instance as a bean.
Now let's look at the usage:
public class ClassService {
public List<Student> getStudents() {
List<Student> students = new ArrayList<>();
students.add(new Student("John"));
students.add(new Student("Sarah"));
return students;
}
}
Here, we have a plain Java class. Note that we aren't using any Spring annotation. In order to create a bean for this class we should create a method returning an instance of ClassService:
@Configuration
public class SchoolConfiguration {
@Bean
public ClassService classService() {
return new ClassService();
}
}
Here, we're declaring classService() method and annotating it with @Bean annotation. Note that this in contrast to the annotation-based configuration where we are annotating classes with the @Component annotation.
We're also using the @Configuration annotation which we'll investigate next.
3. @Configuration Annotation
Now let's look at where we should define our @Bean annotated methods.
Spring requires us to put @Bean annotated methods into @Configuration annotated classes. Configuration classes act as a container for bean definitions and also lives as a bean in the ApplicationContext.
Let's look at the usage:
public class SchoolService {
private final ClassService classService;
public SchoolService(ClassService classService) {
this.classService = classService;
}
// Other methods
}
Here, we have another class without any Spring annotation.
We'll also use the @Bean annotation to create a bean for SchoolService:
@Configuration
public class SchoolConfiguration {
@Bean
public SchoolService schoolService() {
return new SchoolService(classService());
}
@Bean
public ClassService classService() {
return new ClassService();
}
}
There are a few things to note here. Firstly, we're defining our SchoolService bean creating another method - schoolService(). Secondly, the SchoolService class requires a ClassService instance as a constructor argument. So we're passing classService() to the constructor. This provides a similar result as the usage of the @Autowired annotation in the annotation-based configuration.
Moreover, the usage of the @Autowired annotation is not restricted to the @Component annotated classes:
public class SchoolService {
@Autowired
private TeacherService teacherService;
private final ClassService classService;
public SchoolService(ClassService classService) {
this.classService = classService;
}
// Other methods
}
Here we're defining TeacherService as a dependency and annotating the field with @Autowired annotation. If Spring finds a bean for TeacherService, it will inject it to the SchoolService bean:
@Configuration
public class SchoolConfiguration {
@Bean
public TeacherService teacherService() {
return new TeacherService();
}
// Other bean definitions
}
Here, we're defining the bean for TeacherService, so Spring can inject it as an @Autowired target.
4. @ComponentScan Annotation
Now, let's look at how we can manage bean scanning in a Java-based configuration.
The @ComponentScan annotation enables us to define how Spring should look for the bean definitions. Although it is not required for Java-based annotation, it helps us to capture multiple @Configuration classes:
@ComponentScan
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = new AnnotationConfigApplicationContext(Application.class);
SchoolService schoolService = applicationContext.getBean(SchoolService.class);
schoolService.teachStudents();
applicationContext.close();
}
}
Here, we're annotating the Application class with @Configuration. As a result, it scans all @Configuration beans.
In our case where we have only one @Configuration class, we can also start the ApplicationContext without @ComponentScan:
public class Application {
public static void main(String[] args) {
ConfigurableApplicationContext applicationContext = new AnnotationConfigApplicationContext(SchoolConfiguration.class);
SchoolService schoolService = applicationContext.getBean(SchoolService.class);
schoolService.teachStudents();
applicationContext.close();
}
}
Here, we aren't using the @ComponentScan annotation and directly specifying the SchoolConfiguration class as the holder of bean definitions.
5. Summary
In this tutorial, we've looked at the Java-based configuration for Spring. We've investigated @Bean, @Configuration, and @ComponentScan annotations.
As always the source code for all examples in this tutorial is available on Github.