bean 生命周期
Spring 只帮我们管理单例模式 Bean 的完整生命周期,对于 prototype 的 bean ,Spring 在创建好交给使用者之后则不会再管理后续的生命周期。
注解方式
在 bean 初始化时会经历几个阶段,首先可以使用注解 @PostConstruct
, @PreDestroy
来在 bean 的创建和销毁阶段进行调用:
@Component
public class AnnotationBean {
private final static Logger LOGGER = LoggerFactory.getLogger(AnnotationBean.class);
@PostConstruct
public void start(){
LOGGER.info("AnnotationBean start");
}
@PreDestroy
public void destroy(){
LOGGER.info("AnnotationBean destroy");
}
}
InitializingBean, DisposableBean 接口
还可以实现 InitializingBean,DisposableBean
这两个接口,也是在初始化以及销毁阶段调用:
@Service
public class SpringLifeCycleService implements InitializingBean,DisposableBean{
private final static Logger LOGGER = LoggerFactory.getLogger(SpringLifeCycleService.class);
@Override
public void afterPropertiesSet() {
LOGGER.info("SpringLifeCycleService start");
}
@Override
public void destroy() {
LOGGER.info("SpringLifeCycleService destroy");
}
}
自定义初始化和销毁方法
也可以自定义方法用于在初始化、销毁阶段调用:
@Configuration
public class LifeCycleConfig {
@Bean(initMethod = "start", destroyMethod = "destroy")
public SpringLifeCycle create(){
SpringLifeCycle springLifeCycle = new SpringLifeCycle() ;
return springLifeCycle ;
}
}
public class SpringLifeCycle{
private final static Logger LOGGER = LoggerFactory.getLogger(SpringLifeCycle.class);
public void start(){
LOGGER.info("SpringLifeCycle start");
}
public void destroy(){
LOGGER.info("SpringLifeCycle destroy");
}
}
复制代码
以上是在 SpringBoot 中可以这样配置,如果是原始的基于 XML 也是可以使用:
<bean class="com.crossoverjie.spring.SpringLifeCycle" init-method="start"
destroy-method="destroy" />
实现 *Aware 接口
*Aware
接口可以用于在初始化 bean 时获得 Spring 中的一些对象,如获取 Spring 上下文
等。
@Component
public class SpringLifeCycleAware implements ApplicationContextAware {
private final static Logger LOGGER = LoggerFactory.getLogger(SpringLifeCycleAware.class);
private ApplicationContext applicationContext ;
@Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext ;
LOGGER.info("SpringLifeCycleAware start");
}
}
这样在 springLifeCycleAware
这个 bean 初始化会就会调用 setApplicationContext
方法,并可以获得 applicationContext
对象。
BeanPostProcessor 增强处理器
实现 BeanPostProcessor 接口,Spring 中所有 bean 在做初始化时都会调用该接口中的两个方法,可定义一个注解,然后在这个方法里拦截所有该注解,做一些特殊的处理。
IOC
ioc 类图
主要常用的ioc 容器有 BeanFactory 和 ApplicationContext
IOC
Spring容器的顶层接口是:BeanFactory
,但使用更多的是它的子接口:ApplicationContext
。
通常情况下,如果想要手动初始化通过xml文件
配置的Spring容器时,代码是这样的:
ClassPathXmlApplicationContext applicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");User user = (User)applicationContext.getBean("name");
如果想要手动初始化通过配置类
配置的Spring容器时,代码是这样的:
AnnotationConfigApplicationContext applicationContext = new AnnotationConfigApplicationContext(Config.class);User user = (User)applicationContext.getBean("name");
这两个类应该是最常见的入口了,它们却殊途同归,最终都会调用refresh
方法,该方法才是Spring容器初始化的真正入口。
其实调用refresh
方法的类并非只有这两个,用一张图整体认识一下:
虽说调用refresh
方法的类有这么多,这里用ClassPathXmlApplicationContext
类作为列子进行分析,因为它足够经典,而且难度相对来说要小一些。
refresh方法
refresh
方法是spring ioc
的真正入口,它负责初始化Spring容器。
既然这个方法的作用是初始化Spring容器,那方法名为啥不叫init
?
答案很简单,因为它不只被调用一次。
在SpringBoot的SpringAppication
类中的run
方法会调用refreshContext
方法,该方法会调用一次refresh
方法。
在SpringCloud的BootstrapApplicationListener
类中的onApplicationEvent
方法会调用SpringAppication
类中的run
方法。也会调用一次refresh
方法。
这是SpringBoot项目中如果引入了SpringCloud,则refresh方法会被调用两次的原因。
在springmvc
的FrameworkServlet
类中的initWebApplicationContext
方法会调用configureAndRefreshWebApplicationContext
方法,该方法会调用一次refresh
方法,不过会提前判断容器是否激活。
所以这里的refresh
表示重新构建的意思。
重点看看refresh
的关键步骤:
其实上图中一眼看过去好像有很多方法,但是真正的核心的方法不多,其中最重要的:
- obtainFreshBeanFactory
- invokeBeanFactoryPostProcessors
- registerBeanPostProcessors
finishBeanFactoryInitialization
解析xml配置文件
obtainFreshBeanFactory
方法会解析xml的bean配置,生成BeanDefinition
对象,并且注册到Spring容器中(就是很多map集合中)。
经过几层调用,会调到AbstractBeanDefinitionReader
类的loadBeanDefinitions
方法:
该方法会循环locations
(applicationContext.xml文件路径),调用另外一个loadBeanDefinitions
方法,一个文件一个文件解析。
经过一些列的操作,会将location
转换成inputSource
和resource
,然后再转换成Document
对象,方便解析。
在解析xml文件时,需要判断是默认标签,还是自定义标签,处理逻辑不一样:
Spring的默认标签只有4种:<import/>
<alias/>
<bean/>
<beans/>
对应的处理方法是:
注意常见的:<aop/>
、<context/>
、<mvc/>
等都是自定义标签。
从上图中处理<bean/>
标签的processBeanDefinition
方法开始,经过一系列调用,最终会调到DefaultBeanDefinitionDocumentReader
类的processBeanDefinition
方法。
这个方法包含了关键步骤:解析元素生成BeanDefinition 和 注册BeanDefinition。
生成BeanDefinition
下面重点看看BeanDefinition是如何生成的。
上面的方法会调用BeanDefinitionParserDelegate
类的parseBeanDefinitionElement
方法:
一个<bean/>
标签会对应一个BeanDefinition
对象。
该方法又会调用同名的重载方法:processBeanDefinition
,真正创建BeanDefinition
对象,并且解析一系列参数填充到对象中:
其实真正创建BeanDefinition
的逻辑是非常简单的,直接new
了一个对象:
真正复杂的地方是在前面的各种属性的解析和赋值上。
注册BeanDefinition
上面通过解析xml文件生成了很多BeanDefinition
对象,下面就需要把BeanDefinition
对象注册到Spring容器中,这样Spring容器才能初始化bean。
在BeanDefinitionReaderUtils
类的registerBeanDefinition
方法很简单,只有两个流程:
先看看DefaultListableBeanFactory
类的registerBeanDefinition
方法是如何注册beanName
的:
接下来看看SimpleAliasRegistry
类的registerAlias
方法是如何注册alias
别名的:
这样就能通过多个不同的alias
找到同一个name
,再通过name
就能找到BeanDefinition
。
修改BeanDefinition
上面BeanDefinition
对象已经注册到Spring容器当中了,接下来,如果想要修改已经注册的BeanDefinition
对象该怎么办呢?refresh
方法中通过invokeBeanFactoryPostProcessors
方法修改BeanDefinition
对象。
经过一系列的调用,最终会到PostProcessorRegistrationDelegate
类的invokeBeanFactoryPostProcessors
方法:
流程看起来很长,其实逻辑比较简单,主要是在处理BeanDefinitionRegistryPostProcessor
和BeanFactoryPostProcessor
。
而BeanDefinitionRegistryPostProcessor
本身是一种特殊的BeanFactoryPostProcessor
,它也会执行BeanFactoryPostProcessor
的逻辑,只是加了一个额外的方法。ConfigurationClassPostProcessor
可能是最重要的BeanDefinitionRegistryPostProcessor
,它负责处理@Configuration
注解。
注册BeanPostProcessor
处理完前面的逻辑,refresh
方法接着会调用registerBeanPostProcessors
注册BeanPostProcessor
,它的功能非常强大,后面的文章会详细讲解。
经过一系列的调用,最终会到PostProcessorRegistrationDelegate
类的registerBeanPostProcessors
方法:
注意,这一步只是注册BeanPostProcessor
,真正的使用在后面。
总结
主要介绍了:
- Spring容器初始化的入口
- refresh方法的主要流程
- 解析xml配置文件
- 生成BeanDefinition
- 注册BeanDefinition
- 修改BeanDefinition
- 注册BeanPostProcessor
AOP
AOP 主要做三件事:
- 在哪里切入,也就是权限校验等非业务操作在哪些业务代码中执行。
- 在什么时候切入,是业务代码执行前还是执行后。
- 切入后做什么事,比如做权限校验、日志记录等。
AOP 体系: