【Spring Bean】

1 Introduction

When speaking of the Spring Framework, a "bean" generally refers to an object managed in the Spring IoC container, which is a Java object that is instantiated, assembled, and managed. The Spring framework creates and manages these beans through the IoC container, which means that when the application is running, the container is responsible for instantiating these beans and injecting their dependencies into the container, making the application more flexible and configurable.

In Spring, you can use annotations or XML files to define beans, including specifying the bean's type, properties, and dependencies. These beans can be any type of Java object, including simple POJO classes, data access objects, service classes, and so on.

By using Spring's IoC container and bean definitions, it is easier to achieve loose coupling and dependency injection of applications, making applications easier to maintain and extend.

2. Which annotations can declare a class as a Bean?

In the Spring framework, you can use the following annotations to declare a class as a bean:

@Component:

@Component is a generic Spring annotation used to identify a class as a Spring Bean. This means that when you mark a class with the @Component annotation, Spring will automatically instantiate the class and add it to the application context for use by other parts.

Here is an example using the @Component annotation:

@Component
public class MyComponent {
    
    
    public void doSomething() {
    
    
        // ...
    }
}

In this example, the MyComponent class is marked as a Spring Bean and added to the application context. The MyComponent Bean can be injected in other parts using @Autowired or other dependency injection annotations, for example:

@Service
public class MyService {
    
    
    @Autowired
    private MyComponent myComponent;

    public void doSomethingElse() {
    
    
        myComponent.doSomething();
    }
}

Thus, in MyService, you can use the myComponent member variable to access the method doSomething() in the MyComponent Bean.

@Controller:

Bean components used to declare a control layer, usually used to process Web requests and responses, it is based on the MVC pattern. Used to identify a class as a controller for handling web requests. In this controller class, you can use the @RequestMapping annotation to specify methods for handling different request URLs.

Here is a simple example showing how to use the @Controller annotation:

@Controller
public class HomeController {
    
    

    @RequestMapping("/")
    public String home() {
    
    
        return "index";
    }

    @RequestMapping("/about")
    public String about() {
    
    
        return "about";
    }

}

In this example, the HomeController class is marked with the @Controller annotation, indicating that it is a controller. It defines two methods for handling requests: home() and about().

Both methods use the @RequestMapping annotation to specify the URL to process. For example, the home() method handles requests for the root URL ("/") and returns a view named "index". The about() method handles the request for the URL "/about" and returns a view named "about".

When a request arrives at the server, DispatcherServlet will find a matching processing method according to the requested URL, and return the processing result to the client.

Note that neither the view names "index" nor "about" are specifically defined in this example. Usually, they correspond to a JSP or Thymeleaf template file, and these files need to be placed under the resource folder of the project, such as "/resources/templates" or "/src/main/resources/templates".

@RestController:

It is used to declare a Bean component of the control layer,
and specify that the component returns JSON data.
It is usually used to write RESTful-style interfaces, which are also based on the MVC pattern.
(@Controller is used to process general web requests and responses,
while @RestController will serialize the return value into JSON format, which is suitable for writing Web API.)

Here is a simple example showing how to use the @RestController annotation:

@RestController
public class UserController {
    
    

    @GetMapping("/users")
    public List<User> getUsers() {
    
    
        // 从数据库或其他数据源中获取用户列表
        List<User> users = userService.getUsers();
        return users;
    }

    @GetMapping("/users/{id}")
    public User getUserById(@PathVariable Long id) {
    
    
        // 根据ID从数据库或其他数据源中获取用户
        User user = userService.getUserById(id);
        return user;
    }

    @PostMapping("/users")
    public User createUser(@RequestBody User user) {
    
    
        // 创建用户并保存到数据库或其他数据源中
        User savedUser = userService.createUser(user);
        return savedUser;
    }

    @PutMapping("/users/{id}")
    public User updateUser(@PathVariable Long id, @RequestBody User user) {
    
    
        // 根据ID更新用户信息,并保存到数据库或其他数据源中
        User updatedUser = userService.updateUser(id, user);
        return updatedUser;
    }

