Spring Boot -- 启动流程分析之ApplicationContext 中
上一节我们已经分析到AbsractApplicationContext类refresh方法中的postProcessBeanFactory方法,在分析registerBeanPostProcessors之前我们先介绍一下Spring 的钩子接口。
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// Prepare this context for refreshing.
prepareRefresh(); // Tell the subclass to refresh the internal bean factory.
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // Prepare the bean factory for use in this context.
prepareBeanFactory(beanFactory); try {
// Allows post-processing of the bean factory in context subclasses.
postProcessBeanFactory(beanFactory); // Invoke factory processors registered as beans in the context.
invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation.
registerBeanPostProcessors(beanFactory); // Initialize message source for this context.
initMessageSource(); // Initialize event multicaster for this context.
initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses.
onRefresh(); // Check for listener beans and register them.
registerListeners(); // Instantiate all remaining (non-lazy-init) singletons.
finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event.
finishRefresh();
} catch (BeansException ex) {
if (logger.isWarnEnabled()) {
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
} // Destroy already created singletons to avoid dangling resources.
destroyBeans(); // Reset 'active' flag.
cancelRefresh(ex); // Propagate exception to caller.
throw ex;
} finally {
// Reset common introspection caches in Spring's core, since we
// might not ever need metadata for singleton beans anymore...
resetCommonCaches();
}
}
}
一、钩子接口介绍
Spring 提供了非常多的扩展接口,官方将这些接口称之为钩子,这些钩子会在特定的时间被回调,以此来增强 Spring 功能,众多优秀的框架也是通过扩展这些接口,来实现自身特定的功能,如 SpringBoot、mybatis 等。
二、Aware接口
Aware从字面的意思理解就是"知道"、“感知”的意思,是用来获取Spring内部对象的接口。Aware自身是一个顶级接口,它有一系列子接口,在一个 Bean 中实现这些子接口并重写里面的 set 方法后,Spring 容器启动时,就会回调该 set 方法,而相应的对象会通过方法参数传递进去。我们以其中的 ApplicationContextAware 接口为例。
2.1、ApplicationContextAware
大部分 Aware 系列接口都有一个规律,它们以对象名称为前缀,获取的就是该对象,所以 ApplicationContextAware 获取的对象是 ApplicationContext 。
public interface ApplicationContextAware extends Aware { /**
* Set the ApplicationContext that this object runs in.
* Normally this call will be used to initialize the object.
* <p>Invoked after population of normal bean properties but before an init callback such
* as {@link org.springframework.beans.factory.InitializingBean#afterPropertiesSet()}
* or a custom init-method. Invoked after {@link ResourceLoaderAware#setResourceLoader},
* {@link ApplicationEventPublisherAware#setApplicationEventPublisher} and
* {@link MessageSourceAware}, if applicable.
* @param applicationContext the ApplicationContext object to be used by this object
* @throws ApplicationContextException in case of context initialization errors
* @throws BeansException if thrown by application context methods
* @see org.springframework.beans.factory.BeanInitializationException
*/
void setApplicationContext(ApplicationContext applicationContext) throws BeansException; }
ApplicationContextAware 源码非常简单,其继承了 Aware 接口,并定义一个 set 方法,参数就是 ApplicationContext 对象,当然,其它系列的 Aware 接口也是类似的定义。其具体使用方式如下:
package com.goldwind.spring; import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeansException;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component; @Data
@Slf4j
@Component
public class AwareTest implements ApplicationContextAware {
/*
* 保存应用上下文
*/
private ApplicationContext applicationContext; @Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
this.applicationContext = applicationContext;
//输出所有BeanDefinition name
for(String name:applicationContext.getBeanDefinitionNames()) {
log.info(name);
}
}
}
在 Spring 启动过程中,会回调 setApplicationContext 方法,并传入 ApplicationContext 对象,之后就可对该对象进行操作。我们获取到ApplicationContext对象,并将所有BeanDefinition名称输出:
其它系列的 Aware 接口也是如此使用。具体的调用时机会在后面详细介绍。
以下是几种常用的 Aware 接口:
- BeanFactoryAware:获取 BeanFactory 对象,它是基础的容器接口。
- BeanNameAware:获取 Bean 的名称。
- EnvironmentAware:获取 Environment 对象,它表示整个的运行时环境,可以设置和获取配置属性。
- ApplicationEventPublisherAware:获取 ApplicationEventPublisher 对象,它是用来发布事件的。
- ResourceLoaderAware:获取 ResourceLoader 对象,它是获取资源的工具。
三、InitializingBean, DisposableBean
3.1、InitializingBean
InitializingBean 是一个可以在 Bean 的生命周期执行自定义操作的接口,凡是实现该接口的 Bean,在初始化阶段都可以执行自定义的操作。
public interface InitializingBean { /**
* Invoked by the containing {@code BeanFactory} after it has set all bean properties
* and satisfied {@link BeanFactoryAware}, {@code ApplicationContextAware} etc.
* <p>This method allows the bean instance to perform validation of its overall
* configuration and final initialization when all bean properties have been set.
* @throws Exception in the event of misconfiguration (such as failure to set an
* essential property) or if initialization fails for any other reason
*/
void afterPropertiesSet() throws Exception; }
从 InitializingBean 源码中可以看出它有一个 afterPropertiesSet 方法,当一个 Bean 实现该接口时,在 Bean 的初始化阶段,会回调 afterPropertiesSet 方法,其初始化阶段具体指 Bean 设置完属性之后。
3.2、DisposableBean
同理,DisposableBean在Bean销毁时执行自定义的操作,必须资源的释放。
public interface DisposableBean { /**
* Invoked by the containing {@code BeanFactory} on destruction of a bean.
* @throws Exception in case of shutdown errors. Exceptions will get logged
* but not rethrown to allow other beans to release their resources as well.
*/
void destroy() throws Exception; }
比如:
package com.goldwind.spring; import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.annotation.Configuration; @Data
@Configuration
@Slf4j
public class BeanTest implements InitializingBean, DisposableBean {
/*
* 构造函数
*/
public BeanTest(){
log.info("New object...");
} @Override
public void destroy() {
log.info("Destroying ...");
} @Override
public void afterPropertiesSet() {
log.info("Initializing ....");
}
}
四、BeanPostProcessor、BeanFactoryPostProcessor
4.1、BeanPostProcessor
BeanPostProcessor 和 InitializingBean 有点类似,也是可以在 Bean 的生命周期执行自定义操作,一般称之为 Bean 的后置处理器,不同的是, BeanPostProcessor 可以在 Bean 初始化前、后执行自定义操作,且针对的目标也不同,InitializingBean 针对的是实现 InitializingBean 接口的 Bean,而 BeanPostProcessor 针对的是所有的 Bean。并且postProcessBeforeInitialization在对象创建之后,afterPropertiesSet之前执行,而postProcessAfterInitialization在afterPropertiesSet之后执行:
public interface BeanPostProcessor { // Bean 初始化前调用
default Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
return bean;
} // Bean 初始化后调用
default Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
return bean;
}
}
所有的 Bean 在初始化前、后都会回调接口中的 postProcessBeforeInitialization 和 postProcessAfterInitialization 方法,入参是当前正在初始化的 Bean 对象和 BeanName。值得注意的是 Spring 内置了非常多的 BeanPostProcessor ,以此来完善自身功能,这部分会在后面文章深入讨论。
我们扩充我们的测试类AwareTest :
package com.goldwind.spring; import lombok.Data;
import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.ApplicationContext;
import org.springframework.context.ApplicationContextAware;
import org.springframework.stereotype.Component; @Data
@Slf4j
@Component
public class AwareTest implements ApplicationContextAware, BeanPostProcessor {
/*
* 保存应用上下文
*/
private ApplicationContext applicationContext; @Override
public void setApplicationContext(ApplicationContext applicationContext) {
this.applicationContext = applicationContext;
//输出所有BeanDefinition name
for(String name:applicationContext.getBeanDefinitionNames()) {
log.info(name);
}
} @Override
public Object postProcessBeforeInitialization(Object bean, String beanName) {
if(beanName.equals("beanTest")) {
log.info("postProcessBeforeInitialization:" + beanName);
}
return bean;
} @Override
public Object postProcessAfterInitialization(Object bean, String beanName) {
if(beanName.equals("beanTest")) {
log.info("postProcessAfterInitialization:" + beanName);
}
return bean;
} }
BeanTest :
package com.goldwind.spring; import lombok.Data;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.BeanNameAware;
import org.springframework.beans.factory.DisposableBean;
import org.springframework.beans.factory.InitializingBean;
import org.springframework.context.annotation.Configuration; @Data
@Configuration
@Slf4j
public class BeanTest implements InitializingBean, DisposableBean, BeanNameAware {
/*
* 保存当前bean name
*/
private String beanName; /*
* 构造函数
*/
public BeanTest(){
log.info("New object...");
} @Override
public void destroy() {
log.info("Destroying ...");
} @Override
public void afterPropertiesSet() {
log.info("Initializing ....");
} @Override
public void setBeanName(String name) {
this.beanName = name;
log.info("Current bean name:" + name);
}
}
可以看到beanTest对象先是被实例化出来,然后执行BeanPostProcessor的postProcessBeforeInitialization,再执行InitializingBean的afterPropertiesSet,最后执行BeanPostProcessor的postProcessAfterInitialization方法。而ApplicationContextAware的setApplicationContext方法执行时所有BeanDefinition都已加载,但还未实例化Bean
BeanPostProcessor 使用场景其实非常多,因为它可以获取正在初始化的 Bean 对象,然后可以对Bean 对象做一些定制化的操作,如:判断该 Bean 是否为某个特定对象、获取 Bean 的注解元数据等。事实上,Spring 内部也正是这样使用的,之前我们介绍的Spring Boot -- Spring AOP原理及简单实现手写AOP时也是利用了BeanPostProcessor的特性,我们对@Pointcut注解指定的Bean都进行了代理处理。
4.2、BeanFactoryPostProcessor
BeanFactoryPostProcessor 是 Bean 工厂的后置处理器,一般用来修改上下文中的 BeanDefinition,修改 Bean 的属性值。
public interface BeanFactoryPostProcessor {
// 入参是一个 Bean 工厂:ConfigurableListableBeanFactory。该方法执行时,所有 BeanDefinition 都已被加载,但还未实例化 Bean。
// 可以对其进行覆盖或添加属性,甚至可以用于初始化 Bean。
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}
BeanFactoryPostProcessor 源码非常简单,其提供了一个 postProcessBeanFactory 方法,当所有的 BeanDefinition 被加载时,该方法会被回调。值得注意的是,Spring 内置了许多 BeanFactoryPostProcessor 的实现,以此来完善自身功能。 这里,我们来实现一个自定义的 BeanFactoryPostProcessor:
package com.goldwind.spring; import lombok.Data;
import lombok.extern.slf4j.Slf4j;import org.springframework.beans.factory.config.BeanDefinition;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;import org.springframework.stereotype.Component; @Data
@Slf4j
@Component
public class AwareTest implements BeanFactoryPostProcessor {
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
log.info("------------------------------------------postProcessBeanFactory-------------------------");
String beanNames[] = beanFactory.getBeanDefinitionNames();
for (String beanName : beanNames) {
BeanDefinition beanDefinition = beanFactory.getBeanDefinition(beanName);
log.info(beanDefinition.toString());
}
}
}
主要是通过 Bean 工厂获取所有的 BeanDefinition 。
可以看到,BeanDefinition 正确输出,里面是一些 Bean 的相关定义,如:是否懒加载、Bean 的 Class 以及 Bean 的属性等。
五、ImportSelector
参考文章:
[1]Spring(七)核心容器 - 钩子接口(转载)
Spring Boot -- 启动流程分析之ApplicationContext 中的更多相关文章
- Spring Boot启动流程分析
引言 早在15年的时候就开始用spring boot进行开发了,然而一直就只是用用,并没有深入去了解spring boot是以什么原理怎样工作的,说来也惭愧.今天让我们从spring boot启动开始 ...
- Spring MVC启动流程分析
本文是Spring MVC系列博客的第一篇,后续会汇总成贴子. Spring MVC是Spring系列框架中使用频率最高的部分.不管是Spring Boot还是传统的Spring项目,只要是Web项目 ...
- Spring Boot 启动原理分析
https://yq.aliyun.com/articles/6056 转 在spring boot里,很吸引人的一个特性是可以直接把应用打包成为一个jar/war,然后这个jar/war是可以直接启 ...
- Spring Boot(三):Spring Boot中的事件的使用 与Spring Boot启动流程(Event 事件 和 Listeners监听器)
前言:在讲述内容之前 希望大家对设计模式有所了解 即使你学会了本片的内容 也不知道什么时候去使用 或者为什么要这样去用 观察者模式: 观察者模式是一种对象行为模式.它定义对象间的一种一对多的依赖关系, ...
- Spring Boot启动流程详解(一)
环境 本文基于Spring Boot版本1.3.3, 使用了spring-boot-starter-web. 配置完成后,编写了代码如下: @SpringBootApplication public ...
- Spring Boot启动流程详解
注:本文转自http://zhaox.github.io/java/2016/03/22/spring-boot-start-flow 环境 本文基于Spring Boot版本1.3.3, 使用了sp ...
- Spring Boot启动流程
基础准备 1,BeanPostProcessor:这个接口的作用在于对于新构造的实例可以做一些自定义的修改.比如如何构造.属性值的修改.构造器的选择等等 2,BeanFactoryPostProces ...
- u-boot启动流程分析(1)_平台相关部分
转自:http://www.wowotech.net/u-boot/boot_flow_1.html 1. 前言 本文将结合u-boot的“board—>machine—>arch—> ...
- Uboot启动流程分析(三)
1.前言 在前面的文章Uboot启动流程分析(二)中,链接如下: https://www.cnblogs.com/Cqlismy/p/12002764.html 已经对_main函数的整个大体调用流程 ...
随机推荐
- Java实现 蓝桥杯 历届试题 连号区间数
问题描述 小明这些天一直在思考这样一个奇怪而有趣的问题: 在1~N的某个全排列中有多少个连号区间呢?这里所说的连号区间的定义是: 如果区间[L, R] 里的所有元素(即此排列的第L个到第R个元素)递增 ...
- Java实现考察团组成
考察团组成 某饭店招待国外考察团.按照标准,对领导是400元/人,随团职员200元/人,对司机50元/人. 考察团共36人,招待费结算为3600元,请问领导.职员.司机各几人. 答案是三个整数,用逗号 ...
- iOS-Core Foundation框架到Foundation桥接的三种方式
温故知新.勤总结,才能生巧!这次总结一下 :Core Foundation框架到Foundation桥接的三种方式 Foundation提供OC的基础类(像NSObject).基本数据类型等. Cor ...
- 上位机开发之西门子PLC-S7通信实践
写在前面: 就目前而言,在中国的工控市场上,西门子仍然占了很大的份额,因此对于上位机开发而言,经常会存在需要与西门子PLC进行通信的情况.然后对于西门子PLC来说,通信方式有很多,下面简单列举一下: ...
- springmvc无法进入controller,且报错404
今天搭建一个springmvc项目时,前台一直报错404,在controller中调试发现程序没有进入controller. 通过多次刷新前台页面,发现第一次进入是会弹出错误提示,第二次之后就直接40 ...
- 阻塞队列一——java中的阻塞队列
目录 阻塞队列简介:介绍阻塞队列的特性与应用场景 java中的阻塞队列:介绍java中实现的供开发者使用的阻塞队列 BlockQueue中方法:介绍阻塞队列的API接口 阻塞队列的实现原理:具体的例子 ...
- EAS:基于网络转换的神经网络结构搜索 | AAAI 2018
论文提出经济实惠且高效的神经网络结构搜索算法EAS,使用RL agent作为meta-controller,学习通过网络变换进行结构空间探索.从指定的网络开始,通过function-preservin ...
- 分享我在前后端分离项目中Gitlab-CI的经验
长话短说,今天分享我为前后端分离项目搭建Gitlab CI/CD流程的一些额外经验. Before Gitlab-ci是Gitlab提供的CI/CD特性,结合Gitlab简单友好的配置界面,能愉悦的在 ...
- 【loj - 3056】 「HNOI2019」多边形
目录 description solution accepted code details description 小 R 与小 W 在玩游戏. 他们有一个边数为 \(n\) 的凸多边形,其顶点沿逆时 ...
- MySQL——事务(Transaction)详解
原文:https://blog.csdn.net/w_linux/article/details/79666086