Spring Bean的生命周期是什么样的?
在Spring容器里一个Bean的从创建到销毁一般都是经历了以下几个阶段:
定义阶段
(Bean元信息配置)=>实例化阶段
(创建Bean对象)=>初始化阶段
(执行初始化逻辑)=>使用阶段
(Bean可用)=>销毁阶段
(释放资源)
定义阶段(BeanDefinition解析)
Spring通过配置(XML、注解、Java配置)解析Bean的元数据,生成BeanDefinition
对象 。
BeanDefinition
存储了Bean的类名、作用域(scope)、依赖项(depends-on)、初始化方法、销毁方法
等元数据。
所有BeanDefinition
存储在容器的BeanDefinitionMap
(一个HashMap)中,键为Bean名称,值为BeanDefinition
对象。
解析器:
- XML配置:
XmlBeanDefinitionReader
解析<bean>
标签。 - 注解配置:
ClassPathBeanDefinitionScanner
扫描@Component
等注解。 - Java配置:
ConfigurationClassPostProcessor
解析@Bean
方法。
实例化阶段(创建Bean实例)
根据BeanDefinition
通过反射或工厂方法创建Bean
实例(对象),但此时属性未注入 。
默认通过无参构造方法实例化(若未指定,Spring会强制要求无参构造)。
在
AbstractAutowireCapableBeanFactory
类中的createBeanInstance
方法中实现。
属性值填充(依赖注入)
为Bean的属性设置值或注入依赖。
- 通过
@Autowired
、@Value
、XML
的<property>
等方式注入属性。 - 若注入的依赖是其他Bean,会递归触发依赖Bean的生命周期。
- 循环依赖问题:在属性注入阶段处理循环依赖(通过三级缓存解决)。
在
AbstractAutowireCapableBeanFactory
的populateBean
方法中处理。
Aware接口回调设置
若Bean实现了特定Aware接口,Spring会回调对应方法,注入容器相关对象。
BeanNameAware
:注入Bean在容器中的名称(setBeanName(String beanName)
)。BeanFactoryAware
:注入当前Bean所在的BeanFactory(setBeanFactory(BeanFactory beanFactory)
)。ApplicationContextAware
:若容器是ApplicationContext,注入应用上下文(setApplicationContext(ApplicationContext applicationContext)
)。
在
AbstractAutowireCapableBeanFactory
的initializeBean
方法中调用。
BeanPostProcessor前置处理
在Bean初始化前,允许自定义BeanPostProcessor
对Bean实例进行处理。
主要是调用BeanPostProcessor
的postProcessBeforeInitialization
方法。
常见的实现类
ApplicationContextAwareProcessor
:处理ApplicationContextAware
接口。InitDestroyAnnotationBeanPostProcessor
:处理@PostConstruct
注解。
由
AbstractAutowireCapableBeanFactory
的applyBeanPostProcessorsBeforeInitialization
方法执行。
InitializingBean处理以及自定义init-method处理
执行Bean的初始化逻辑。
InitializingBean
处理,在所有Bean属性设置完成后进行初始化操作。如果Bean实现了InitializingBean
接口,InitializingBean
的afterPropertiesSet
方法会被调用。
自定义init-method处理,如果Bean在配置文件中定义了初始化方法那么该方法会被调用。
例如:通过XML配置init-method或Java配置@Bean(initMethod="xxx")。
在
AbstractAutowireCapableBeanFactory
的invokeInitMethods
方法中调用
BeanPostProcessor后置处理
在Bean初始化后,允许自定义BeanPostProcessor
对Bean实例进行处理。
BeanPostProcessor
的postProcessAfterInitialization
方法会被调用。
常见用途:AOP代理(如AbstractAutoProxyCreator在此阶段为目标对象创建代理)。
由
AbstractAutowireCapableBeanFactory
的applyBeanPostProcessorsAfterInitialization
方法执行
注册DisposableBean回调
如果Bean实现了DisposableBean
接口或在Bean定义中指定了自定义的销毁方法,Spring容器会为这些Bean注册一个销毁回调,确保在容器关闭时能够正确地清理资源。
在
AbstractAutowireCapableBeanFactory
类中的registerDisposableBeanlfNecessary
方法中实现
Bean使用阶段
Bean已完全初始化,可被应用程序使用。 通过依赖注入获取Bean实例(如@Autowired
或ApplicationContext.getBean()
)。
此阶段Bean处于"可用"状态,直到容器关闭。
Bean销毁阶段
容器关闭时,释放Bean资源。
主要步骤:
- 接口回调:若Bean实现了
DisposableBean
,调用destroy
方法。 - 注解:若方法标注了
@PreDestroy
,Spring会调用该方法。 - 自定义销毁方法:通过XML配置
destroy-method
或Java配置@Bean(destroyMethod="xxx")
。 - 资源释放:如关闭数据库连接、释放文件句柄等。
在
DisposableBeanAdapter
的destroy
方法中实现
总结
通过代码出处,可以观察到整个Bean的创建的过程都依赖于AbstractAutowireCapableBeanFactory
这个类,而销毁主要依赖DisposableBeanAdapter
这个类。
AbstractAutowireCapableBeanFactory
的入口处,doCreateBean
的核心代码如下,其中包含了实例化、设置属性值、初始化Bean以及注册销毁回调 的几个核心方法。
这里就不贴代码了,想更深入看细节的可以去看源码。
Spring中创建Bean的方式有哪些?
基于注解的自动扫描
通过注解标记类,并配合组件扫描实现自动注册。
常见的注解有
@Component
, @Service
, @Repository
, @Controller
(及其衍生注解)。
例如:当在类上添加@Component
时,再在配置类或 XML 中启用组件扫描(@ComponentScan
或 <context:component-scan>
)。这个类在服务启动时会自动被扫描到,然后注入到Spring容器。
java
@Configuration
@ComponentScan("com.jimoer.service")
public class BeanConfig {
}
java
@Service
public class UserService {
public void hello() {
System.out.println("Hello from UserService");
}
}
@Component
public class UserHandler {
public void hello() {
System.out.println("Hello from UserHandler");
}
}
@Repository
public class UserRepository {
public void hello() {
System.out.println("Hello from UserRepository");
}
}
@Controller
public class UserController {
public void hello() {
System.out.println("Hello from UserController");
}
}
使用@Configuration与@Bean 注解
通过 @Configuration
标注的配置类,显式定义 Bean 的创建逻辑。
适用于:需要精确控制 Bean 的初始化逻辑(如依赖其他 Bean 或复杂条件)。
java
@Configuration
public class AppConfig {
@Bean
public UserService userService() {
return new UserService();
}
}
XML 配置文件
通过 xml 的方式来定义 Bean。
在SpringBoot 流行以前,这种方式挺多的, SpringBoot 流行起来之后,这么用的越来越少了。
xml
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"
<bean id="userService" class="com.jimoer.demo.UserServiceImpl">
<property name="message" value="Hello Spring!" />
</bean>
</beans>
java
ApplicationContext context = new ClassPathXmlApplicationContext("applicationContext.xml");
UserService userService = context.getBean("userService");
更适用于遗留项目或需要与非注解配置兼容的场景。
使用@Import注解
@Import
注解的作用是快速导入某一个或多个类,使这些类能够被Spring加载到IOC容器中进行管理。
让类被Spring 的 IOC 容器管理,这不也是创建 Bean 么,因此,这种方式也可以算是创建Bean的一种方式。
java
@Import({UserServiceImpl.class})
@Configuration
public class UserBeanConfiguration {
}
自定义注解
通过自定义一种注解,然后在 Spring 应用启动过程中,通过自定义的 BeanDefinitionRegistryPostProcessor
和 BeanfactoryPostProcessor
来扫描配置的包路径,识别出带有自定义注解的类。
这些处理器解析注解中的属性(如接口类、版本号、超时时间等),并基于这些信息创建 Spring的 BeanDefinition
。
例如:Dubbo框架使用的@DubboService
注解
java
@DubboService("version=1.0.0")
public class UserServiceImpl implements UserFacadeService {
}
动态注册(运行时注册)
在运行时通过 BeanDefinitionRegistry 动态注册 Bean。
java
// 获取 BeanFactory
ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
// 定义 Bean 的元数据
GenericBeanDefinition userDefinition = new GenericBeanDefinition();
userDefinition.setBeanClass(UserService.class);
// 注册 Bean
beanFactory.registerBeanDefinition("userService", userDefinition);
适用于:根据运行时条件动态生成 Bean(如插件化系统、动态配置)。
Spring Bean的注入方式有哪些?
使用@Autowired注解
@Autowired
注解是Spring框架提供的一个注解,支持多种方式自动将Spring的bean注入到其他Bean中。
字段注入
java
@Component
public class JimoerUserService {
@Autowired
private UserRepository userRepository;
}
构造方法注入
java
@Component
public class JimoerUserService {
private final UserRepository userRepository;
// Spring 4.3+ 可省略 @Autowired(单构造器)
@Autowired
public JimoerUserService(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
setter注入
java
@Component
public class JimoerUserService {
private UserRepository userRepository;
@Autowired
public void setUserRepository(UserRepository userRepository) {
this.userRepository = userRepository;
}
}
使用@Resource和@Inject注解
除了Spring提供的注解,JDK也提供了可以互相注入Bean的注解,有@Resource
和@Inject
java
@Component
public class JimoerUserService {
@Resource
private UserRepository userRepository;
}
@Component
public class JiomerUserService {
@Inject
private UserRepository userRepository;
}
使用XML配置注入
如何不使用注解注入,还可以使用XML文件的配置进行Bean的互相注入。
xml
<bean id="userRepository" class="com.jimoer.UserRepository"/>
<!-- 构造方法注入 -->
<bean id="userService" class="com.jiomer.UserService">
<constructor-arg ref="userRepository"/>
</bean>
<!-- 字段注入 -->
<bean id="jimoerUserService" class="com.jiomer.JimoerUserService">
<property name="userRepository" ref="userRepository"/>
</bean>
构造方法自动注入
其实从 Spring 4.3 开始,除非一个类中声明了至少两个构造函数,否则不需要用 @Autowired
标注构造函数,这个构造函数也能直接注入 Bean。
java
@Component
public class JimoerUserService {
private UserRepository userRepository;
public JimoerUserService(UserRepository userRepository){
this.userRepository=userRepository;
}
}
Spring Bean的作用域有哪些?
Spring的Bean的作用域,就是指这个Bean在哪个范围内可以被使用。
不同的作用域决定了Bean的创建管理和销毁的方式。
常见的作用域有Singleton
、Prototype
、Request
、Session
、Application
这五种。
在代码中,可以在定义一个Bean的时候,通过@Scope
注解来指定他的作用域。
如果没有指定Bean的作用域,默认是Singleton
(单例)。
Singleton(单例)
- 周期:Spring 容器启动时创建实例,容器关闭时销毁。
- 作用域:每个Spring IOC容器,只创建一个Bean实例。
- 适用于:无状态服务(如工具类、缓存管理器、数据库连接池)。
- 线程安全:需注意,若 Bean 有可变状态(即Bean中存在线程共享变量),需通过同步机制或线程安全集合处理。
- 配置方式:
java
@Component // 默认即为 singleton
public class SingletonBean {
}
Propertype(原型)
- 周期:每次调用
getBean()
或注入时创建新实例,容器不负责销毁。 - 适用于:有状态 Bean(如用户会话数据、临时对象)。
- 线程安全:实例独立,避免线程安全问题。
- 配置方式:
java
@Component
@Scope("prototype")
public class PrototypeBean {
}
Request(HTTP 请求)
- 周期:每个 HTTP 请求创建一个实例,请求结束后销毁。
- 适用于:Web 应用中请求级别的数据共享(如请求日志、上下文信息)。
仅适用于 Web 应用环境。
- 配置方式:
java
@Component
@Scope("request")
public class RequestBean {
}
Session(HTTP 会话)
- 周期:每个用户会话(
HttpSession
)创建一个实例,会话结束时销毁。 - 适用于:用户会话数据(如购物车、用户偏好设置)。
仅适用于 Web 应用环境。
- 配置方式:
java
@Component
@Scope("session")
public class SessionBean {
}
Application(应用)
- 周期:Web 应用启动时创建实例,应用关闭时销毁。
- 适用于:全局配置或共享资源(如应用级缓存、配置信息)。类似
singleton
,但绑定到ServletContext
。
仅适用于 Web 环境
- 配置方式:
java
@Component
@Scope("application")
public class ApplicationBean {
}
Websocket(WebSocket 会话)
- 周期:
WebSocket
连接建立时创建实例,连接关闭时销毁。 - 适用于:
WebSocket
会话上下文数据(如实时通信状态)。
仅适用于 WebSocket 应用。
- 配置方式:
java
@Component
@Scope("websocket")
public class WebSocketBean {
}
自定义作用域
一般情况下,在开发过程中,都是使用Singleton
作用域,有时候也会用Propertype
,其他几个用的都不多。但是除了上面列举的6个Spring提供作用域以外,还可以自己定义Bean作用域。
自定义一个Spring Bean的作用域,需要实现org.springframework.beans.factory.config.Scope
接口,主要是实现如下几个方法来管理Bean的生命周期。
java
package org.springframework.beans.factory.config;
import org.springframework.beans.factory.ObjectFactory;
public interface Scope {
Object get(String var1, ObjectFactory<?> var2);
Object remove(String var1);
void registerDestructionCallback(String var1, Runnable var2);
Object resolveContextualObject(String var1);
String getConversationId();
}
自定义一个类,然后实现Scope
接口,来实现我们自己的Bean作用域。
java
public class JimoerScope implements Scope{
@Override
public Object get(String s, ObjectFactory<?> objectFactory) {
// 获取Bean的逻辑
return objectFactory.getObject();
}
@Override
public Object remove(String s) {
// 移除Bean的逻辑
return null;
}
@Override
public void registerDestructionCallback(String s, Runnable runnable) {
// 注册Bean销毁时的回调
}
@Override
public Object resolveContextualObject(String s) {
// 解析上下文
return null;
}
@Override
public String getConversationId() {
// 获取会话ID
return "";
}
}
接下来,我们将Spring配置中注册这个自定义的作用域。
这可以通过ConfigurableBeanFactory.registerScope
方法实现。
java
import org.springframework.beans.factory.config.ConfigurableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class AppConfig {
@Bean
public JimoerScope jimoerScope(ConfigurableBeanFactory beanFactory) {
JimoerScope jimoerScope = new JimoerScope();
beanFactory.registerScope("jimoer", jimoerScope);
return jimoerScope;
}
}
此时在Bean定义中使用自定义的作用域的名称jimoer
。
Spring 容器将会根据你的自定义逻辑来创建和管理这些 Bean。
java
@Component
@Scope("jimoer")
public class CustomerScopeTest {
}