    @DeleteMapping("/users/{id}")
    public void deleteUser(@PathVariable Long id) {
    
    
        // 根据ID从数据库或其他数据源中删除用户
        userService.deleteUser(id);
    }
}

In this example, the UserController class is annotated with @RestController to indicate that it is a RESTful controller.

It defines five methods for handling requests, each of which uses a different HTTP method (GET, POST, PUT, DELETE) and a different URL to handle the request. The return values ​​of these methods are Java objects, and Spring Boot will automatically convert them to responses in JSON or XML format.

For example, the getUsers() method handles a GET request for the URL "/users" and returns a List object containing a list of users. The getUserById() method handles the GET request for the URL "/users/{id}" and returns a user object with the specified ID. The createUser() method processes the POST request of the URL "/users", obtains the User object from the request body, creates and saves the user to the data source, and returns the created User object. The updateUser() method processes the PUT request of the URL "/users/{id}", obtains the User object from the request body, updates the user information according to the ID, and returns the updated User object. The deleteUser() method handles the DELETE request for the URL "/users/{id}" and deletes the user from the data source based on the ID.

It should be noted that the User object in this example needs to be defined first, and these methods use some Spring Boot annotations, such as @GetMapping, @PostMapping, @PutMapping, @DeleteMapping, @PathVariable and @RequestBody, which can be used Simplify code and improve development efficiency.

@Service:

The Bean component used to declare a service layer is usually used for injection into the control layer. It is usually based on business logic and implements certain specific business functions.

Here is a simple example demonstrating how to use the @Service annotation in Spring Boot:

@Service
public class MyService {
    
    
    
    public void doSomething() {
    
    
        // 业务逻辑实现
    }
}

In the above example, a service class called MyService is created and it is marked with @Service annotation. This class contains a method called doSomething() that implements the business logic of the service.

When a service is used in an application, its instance can be obtained through dependency injection. Here is an example showing how to inject a service into another class using the @Autowired annotation:

@Service
public class MyService {
    
    
    
    public void doSomething() {
    
    
        // 业务逻辑实现
    }
}

@Controller
public class MyController {
    
    
    
    @Autowired
    private MyService myService;
    
    @RequestMapping("/doSomething")
    public void handleRequest() {
    
    
        myService.doSomething();
    }
}

In the above example, a controller class called MyController is created and MyService is injected into its myService field using the @Autowired annotation. A method called handleRequest() is also created that handles the request and calls the doSomething() method of myService.

In summary, using the @Service annotation is a convenient way of creating a service layer in a Spring Boot application. It allows us to encapsulate business logic in service classes and use them in other parts of the application through dependency injection.

@Repository:

The Bean component used to declare a data access layer is usually used for injection into the service layer. It is based on data access and implements operations on data storage such as databases. This annotation is often used together with @Autowired or @Resource annotations to use Data Access Objects (DAO) in the application.

Here is an example using the @Repository annotation:

First, define a DAO interface:

public interface UserRepository {
    
    
    User findById(Long id);
    List<User> findAll();
    void save(User user);
    void delete(User user);
}

Then, create a class that implements the interface and identify it with the @Repository annotation:

@Repository
public class UserRepositoryImpl implements UserRepository {
    
    

    private final JdbcTemplate jdbcTemplate;

    @Autowired
    public UserRepositoryImpl(JdbcTemplate jdbcTemplate) {
    
    
        this.jdbcTemplate = jdbcTemplate;
    }

    @Override
    public User findById(Long id) {
    
    
        // 实现查找用户的方法
        return null;
    }

    @Override
    public List<User> findAll() {
    
    
        // 实现查找所有用户的方法
        return null;
    }

    @Override
    public void save(User user) {
    
    
        // 实现保存用户的方法
    }

    @Override
    public void delete(User user) {
    
    
        // 实现删除用户的方法
    }
}

In the above example, we used Spring's JdbcTemplate to perform database operations. Injecting the JdbcTemplate through the constructor can make the class a bean in the Spring container, so that the class can be used in the application.

Now, we can use UserRepository in other classes:

@Service
public class UserService {
    
    

    private final UserRepository userRepository;

    @Autowired
    public UserService(UserRepository userRepository) {
    
    
        this.userRepository = userRepository;
    }

