Spring Bean的生命周期
阅读数:228 评论数:0
跳转到新版页面分类
python/Java
正文
一、概述
1、spring容器
spring中有三种bean容器:BeanFactory、ApplicationContext、WebApplicationContext。
(1)BeanFactory
BeanFactory是Bean的工厂,主要负责初始化各种Bean,并调用它们的生命周期方法,实现此接口的常见类是XmlBeanFactory。
(2)ApplicationContext
是BeanFactory的子接口。
二、BeanFactory
Bean本身调用的方法和通过配置文件中的init-method和destory-method指定的方法(通过xml配置或@Bean注解)。
Spring要求init-method是一个无参数的方法,init-method指定的方法可以是public、protected以及private的,并且方法也可以是final的。
init-method是通过反射执行的。
BeanNameAware、BeanFactoryAware、InitializingBean和DisposableBean这些接口的方法。
afterPropertiesSet() | 作用是:当一个Bean实现InitializingBean,#afterPropertiesSet方法里面可以添加自定义的初始化方法或者做一些资源初始化。 |
public class NormalBeanA implements InitializingBean{
@Override
public void afterPropertiesSet() throws Exception {
System.out.println("[InitializingBean] NormalBeanA");
}
}
destroy() | 作用是:当一个单例Bean实现DisposableBean,#destroy可以添加自定义的一些销毁方法或者资源释放操作。 |
public class NormalBeanA implements DisposableBean {
@Override
public void destroy() throws Exception {
System.out.println("[DisposableBean] NormalBeanA");
}
}
Aware接口是指以Aware结尾的一些Spring提供的接口,当你的Bean实现了这些接口的话,在创建过程中会回调对应的set方法,并传入相应的对象。
ApplicationContextAware | 注入ApplicationContext |
ApplicationEventPublisherAware | 注入ApplicationEventPublisher事件发布器 |
BeanFactoryAware | 注入BeanFactory |
BeanNameAware | 注入Bean的名称 |
public class TestBeanFactoryAware implements BeanFactoryAware {
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
System.out.println("[TestBeanFactoryAware] " + beanFactory.getBean(TestBeanFactoryAware.class).getClass().getSimpleName());
}
}
public class NormalBeanA implements BeanNameAware{
public NormalBeanA() {
System.out.println("NormalBean constructor");
}
@Override
public void setBeanName(String name) {
System.out.println("[BeanNameAware] " + name);
}
}
InstantiationAwareBeanPostProcessor和BeanPostProcessor这两个接口
作用是为Bean的初始化前后提供可扩展的空间。
这里说的Bean的初始化是:
bean实现了InitializingBean接口,对应的方法为afterPropertiesSet |
在bean定义的时候,通过init-method设置的方法。 |
实现BeanPostProcessor接口可以在Bean(实例化之后)初始化的前后做一些自定的操作,但是拿到的参数只有BeanDefinition实例和实例名称。
Spring内置的BeanPostProcessor
AutowiredAnnotationBeanPostProcessor | 处理@Autowired、@Value注解 |
CommonAnnotationBeanPostProcessor | 处理@Resource、@PostConstruct、@PreDestroy注解 |
AnnotationAwareAspectJAutoProxyCreator | 处理一些注解或者是AOP切面的动态代理 |
ApplicationContextAwareProcessor | 处理Aware接口注入的 |
AsyncAnnotationBeanPostProcessor | 处理@Async注解 |
ScheduledAnnotationBeanPostProcessor | 处理@Scheduled注解 |
(2)InstantiationAwareBeanPostProcessor
感知Bean实例实例化的处理器。
该接口继承了BeanPostProcess
接口,该类主要的扩展点有以下5个方法
postProcessBeforeInstantiation |
实例化bean之前 |
postProcessAfterInstantiation |
实例化bean之后 |
postProcessPropertyValues |
bean已经实例化完成,在属性注入时阶段触发,@Autowired ,@Resource 等注解原理基于此方法实现 |
postProcessBeforeInitialization |
把bean注入spring上下文之前 |
postProcessAfterInitialization |
把bean注入spring上下文之后 |
public class TestInstantiationAwareBeanPostProcessor implements InstantiationAwareBeanPostProcessor {
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
System.out.println("[TestInstantiationAwareBeanPostProcessor] before initialization " + beanName);
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
System.out.println("[TestInstantiationAwareBeanPostProcessor] after initialization " + beanName);
return bean;
}
@Override
public Object postProcessBeforeInstantiation(Class<?> beanClass, String beanName) throws BeansException {
System.out.println("[TestInstantiationAwareBeanPostProcessor] before instantiation " + beanName);
return null;
}
@Override
public boolean postProcessAfterInstantiation(Object bean, String beanName) throws BeansException {
System.out.println("[TestInstantiationAwareBeanPostProcessor] after instantiation " + beanName);
return true;
}
@Override
public PropertyValues postProcessPropertyValues(PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeansException {
System.out.println("[TestInstantiationAwareBeanPostProcessor] postProcessPropertyValues " + beanName);
return pvs;
}
三、ApplicationContext
我们通常使用ApplicationContext作为Spring容器。
相比于BeanFactory,在BeanFactoryAware的setBeanFactory与BeanPostProcessor之间多了一个ApplicationContextAware的setApplicationContext()方法的调用。
可以在Spring容器实例化Bean对象之前可以修改BeanDefinition
postProcessorBeanFactory()方法 | 在该方法中,可以通过BeanDefinitionRegistry来获取和悠Bean的配置元数据。 |
Spring IoC容器允许BeanFactoryPostProcessor在容器实际实例化任何其它的bean之前读取配置元数据,并有可能修改它。
如果你愿意,你可以配置多个BeanFactoryPostProcessor。你还能通过设置“order”属性来控制BeanFactoryPostProcessor的执行次序。
四、BeanDefinition
Spring容器启动的时候会定位配置文件,加载文件,并解析成BeanDefinition。
在实例化Bean之前在BeanDefinition里头已经有了所有需要实例化时用到的元数据,接下来Spring只需要选择合适的实例化方法以及策略即可。
实例化方法有两大类分别是工厂方法和构造方法实例化,后者更常见。其中Spring默认的实例方法就是无参构造函数实例化。
五、springboot启动过程
进入run方法(还没实例化SpringApplication对象,所以是静态方法run)后,会 new 一个SpringApplication 对象,创建这个对象的构造函数做了一些准备工作。
在SpringApplication的构造方法内,首先会通过 WebApplicationType.deduceFromClasspath(); 方法判断当前应用程序的容器,默认使用的是Servlet 容器,除了servlet之外,还有NONE 和 REACTIVE (响应式编程)
这里加载的初始化器是springboot自带初始化器,从 META-INF/spring.factories 配置文件中加载(对应的是ApplicationContextInitializer接口),自带有2个,分别在源码的jar包的 spring-boot-autoconfigure 项目 和 spring-boot 项目里面各有一个。
监听器加载的是实现了 ApplicationListener接口的类。
deduceMainApplicationClass(); 这个方法仅仅是找到main方法所在的类,为后面的扫包作准备,deduce是推断的意思。
public class SpringApplication {
...
public ConfigurableApplicationContext run(String... args) {
// 这是 Spring 的一个计时器,计算代码的执行时间(ms级别)
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 这俩变量在后面赋值处进行说明
ConfigurableApplicationContext context = null;
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 用来设置java.awt.headless属性值
configureHeadlessProperty();
// 该对象属于组合模式的实现,核心是内部关联的 SpringApplicationRunListener 集合,SpringApplicationRunListener 是 Spring Boot 的运行时监听器
SpringApplicationRunListeners listeners = getRunListeners(args);
// 会在不同的阶段调用对应的方法,这里表示启动run方法被调用
listeners.starting();
try {
// 用来获取 SpringApplication.run(args)传入的参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 获取 properties 配置文件
ConfigurableEnvironment environment = prepareEnvironment(listeners, applicationArguments);
// 设置 spring.beaninfo.ignore 的属性值,判断是否跳过搜索BeanInfo类
configureIgnoreBeanInfo(environment);
// 这里是项目启动时,控制台打印的 Banner
Banner printedBanner = printBanner(environment);
// 这里就是创建 Spring 应用上下文
context = createApplicationContext();
// 获取 spring.factories 中key为 SpringBootExceptionReporter 的类名集合
exceptionReporters = getSpringFactoriesInstances(SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 这里是准备 Spring 应用上下文
prepareContext(context, environment, listeners, applicationArguments, printedBanner);
// 这里是启动 Spring 应用上下文,底层调用的是 ApplicationContext 的 refresh() 方法,到这里就正式进入了 Spring 的生命周期,同时,SpringBoot的自动装配特性也随之启动
refreshContext(context);
// 里面是空的,猜测应该是交由开发人员自行扩展
afterRefresh(context, applicationArguments);
stopWatch.stop();
// 这里打印启动信息
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// ApplicationContext 启动时,调用该方法
listeners.started(context);
// 项目启动后,做的一些操作,开发人员可自行扩展
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
// ApplicationContext 启动完成时,调用该方法
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
...
}
现在调用的run方法是非静态的,是需要实例化后才可以调用的。
// 实例化计时器
StopWatch stopWatch = new StopWatch();
// 开始计时
stopWatch.start();
这个定时器主要用于计算springboot启动花了多长时间。
这里将java.awt.headless设置为true,表示运行在服务器端,在没有显示器器和鼠标键盘的模式下照样可以工作,模拟输入输出设备功能。
(1)创建所有 Spring 运行监听器并发布应用启动事件
(2)启动监听器
将执行run方法时传入的参数封装成一个对象
包含系统属性和用户配置的属性
10、忽略bean信息
11、打印banner信息
如果想要改成自己喜欢的图标该怎么办呢,其实很简单,只需要在resources目录下添加一个 banner.txt 的文件即可。
创建应用上下文的同时也会创建一个DefaultListableBeanFactory,这个也是静态代理的体现。而在执行ApplicationContext的构造函数时,会创建一个AnnotatedBeanDefinitionReader,而这个类中会有一个BeanDefinitionRegistry属性,并将我们一些必须的BeanDefinition添加进去,例如最为核心的 ConfigurationClassPostProcessor也就是在这个时候被添加进去的。
异常报告器是用来捕捉全局异常使用的,当springboot应用程序在发生异常时,异常报告器会将其捕捉并做相应处理,在spring.factories 文件里配置了默认的异常报告器。
需要注意的是,这个异常报告器只会捕获启动过程抛出的异常,如果是在启动完成后,在用户请求时报错,异常报告器不会捕获请求中出现的异常。
(1)实例化单例的beanName生成器
(2)执行初始化方法
(3)将启动参数注册到容器
Spring创建Bean就在这一步完成。
16、刷新上下文后置处理
afterRefresh 方法是启动后的一些处理,留给用户扩展使用,目前这个方法里面是空的。
17、结束计时器
到这一步,springboot其实就已经完成了,计时器会打印启动springboot的时长。
(1)实现ApplicationRunner接口
(2)实现CommandLineRunner接口
六、SpringBoot常用扩展点
(1)SpringApplicationRunListener
public interface SpringApplicationRunListener {
// 在run()方法开始执行时被调用,表示应用刚刚启动,对应的 Spring Boot 事件为 ApplicationStartingEvent
void starting();
// ConfigurableEnvironment 构建完成时调用,对应的 Spring Boot 事件为 ApplicationEnvironmentPreparedEvent
void environmentPrepared(ConfigurableEnvironment environment);
// ApplicationContext 构建完成时调用,对应的 Spring Boot 事件为 ApplicationContextInitializedEvent
void contextPrepared(ConfigurableApplicationContext context);
// ApplicationContext 完成加载但还未启动时调用,对应的 Spring Boot 事件为 ApplicationPreparedEvent
void contextLoaded(ConfigurableApplicationContext context);
// ApplicationContext 已启动,但 callRunners 还未执行时调用,对应的 Spring Boot 事件为 ApplicationStartedEvent
void started(ConfigurableApplicationContext context);
// ApplicationContext 启动完毕被调用,对应的 Spring Boot 事件为 ApplicationReadyEvent
void running(ConfigurableApplicationContext context);
// 应用出错时被调用,对应的 Spring Boot 事件为 ApplicationFailedEvent
void failed(ConfigurableApplicationContext context, Throwable exception);
}
(3)ApplicationContextInitializer
这是整个spring容器在刷新之前初始化ConfigurableApplicationContext
的回调接口.用户可以在整个spring容器还没被初始化之前做一些事情。
public class TestApplicationContextInitializer implements ApplicationContextInitializer {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
System.out.println("[ApplicationContextInitializer]");
}
}
因为这时候spring容器还没被初始化,所以想要自己的扩展的生效,有以下三种方式:
在启动类中用springApplication.addInitializers(new TestApplicationContextInitializer()) 语句加入 |
配置文件配置context.initializer.classes=com.example.demo.TestApplicationContextInitializer |
Spring SPI扩展,在spring.factories中加入
org.springframework.context.ApplicationContextInitializer=com.example.demo.TestApplicationContextInitializer
|
(1)BeanDefinitionRegistryPostProcessor
可以在Spring容器实例化Bean对象之前修改Bean的配置元数据,并向容器中添加新的Bean定义。
当容器读取完 Bean 定义信息后,就会调用 BeanDefinitionRegistryPostProcessor 接口的实现类的 postProcessBeanDefinitionRegistry() 方法。在该方法中,开发者可以通过 BeanDefinitionRegistry 接口来获取和修改 Bean 的配置元数据,并向容器中添加新的 Bean 定义。
public class CustomBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
@Override
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
BeanDefinition beanDefinition = BeanDefinitionBuilder.genericBeanDefinition(CustomBean.class)
.addPropertyValue("name", "Custom Bean")
.getBeanDefinition();
registry.registerBeanDefinition("customBean", beanDefinition);
}
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
}
}
(2)SmartInstantiationAwareBeanPostProcessor
该扩展接口有3个触发点方法:
predictBeanType |
这个方法用于预测Bean的类型,返回第一个预测成功的Class类型,如果不能预测返回null; |
determineCandidateConstructors |
用于确定该bean的构造函数之用,返回的是该bean的所有构造函数列表。用户可以扩展这个点,来自定义选择相应的构造器来实例化这个bean。 |
getEarlyBeanReference |
当有循环依赖的场景,当bean实例化好之后,为了防止有循环依赖,会提前暴露回调方法,用于bean实例化的后置处理。这个方法就是在提前暴露的回调方法中触发。 |
public class TestSmartInstantiationAwareBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor {
@Override
public Class<?> predictBeanType(Class<?> beanClass, String beanName) throws BeansException {
System.out.println("[TestSmartInstantiationAwareBeanPostProcessor] predictBeanType " + beanName);
return beanClass;
}
@Override
public Constructor<?>[] determineCandidateConstructors(Class<?> beanClass, String beanName) throws BeansException {
System.out.println("[TestSmartInstantiationAwareBeanPostProcessor] determineCandidateConstructors " + beanName);
return null;
}
@Override
public Object getEarlyBeanReference(Object bean, String beanName) throws BeansException {
System.out.println("[TestSmartInstantiationAwareBeanPostProcessor] getEarlyBeanReference " + beanName);
return bean;
}
}
(1)Spring SPI机制 SpringFactoriesLoader
Spring的SPI机制规定,配置文件必须在classpath路径下的META-INF文件夹内,文件名必须为spring.factories,文件内容为键值对,一个键可以有多个值,只需要用逗号分割就行,同时键值都需要是类的全限定名。
(2)SpringBoot的@EnableAutoConfiguration
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
//忽略
}
在SpringBoot中,@EnableAutoConfiguration是通过@SpringBootApplication来使用的。
当项目启动的时候,会去从所有的spring.factories文件中读取@EnableAutoConfiguration键对应的值,拿到配置类,然后根据一些条件判断,决定哪些配置可以使用,哪些不能使用。
自动装配机制是SpringBoot的一个很重要的扩展点,很多框架在整合SpringBoot的时候,也都通过自动装配来的,实现项目启动,框架就自动启动的。
(3)Springboot的PropertySourceLoader(@PropertySource)
在SpringBoot环境下,外部化的配置文件支持properties和yaml两种格式。
YamlPropertySourceLoader:解析以yml或者yaml结尾的配置文件。
所以可以看出,要想实现json格式的支持,只需要自己实现可以用来解析json格式的配置文件的PropertySourceLoader就可以了。
SpringBoot支持多种类型的PropertySource扩展及支持@PropertySource注解。
RandomValuePropertySource | 对 random.int, random.long, random.uuid 的支持 |
MapPropertySource | 对 K,V 形式的配置支持 |
JsonPropertySource | 对 Json 类型的配置支持 |
SystemEnvironmentPropertySource | 对配置 key 中使用 .、下划线、中划线、大小写的支持 |
CommandLinePropertySource | 对启动命令参数的支持 |
ServletContextPropertySource | 对 ServletContext 的 initParam 的支持 |
(1)WebMvcConfigurer 参见:spring内部的配置方式
(2)HandlerInterceptor 参见:SpringMVC HandlerMapping组件
(3)HandlerMethodArgumentResolver和HandlerMethodReturnValueHandler 参见:Spring HandlerAdapter组件
参见:springboot mybatis多数据源的两种整合方法
(1)CommandLineRunner和ApplicationRunner
参见:http://1024s.top/mbstudy/mbBlog/blogDetail?blogId=13674
springboot2.0以上配置嵌入式servlet容器时EmbeddedServletContainerCustomizer类被WebServerFactoryCustomizer替代了。
//定制嵌入式的servlet容器
//在Spring Boot2.0以上配置嵌入式Servlet容器时EmbeddedServletContainerCustomizer类被WebServerFactoryCustomizer替代
@Bean
public WebServerFactoryCustomizer<ConfigurableWebServerFactory> webServerFactoryCustomizer(){
return new WebServerFactoryCustomizer<ConfigurableWebServerFactory>(){
//定制嵌入式的servlet容器相关的规则
@Override
public void customize(ConfigurableWebServerFactory factory) {
factory.setPort(8083);
}
};
}