springboot bean生命周期和作用域
在Spring的IoC(控制反转)和DI(依赖注入)机制中,Spring框架提供了工具来帮助我们管理对象及其依赖关系。Spring通过注解来声明Bean对象,这样更加简洁且易于维护。以下是常用的注解及其用途:
1.Spring对象的控制
1.1.声明Bean对象
- @Controller通常用于标识Web控制器类;
- @Service用于业务逻辑层;
- @Repository用于数据访问层。
上面三个注解都包含@Component注解,这意味着它们都可以被组件扫描自动检测并注册为Spring容器中的Bean。 - @Component:这是一个通用的注解,用于标记任何Spring管理的组件。允许Spring通过组件扫描机制自动发现并注册到应用上下文中。
- @Configuration:这个注解用于定义一个配置类,其中可以使用@Bean注解的方法来定义Spring管理的bean。配置类本身也是一个Spring bean。
- @Bean:在配置类内部使用,用来显式声明一个或多个方法返回的对象应由Spring容器管理。通常与@Configuration一起使用。
1.2.获取对象
Spring提供了两种主要的方式来获取Spring管理的Bean实例:
- ApplicationContext:这是Spring的核心接口之一,它扩展了BeanFactory的功能,增加了事件发布、国际化支持等功能。常见的实现包括ClassPathXmlApplicationContext, AnnotationConfigApplicationContext等。通过ApplicationContext获取Bean的方式非常直观,例如:
ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
MyBean myBean = context.getBean(MyBean.class);
- BeanFactory:这是Spring IoC容器的基础接口,提供了基本的DI功能。相比ApplicationContext,它不支持基于注解的配置,也不提供一些高级特性如AOP代理等。
1.3. 依赖注入
Spring提供了多种方式进行依赖注入
- @Autowired:最常用的注解之一,可用于构造器、字段以及setter方法上。可以自动装配依赖项,无需手动查找和创建依赖对象。
@Service
public class MyService {
private final MyDependency myDependency;
@Autowired // 构造器注入
public MyService(MyDependency myDependency) {
this.myDependency = myDependency;
}
}
- Setter方法注入:除了构造器注入外,还可以通过setter方法进行依赖注入。这种方式允许在对象创建后动态改变依赖关系,但不如构造器注入安全。
@Service
public class MyService {
private MyDependency myDependency;
@Autowired
public void setMyDependency(MyDependency myDependency) {
this.myDependency = myDependency;
}
}
构造函数注入:推荐的方式,在需要不可变属性的情况下。使得对象一旦创建就处于完全初始化状态,并且避免了潜在的空指针异常问题。
2.Spring Boot Bean 作用域
2.1.Spring支持多种不同的Bean作用域,默认的是singleton。
以下是几种常见的作用域:
- Singleton:默认的作用域,在整个应用上下文中只会存在一个实例。适用于无状态的服务类对象。
- Prototype:每次请求都会创建一个新的实例。适合有状态的对象。
- Request:每个HTTP请求都会有单独的Bean实例,仅适用于Web应用。
- Session:每个HTTP会话都会有单独的Bean实例,同样只适用于Web应用。
- Application:在整个ServletContext范围内有效,类似于Servlet API中的ServletContext属性。
2.2.Spring Boot Bean的作用域决定了Bean的生命周期和可见性范围。
以下是几种主要作用域的区别:
- Singleton(单例):
默认作用域:每个Spring IoC容器中仅存在一个实例。
适用场景:适用于无状态的Bean,如服务层对象或DAO层对象。
特点:所有对该Bean的请求都会返回同一个实例,因此在多线程环境下需要注意线程安全问题。 - Prototype(原型):
每次获取都会创建新实例:每次通过容器请求该Bean时,都会创建一个新的实例。
适用场景:适用于有状态的Bean,例如需要独立状态的对象。
特点:由于每次都创建新实例,因此不支持销毁方法,也无法利用Spring容器管理Bean的完整生命周期。 - Request(请求):
每个HTTP请求对应一个实例:在Web应用中,为每个HTTP请求创建一个Bean实例,在请求完成后实例被丢弃。
适用场景:适用于需要保持请求间数据隔离的场景。
特点:仅在基于Web的Spring ApplicationContext实现中有效。 - Session(会话):
每个HTTP会话对应一个实例:在Web应用中,为每个HTTP会话创建一个Bean实例,在会话结束后实例被销毁。
适用场景:适合需要跨多个请求共享数据的情况。
特点:同样,只在基于Web的Spring ApplicationContext实现中有效。 - Application(应用程序):
整个ServletContext范围内有效:在Web应用中,为整个ServletContext创建一个Bean实例,类似于Servlet API中的ServletContext属性。
适用场景:适用于需要在整个Web应用中共享的数据。
特点:与Singleton类似,但其作用范围限定于Web应用的ServletContext。
2.3.示例
其中单列作用域和原型作用域使用@scope注解,其余作用域使用对应名字的注解。或者使用下面样式
@Scope(value =WebApplicationContext.SCOPE_REQUEST, proxyMode =
ScopedProxyMode.TARGET_CLASS)
- Singleton(单例)
@Component
@Scope("singleton") // 默认,可以省略
public class SingletonBean {
public void sayHello() {
System.out.println("Singleton: Hello from SingletonBean");
}
}
- Prototype(原型)
@Component
@Scope("prototype")
public class PrototypeBean {
public void sayHello() {
System.out.println("Prototype: Hello from PrototypeBean");
}
}
- Request(请求)
为了演示Request,需要一个Web应用环境
@Component
@RequestScope
public class RequestBean {
private final HttpServletRequest request;
public RequestBean(HttpServletRequest request) {
this.request = request;
}
public void sayHello() {
System.out.println("Request: Hello from RequestBean, Request ID: " + request.hashCode());
}
}
Controller使用这个Bean:
@RestController
public class RequestController {
@Autowired
private RequestBean requestBean;
@GetMapping("/request")
public String getRequestMessage() {
requestBean.sayHello();
return "Check console for output";
}
}
- Session(会话)
类似于Request作用域
@Component
@SessionScope
public class SessionBean {
private final HttpSession session;
public SessionBean(HttpSession session) {
this.session = session;
}
public void sayHello() {
System.out.println("Session: Hello from SessionBean, Session ID: " + session.getId());
}
}
Controller使用这个Bean:
@RestController
public class SessionController {
@Autowired
private SessionBean sessionBean;
@GetMapping("/session")
public String getSessionMessage() {
sessionBean.sayHello();
return "Check console for output";
}
}
- Application(应用程序)
@Component
@ApplicationScope
public class ApplicationBean {
private final ServletContext servletContext;
public ApplicationBean(ServletContext servletContext) {
this.servletContext = servletContext;
}
public void sayHello() {
System.out.println("Application: Hello from ApplicationBean, Context Path: " + servletContext.getContextPath());
}
}
使用该Bean的Controller:
@RestController
public class ApplicationController {
@Autowired
private ApplicationBean applicationBean;
@GetMapping("/application")
public String getApplicationMessage() {
applicationBean.sayHello();
return "Check console for output";
}
}
3.Spring Boot Bean 生命周期
Spring Boot Bean的生命周期是Spring框架中一个核心概念,它定义了从Bean的创建到销毁过程中所经历的不同阶段。理解 Bean的生命周期这些阶段有助于更好地设计和实现基于Spring的应用程序
3.1.步骤
-
实例化:创建Bean的实例。
Spring容器根据Bean的定义信息(如XML配置、注解或Java配置)创建Bean的实例。 -
属性填充:设置Bean的属性值。
Spring将配置文件中定义的属性值赋给Bean实例。如:通过构造器注入、setter方法注入或其他方式注入的 -
初始化前回调:调用Aware接口的方法和BeanPostProcessor的前置处理。
如果bean实现了BeanNameAware(设置Bean名称),BeanFactoryAware(创建该Bean的BeanFactory的引用) 则会执行 -
初始化:调用InitializingBean的afterPropertiesSet()方法或自定义初始化方法。
如果bean实现了InitializingBean接口,则会调用其afterPropertiesSet()方法,另外可以通过@PostConstruct注解或在配置文件中指定init-method来定义自己的初始化方法。 -
初始化后回调:调用BeanPostProcessor的后置处理。
允许在初始化方法前后调用自定义BeanPostProcessor#postProcessBeforeInitialization在初始化之前执行的逻辑和BeanPostProcessor#postProcessAfterInitialization在初始化之后执行的逻辑 -
使用:Bean准备就绪,可以被应用程序使用。
-
销毁:调用DisposableBean的destroy()方法或自定义销毁方法。
如果Bean实现了DisposableBean接口,则会调用其destroy()方法。另外也可通过在配置文件中指定destroy-method来定义自己的销毁方法
3.2.bean生命周期演示
- 定义bean
//bean1
@Component
public class MyDependency {
public MyDependency() {
System.out.println("MyDependency created");
}
}
//bean2
@Component
public class MyBean implements InitializingBean, DisposableBean, BeanNameAware, BeanFactoryAware {
private MyDependency myDependency;
private String someProperty = "TEST";
private String beanName;
private BeanFactory beanFactory;
public String getSomeProperty() {
return someProperty;
}
public void setSomeProperty(String someProperty) {
this.someProperty = someProperty;
}
@Autowired
public MyBean(MyDependency myDependency) {
this.myDependency = myDependency;
System.out.println("属性填充: " + myDependency);
}
@PostConstruct
public void init() {
System.out.println("PostConstruct 自定义初始化方法调用");
}
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("InitializingBean.afterPropertiesSet() 调用");
}
//DisposableBean接口:如果实现了DisposableBean接口,则会调用其destroy()方法
@Override
public void destroy() throws Exception {
System.out.println("DisposableBean.destroy() 调用");
}
//BeanNameAware:设置Bean名称。
@Override
public void setBeanName(String name) {
this.beanName = name;
System.out.println</