    public User findById(Long id) {
    
    
        return userRepository.findById(id);
    }

    public List<User> findAll() {
    
    
        return userRepository.findAll();
    }

    public void save(User user) {
    
    
        userRepository.save(user);
    }

    public void delete(User user) {
    
    
        userRepository.delete(user);
    }
}

In the above example, we have used @Autowired annotation to inject UserRepository. Since UserRepository is annotated with @Repository, the Spring container will automatically create this class as a bean and inject it into UserService.

@Configuration:

Used to declare a class as a configuration class, which typically contains methods for creating and configuring beans for a Spring application context.

Here is an example using the @Configuration annotation:

@Configuration
public class AppConfig {
    
    
    @Bean
    public UserService userService() {
    
    
        return new UserServiceImpl();
    }
}

In this example, the @Configuration annotation identifies a Java class named AppConfig that contains a method named userService that creates and returns a new UserServiceImpl object. By marking this method as @Bean, Spring will use the object returned by the method as a bean in the application context.

In addition, the @Configuration annotation can also be used to define other beans, such as data sources, transaction managers, and so on. You can use the @Autowired annotation to inject these beans into other components such as controllers, services, etc.

@Bean

Used to create and configure objects, usually used in configuration classes. Here is an example using @Bean:

@Configuration
public class MyConfig {
    
    

    @Bean
    public MyService myService() {
    
    
        return new MyServiceImpl();
    }
}

In the above code, @Configuration declares a configuration class, and @Bean declares a method myService(), which returns an instance of type MyService. This instance will be managed by the Spring container.

When using the @Bean annotation, there are some common attributes that can be used, for example:

name:指定 bean 的名称,如果不指定,则使用方法名作为 bean 的名称;
initMethod:指定 bean 初始化时调用的方法;
destroyMethod:指定 bean 销毁时调用的方法。

Here is an example with these properties:

@Configuration
public class MyConfig {
    
    

    @Bean(name = "myService", initMethod = "init", destroyMethod = "cleanup")
    public MyService myService() {
    
    
        return new MyServiceImpl();
    }
}

In the above code, the name attribute of @Bean specifies the name of the bean as myService, and the initMethod and destroyMethod attributes specify the methods called init and cleanup when the bean is initialized and destroyed, respectively.

To use this bean in other classes, just use the @Autowired annotation. For example:

@Service
public class MyServiceImpl implements MyService {
    
    

    @Autowired
    private MyService myService;

    // ...
}

In the above code, the @Autowired annotation is used in the MyServiceImpl class to inject the instance myService of the MyService type, and Spring will automatically inject the MyService bean into the myService variable.

Difference from @Component

Both @Component and @Bean are annotations used to declare beans in the Spring Framework, but they have the following differences:

@Component 是一个泛化的概念,可以用来表示任何组件,
而 @Bean 注解通常用于在配置类中声明一个 bean。

@Component 是在类级别上使用的注解,而 @Bean 是在方法级别上使用的注解。

@Component 可以自动扫描和装配到 Spring 容器中,而 @Bean 需要手动配置。

@Component 的作用域是 singleton,而 @Bean 可以通过配置作用域来改变它的范围。

@Bean 方法可以具有参数,这些参数可以通过 Spring 容器中的其他 bean 注入,
而 @Component 中的依赖关系必须通过其他方式注入,例如使用 @Autowired 或 @Resource。

Therefore, if you need to declare a bean in the configuration class, use the @Bean annotation; if you need to use automatically scanned and assembled components in the application, use the @Component annotation.

@Import

It is used to import other configuration classes, and multiple configuration classes can be combined to realize the configuration of complex applications. Below is a simple example demonstrating how to use the @Import annotation in a Spring Boot application.

Suppose there are two configuration classes: FooConfig and BarConfig. FooConfig is responsible for configuring Foo-related beans, and BarConfig is responsible for configuring Bar-related beans. Now we want to use these two configuration classes in an application at the same time, we can use @Import annotation to achieve.

First, import these two configuration classes using the @Import annotation in the main configuration class:

@Configuration
@Import({
    
    FooConfig.class, BarConfig.class})
