From the previous episode:
Spring Beans are components that make up the core of the Spring Framework. They are the building blocks for creating applications in the Spring ecosystem. The framework uses the Inversion of Control (IoC) principle to manage the lifecycle and dependencies of these beans. Spring Beans can be defined using XML configuration files, Java configuration, or annotation-based configuration. The use of Spring Beans results in a loosely-coupled architecture, where changes to one component do not affect the rest of the application. This makes the application more flexible and easier to maintain. Additionally, Spring Beans can be easily tested in isolation, promoting maintainability and reducing the risk of introducing bugs.
In this episode, we will learn how to define beans using Java configurations. The goal is to rewrite the same application from Ep. 3. The final application is available here.
New project
So let's start by creating a new Maven project.
Again we need to add spring-context
dependency in the pom.xml
file.
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>com.cristianrita</groupId>
<artifactId>spring-beans-java-config</artifactId>
<version>1.0-SNAPSHOT</version>
<properties>
<maven.compiler.source>18</maven.compiler.source>
<maven.compiler.target>18</maven.compiler.target>
<project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>6.0.4</version>
</dependency>
</dependencies>
</project>
Transitively spring-context
brings other dependencies. We won't go into details, but it's good to know what the framework does behind the scenes.
Next, we will create the main application class and initialize the context using AnnotationConfigApplicationContext
. As I mentioned previously, this class is used to create a context from Java configuration classes. Once we created an instance of the AnnotationConfigApplicationContext
class, we can retrieve beans from it using the getBean()
method.
package com.cristianrita;
import com.cristianrita.config.MyConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MyApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
}
}
Configuration classes
A class annotated with the @Configuration
in Java is a special type of class that is used to define beans and their dependencies in a Spring application context. When you annotate a class with @Configuration
, Spring will use it to generate a bean definition at runtime, which specifies the name and other properties of the beans that the class defines.
package com.cristianrita.config;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyConfig {
}
So how do we create a bean? We have to write a method that returns an instance of a class and annotate that method with @Bean
. To demonstrate this, let's bring the EmailService we implemented in the previous episode.
package com.cristianrita.services;
public interface Sender {
void send(String to, String message);
}
package com.cristianrita.services;
public class EmailService implements Sender {
public EmailService() {
System.out.println("EmailService created!");
}
@Override
public void send(String to, String message) {
System.out.println("Sending email to " + to + " with message " + message);
}
}
Please note that we are not using any stereotype annotation such as @Component
or @Service
.
Going back to the configuration class, let's define the EmailService bean.
package com.cristianrita.config;
import com.cristianrita.services.EmailService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyConfig {
@Bean
public EmailService emailService() {
return new EmailService();
}
}
When Spring Framework creates the context, it will scan the configuration class and call every method annotated with @Bean
annotation to register the beans.
package com.cristianrita;
import com.cristianrita.config.MyConfig;
import com.cristianrita.services.EmailService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MyApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
EmailService emailService = applicationContext.getBean(EmailService.class);
emailService.send("test@email.com", "Hello World");
}
}
To put our hands on the bean, we can use the getBean()
method.
@Bean annotation
When we annotate a method with @Bean
in a Java configuration class, it tells Spring that the method should be used to create a bean. Some important things to know about methods annotated with @Bean
are:
The name of the method becomes the name of the bean unless you explicitly specify a different name using the
name
parameter of the@Bean
annotation.The method should return an instance of the bean that it creates.
The
@Bean
annotation supports several parameters, includinginitMethod
anddestroyMethod
, which can be used to specify methods to be called when the bean is initialized and destroyed, respectively.By default, Spring will only create one instance of a bean for the entire application context, unless you specify a different scope using the
@Scope
annotation on the method or on the class itself. More on bean's scope in a future episode.
Spring assigns a name for every bean it creates. If we do not specify one, Spring will use the name of the method, emailService
in our case.
package com.cristianrita;
import com.cristianrita.config.MyConfig;
import com.cristianrita.services.EmailService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MyApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
EmailService emailService = (EmailService) applicationContext.getBean("emailService");
emailService.send("test@email.com", "Hello World");
}
}
As you can see, another way to get a bean is by passing a name to the getBean()
method. If we want to change the name, we either rename the method or pass the name
attribute to the @Bean
annotation like this:
@Bean(name = "myEmailServiceBean")
Another handy attribute is the initMethod
. Here we can pass the name of a method inside the EmailSerivce class. When Spring is initializing the EmailService bean, it will call this method.
package com.cristianrita.config;
import com.cristianrita.services.EmailService;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class MyConfig {
@Bean(name = "myEmailServiceBean", initMethod = "initializeEmailService")
public EmailService emailService() {
return new EmailService();
}
}
package com.cristianrita.services;
public class EmailService implements Sender {
public EmailService() {
System.out.println("EmailService created!");
}
@Override
public void send(String to, String message) {
System.out.println("Sending email to " + to + " with message " + message);
}
public void initializeEmailService() {
System.out.println("Doing initialization work");
}
}
package com.cristianrita;
import com.cristianrita.config.MyConfig;
import com.cristianrita.services.EmailService;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
public class MyApplication {
public static void main(String[] args) {
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(MyConfig.class);
EmailService emailService = (EmailService) applicationContext.getBean("myEmailServiceBean");
emailService.send("test@email.com", "Hello World");
}
}
Stereotype annotations vs. @Bean
Stereotype annotations, such as @Component
, @Controller
or @Service
are used to mark a class as a particular type of component.
On the other hand, @Bean
is used when you want to manually declare a bean, such as when you need to configure a third-party library or when you need to create a bean that doesn't fit into one of Spring's pre-defined component types.
Generally, it's a good practice to use stereotype annotations for components that fit into one of Spring's pre-defined types, and @Bean
for everything else. However, there may be situations where you need to use @Bean
annotation for a component that would normally be marked with a stereotype annotation, such as when you need to specify additional configuration options or when you need to create a conditional bean.
Conclusion
In summary, the @Bean
annotation is a fundamental building block of the Spring framework, providing developers with a simple and effective way to define and configure beans for their applications.
By using the @Bean
annotation, developers can create modular and flexible code that is easy to test and maintain. This annotation helps to reduce coupling between different components of an application and makes it easier to change the implementation of a bean without affecting the rest of the system.
Overall, the @Bean
annotation is a powerful and essential tool for any developer using the Spring framework, providing a simple and effective way to create, configure, and manage application components.
when is ep 5??