public class AppConfig {
    
    
    // ...
}

Curly braces are used here to wrap multiple configuration classes together. Note that the commas here are used to separate different configuration classes. All beans defined in FooConfig and BarConfig are now available in AppConfig.

For example, we can inject the beans defined in FooConfig and BarConfig in AppConfig:

@Configuration
@Import({
    
    FooConfig.class, BarConfig.class})
public class AppConfig {
    
    
    @Autowired
    private FooService fooService;
    
    @Autowired
    private BarService barService;
    
    // ...
}

Here it is assumed that FooService and BarService are beans defined in FooConfig and BarConfig. Now we can inject these two beans in AppConfig and use them.

The @Import annotation also has some other uses, such as importing other Java classes or dynamically selecting configuration classes to import through conditions. For more details, please refer to the official Spring documentation.

3. Which annotations can be injected into Bean

In the Spring framework, the commonly used annotations for injecting beans are as follows:

@Autowired:

Inject a bean into another bean by type autowiring.
When injecting, Spring will look up the corresponding Bean in the container according to the type of the variable, and inject it into the variable.
If multiple beans of the same type are found, specific beans can also be specified by specifying a name or using the @Qualifier annotation.

@Resource:

Inject a bean into another bean by name autowiring.
Unlike @Autowired, @Resource will look up the corresponding Bean by name, not by type.

Difference from @Autowired

Both @Autowired and @Resource are annotations used to inject dependencies in Java, but they have the following differences:

The source is different: @Autowired is an annotation provided by the Spring framework, while @Resource is an annotation provided by the JavaEE standard, but Spring also supports the @Resource annotation.

The automatic assembly method is different: @Autowired performs automatic assembly according to the type. If there are multiple beans of the same type in the container, you need to specify the specific bean through @Qualifier or other methods; while @Resource performs automatic assembly based on the name, you can pass the name Attributes specify concrete bean names.

The scope of dependency is different: @Autowired can be used on fields, methods, constructors, and also on collection types, such as List, Set, Map, etc.; while @Resource can only be used on fields and does not support collection types.

The default value of the required attribute is different: the default value of the required attribute of @Autowired is true, and if no matching Bean is found in the container, NoSuchBeanDefinitionException will be thrown; while the default value of the required attribute of @Resource is true, but no exception will be thrown. Instead, the field will be set to null.

In general, @Autowired is more flexible and can be used in more scenarios, while @Resource is simpler and only suitable for basic injection scenarios.

@Inject:

The same function as @Autowired, injecting a bean into another bean through type autowiring.
The difference is that @Inject is a Java standard annotation, while @Autowired is a Spring framework-specific annotation.

@Value:

Used to inject property values ​​of simple types, such as strings, numbers, booleans, etc.
The attribute value can be specified through the @Value annotation, or the placeholder ${} can be used in the configuration file to dynamically specify the attribute value.

The above are commonly used annotations for injecting beans. Choosing different annotations to inject beans according to the actual situation can improve the readability and flexibility of the code.

4. Bean scope

In Java, the scope of a bean refers to the time range in which the bean instance exists in the container. Commonly used bean scopes include:

Singleton:在整个应用程序中只存在一个实例。该作用域是默认作用域,通常用于状态无关的 Bean。

Prototype:每次请求时都会创建一个新的实例。适合那些状态随请求变化的 Bean。

Request:在每个 HTTP 请求中创建一个新的实例。适合那些与 HTTP 请求相关的 Bean。

Session:在每个 HTTP 会话中创建一个新的实例。适合那些与用户会话相关的 Bean。

Global Session:在一个全局 HTTP 会话中创建一个新的实例。通常用于 Portlet 环境。

Among them, Singleton and Prototype are the most commonly used Bean scopes. Singleton is used for beans that only need one instance during the application life cycle, and Prototype is used for beans that need to create a new instance every time it is called.

Thread Safety Issues of Singleton Bean

In a multi-threaded environment, the shared instance of a singleton bean may cause thread safety issues. Here are two possible problems:

竞态条件:当多个线程同时访问单例 Bean 的实例变量时,可能会发生数据竞争问题,导致结果不确定或不一致。

非线程安全的操作:在单例 Bean 中使用非线程安全的对象或方法时,可能会导致线程安全问题,例如使用非线程安全的 HashMap类。

In order to solve these problems, the following measures can be taken:

同步方法或代码块:可以使用 synchronized 关键字来保证多线程访问单例 Bean 时的同步性。

使用线程安全的对象或方法:可以使用线程安全的对象或方法,例如使用线程安全的 ConcurrentHashMap类。

使用线程池:在高并发环境下,可以使用线程池来控制并发数量,从而减少对单例 Bean 的压力。

5. Bean life cycle

The life cycle of Spring Bean (Java object in the Spring framework) can be roughly divided into the following stages:

实例化:当Spring容器启动时,它会读取配置文件并创建Bean定义,然后实例化Bean。在这个阶段,Spring会使用构造函数或工厂方法来创建Bean的实例。

属性赋值:在Bean实例化后,Spring容器会将配置文件中定义的属性值注入到Bean中。这些属性可以是简单的值、引用其他Bean的引用,或者是集合类型。

初始化:在Bean实例化和属性赋值完成后,Spring容器会调用Bean的初始化方法(如果定义了)。可以在这个阶段执行一些初始化的操作。

使用:在初始化完成后,Bean可以被应用程序使用。在这个阶段,Bean会处理业务逻辑并执行相应的操作。

销毁:当应用程序关闭时,Spring容器会调用Bean的销毁方法(如果定义了)。可以在这个阶段执行一些清理工作,比如释放资源等。

It should be noted that the life cycle of the Bean is controlled by the Spring container, and the initialization and destruction process of the Bean can be controlled by implementing a specific interface or defining a specific method.

Circular dependencies and memory leaks caused by the life cycle of beans

Circular dependency means that two or more beans depend on each other to form a circular dependency relationship. If there is a circular dependency, Spring will use the "early exposure" and "post-processor" mechanisms to resolve it. Early exposure means that in the process of creating a bean, the Spring container will first create an empty bean instance, and then fill its properties. In this way, when dealing with circular dependencies, this empty Bean instance can be used to resolve dependencies. The post-processor is to perform some additional processing on the Bean after the Bean is initialized, such as enhancement, proxy, etc. Through the post-processor, some processing can be performed after the Bean is created to solve the circular dependency problem.

Memory leaks can occur during the Bean destruction phase. If there are some long-lived objects (such as threads, database connections, etc.) in the Bean, and these objects are not properly closed or released, it may cause memory leaks. In order to avoid memory leaks, you can specify the Bean's destruction method by implementing the DisposableBean interface in the Bean or using the destroy-method attribute in the configuration file. In the destroy method, you can manually close or release long-lived objects to ensure they don't cause memory leaks. In addition, Spring also provides an ApplicationContextAware interface, through which the context object of the Spring container can be obtained, and the context object can be used to perform some operations in the destruction method.

The method of destroying the bean

There are two ways to destroy beans in the Spring framework, one is by implementing the DisposableBean interface, and the other is by defining a custom destruction method.

Implement the DisposableBean interface:

The DisposableBean interface is an interface provided by the Spring framework. It has only one method destroy(), which is automatically called when the bean is destroyed. If we want to do some cleanup when destroying the bean, we can make the bean implement the DisposableBean interface and implement the required cleanup logic in the destroy() method. For example:

public class MyBean implements DisposableBean {
    
    

    @Override
    public void destroy() throws Exception {
    
    
        // 在这里实现需要的清理逻辑
    }
}

Custom destroy method:

In addition to implementing the DisposableBean interface, we can also destroy beans by configuring a custom destruction method in the bean definition file. You need to set the destroy-method attribute in the bean tag and specify the method name to be called. For example:

<bean id="myBean" class="com.example.MyBean" destroy-method="cleanup">
</bean>

In this example, when the bean is destroyed, Spring will automatically call the cleanup() method in the MyBean class.

In general, the Spring framework provides a variety of ways to destroy beans, so that we can choose the most appropriate way to clean up according to specific needs, thereby improving the robustness and stability of the application.

Guess you like

Origin blog.csdn.net/qq_43116031/article/details/129893244
Recommended