拉勾教育Java高薪训练营
好处:提高培养代码架构思维、深入理解框架
原则
定焦原则:抓主线
宏观原则:站在上帝视⻆,关注源码结构和业务流程(淡化具体某行代码的编写细节)
读源码的方法和技巧
断点(观察调用栈)
经验(spring框架中doXXX,做具体处理的地方)
IoC容器是Spring的核心模块,是抽象了对象管理、依赖关系管理的框架解决方案。
Spring 提供了很多的容器,其中 BeanFactory 是顶层容器(根容器),不能被实例化,它定义了所有 IoC 容器必须遵从的一套原则
具体的容器实现可以增加额外的功能
ApplicationContext
,其下更具体的实现如 :ClassPathXmlApplicationContext
包含了解析 xml 等一系列的内容,AnnotationConfigApplicationContext
则是包含了注解解析等一系列的内容。Spring IoC 容器继承体系非常聪明,需要使用哪个层次用哪个层次即可,不必使用功能大而全的。
例如我们一贯使用的 ApplicationContext 除了继承BeanFactory的子接口, 还继承了ResourceLoader、MessageSource等接口,因此其提供的功能也就更丰富了。
下面我们以 ClasspathXmlApplicationContext 为例,深入源码说明 IoC 容器的初始化流程。
思路 : 创建一个类 LagouBean ,让其实现几个特殊的接口,并分别在接口实现的构造器、接口方法中断点,观察线程调用栈,分析出 Bean 对象创建和管理关键点的触发时机。
//bean的构造器
public LagouBean(){
System.out.println("LagouBean 构造器...");
}
//InitializingBean接口的实现
public void afterPropertiesSet() throws Exception {
System.out.println("LagouBean afterPropertiesSet...");
}
//BeanPostProcessor接口的postProcessBeforeInitialization和postProcessAfterInitialization实现
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)throws BeansException {
if("lagouBean".equals(beanName)) {
System.out.println("BeanPostProcessor 实现类
postProcessBeforeInitialization 方法被调用中......");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)throws BeansException {
if("lagouBean".equals(beanName)) {
System.out.println("BeanPostProcessor 实现类
postProcessAfterInitialization 方法被调用中......"); }
return bean;
}
}
//BeanFactoryPostProcessor接口的实现方法
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("BeanFactoryPostProcessor的实现方法调用中......");
}
根据下面的调试分析,我们发现 Bean对象创建的几个关键时机点代码层级的调用都在 AbstractApplicationContext 类 的 refresh 方法中,可⻅这个方法对于Spring IoC 容器初始化来说相当关键,汇总如下:
关键点 | 触发代码 |
---|---|
构造器 | refresh#finishBeanFactoryInitialization(beanFactory)(beanFactory) |
BeanFactoryPostProcessor 初始化 | refresh#invokeBeanFactoryPostProcessors(beanFactory) |
BeanFactoryPostProcessor 方法调用 | refresh#invokeBeanFactoryPostProcessors(beanFactory) |
BeanPostProcessor 初始化 | registerBeanPostProcessors(beanFactory) |
BeanPostProcessor 方法调用 | refresh#finishBeanFactoryInitialization(beanFactory) |
1)分析 Bean 的创建是在容器初始化时还是在 getBean 时
@Test
public void testIoC() {
ApplicationContext applicationContext = new ClassPathXmlApplicationContext("classpath:applicationContext.xml");
//在此处打断点,观察调用栈发现applicationContext对象已经持有lagouBean
LagouBean lagouBean = applicationContext.getBean(LagouBean.class);
System.out.println(lagouBean);
}
结论:在未设置延迟加载的前提下,Bean 的创建是在容器初始化过程中完成的。
//bean的构造器
public LagouBean(){
//在构造函数处打上断点
System.out.println("LagouBean 构造器...");
}
结论:发现构造函数的调用时机在AbstractApplicationContext类refresh方法的 finishBeanFactoryInitialization(beanFactory)处;
3)分析 InitializingBean 之 afterPropertiesSet 初始化方法调用情况
//InitializingBean接口的实现
public void afterPropertiesSet() throws Exception {
//在此处打上断点
System.out.println("LagouBean afterPropertiesSet...");
}
结论:发现 InitializingBean中afterPropertiesSet 方法的调用时机也是在 AbstractApplicationContext类refresh方法的finishBeanFactoryInitialization(beanFactory);
//BeanPostProcessor接口的postProcessBeforeInitialization和postProcessAfterInitialization实现
@Override
public Object postProcessBeforeInitialization(Object bean, String beanName)throws BeansException {
if("lagouBean".equals(beanName)) {
System.out.println("BeanPostProcessor 实现类
postProcessBeforeInitialization 方法被调用中......");
}
return bean;
}
@Override
public Object postProcessAfterInitialization(Object bean, String beanName)throws BeansException {
if("lagouBean".equals(beanName)) {
System.out.println("BeanPostProcessor 实现类
postProcessAfterInitialization 方法被调用中......"); }
return bean;
}
}
分别在构造函数、postProcessBeanFactory 方法处打断点,观察调用栈。
结论:
invokeBeanFactoryPostProcessors(beanFactory);
5)分析 BeanPostProcessor 初始化和调用情况
//BeanFactoryPostProcessor接口的实现方法
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
System.out.println("BeanFactoryPostProcessor的实现方法调用中......");
}
分别在构造函数、postProcessBeanFactory 方法处打断点,观察调用栈
结论:
BeanPostProcessor 初始化在AbstractApplicationContext类refresh方法的 registerBeanPostProcessors(beanFactory);
postProcessBeforeInitialization 调用在AbstractApplicationContext类refresh方法的 finishBeanFactoryInitialization(beanFactory);
postProcessAfterInitialization 调用在AbstractApplicationContext类refresh方法的 finishBeanFactoryInitialization(beanFactory);
由上分析可知,Spring IoC 容器初始化的关键环节就在 AbstractApplicationContext#refresh() 方法中 ,我们查看 refresh 方法来俯瞰容器创建的主体流程,主体流程下的具体子流程我们后面再来讨论。
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
// 第一步:刷新前的预处理
prepareRefresh();
// 第二步: 获取BeanFactory;
// 默认实现是DefaultListableBeanFactory
// 加载BeanDefition 并注册到 BeanDefitionRegistry
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 第三步:BeanFactory的预准备工作(BeanFactory进行一些设置,比如context的类加 载器等)
prepareBeanFactory(beanFactory);
try {
// 第四步:BeanFactory准备工作完成后进行的后置处理工作
postProcessBeanFactory(beanFactory);
// 第五步:实例化并调用实现了BeanFactoryPostProcessor接口的Bean
invokeBeanFactoryPostProcessors(beanFactory);
// 第六步:注册BeanPostProcessor(Bean的后置处理器),在创建bean的前后等执
registerBeanPostProcessors(beanFactory);
// 第七步:初始化MessageSource组件(做国际化功能;消息绑定,消息解析);
initMessageSource();
// 第八步:初始化事件派发器
initApplicationEventMulticaster();
// 第九步:子类重写这个方法,在容器刷新的时候可以自定义逻辑
onRefresh();
// 第十步:注册应用的监听器。就是注册实现了ApplicationListener接口的监听器
registerListeners();
// 第十一步:初始化所有剩下的非懒加载的单例bean,初始化创建非懒加载方式的单例Bean实例(未设置属性),填充属性,初始化方法 调用(比如调用afterPropertiesSet方法、init-method方法)调用BeanPostProcessor(后置处理器)对实例bean进行后置处
finishBeanFactoryInitialization(beanFactory);
// 第十二步: 完成context的刷新。主要是调用LifecycleProcessor的onRefresh()方法,并且发布事件。
(ContextRefreshedEvent)finishRefresh();
}
BeanDefinition对象: 我们在 XML 中定义的 bean标签,Spring 解析 bean 标签成为一个 JavaBean,这个JavaBean就是BeanDefinition
(1)该子流程涉及到如下几个关键步骤
Resource定位:指对BeanDefinition的资源定位过程。通俗讲就是找到定义Javabean信息的XML文件,并将其封装成Resource对象。
BeanDefinition载入:把用户定义好的Javabean表示为IoC容器内部的数据结构,这个容器内部的数据结构就是BeanDefinition。
注册BeanDefinition到 IoC 容器
(2)过程分析
Step 1:子流程入口在 AbstractRefreshableApplicationContext#refreshBeanFactory 方法中
Step 2:依次调用多个类的 loadBeanDefinitions 方法 —> AbstractXmlApplicationContext —> AbstractBeanDefinitionReader —> XmlBeanDefinitionReader 一直执行到 XmlBeanDefinitionReader 的 doLoadBeanDefinitions 方法
protected int doLoadBeanDefinitions(InputSource inputSource, Resource resource) throws BeanDefinitionStoreException {
try {
//读取xml信息,将xml中的信息保存到Document对象
Document doc = this.doLoadDocument(inputSource, resource);
//解析document对象,封装BeanDefinition对象并进行注册
int count = this.registerBeanDefinitions(doc, resource);
if (this.logger.isDebugEnabled()) {
this.logger.debug("Loaded " + count + " bean definitions from " + resource);
}
return count;
} catch ...省略
}
Step 3:我们重点观察XmlBeanDefinitionReader 类的 registerBeanDefinitions 方法,期间产生了多次重载调用,我们定位到最后一个。
public int registerBeanDefinitions(Document doc, Resource resource) throws BeanDefinitionStoreException {
BeanDefinitionDocumentReader documentReader = this.createBeanDefinitionDocumentReader();
//获取已有BeanDefinition的数量
int countBefore = this.getRegistry().getBeanDefinitionCount();
//注册BeanDefinition
documentReader.registerBeanDefinitions(doc, this.createReaderContext(resource));
//返回新注册的BeanDefinition数量
return this.getRegistry().getBeanDefinitionCount() - countBefore;
}
此处我们关注注册BeanDefinition中的两个地方:
一个createRederContext方法,一个是 DefaultBeanDefinitionDocumentReader类的registerBeanDefinitions方法。
先进入 createRederContext 方法看看,此处 Spring 首先完成了 NamespaceHandlerResolver 的初始化。
我们再进入 registerBeanDefinitions 方法中追踪,调用了DefaultBeanDefinitionDocumentReader#registerBeanDefinitions 方法
public void registerBeanDefinitions(Document doc, XmlReaderContext readerContext) {
this.readerContext = readerContext;
this.doRegisterBeanDefinitions(doc.getDocumentElement());
}
//进入doRegisterBeanDefinitions
protected void doRegisterBeanDefinitions(Element root) {
...省略
//重点是parseBeanDefinitions,preProcessXml和postProcessXml为钩子方法
this.preProcessXml(root);
this.parseBeanDefinitions(root, this.delegate);
this.postProcessXml(root);
this.delegate = parent;
}
//进入 parseBeanDefinitions 方法
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) {
if (delegate.isDefaultNamespace(root)) {
NodeList nl = root.getChildNodes();
for(int i = 0; i < nl.getLength(); ++i) {
Node node = nl.item(i);
if (node instanceof Element) {
Element ele = (Element)node;
if (delegate.isDefaultNamespace(ele)) {
//解析bean元素
this.parseDefaultElement(ele, delegate);
} else {
delegate.parseCustomElement(ele);
}
}
}
} else {
delegate.parseCustomElement(root);
}
}
//进入 parseDefaultElement 方法
private void parseDefaultElement(Element ele, BeanDefinitionParserDelegate delegate) {
//import元素解析
if (delegate.nodeNameEquals(ele, "import")) {
this.importBeanDefinitionResource(ele);
//alias元素解析
} else if (delegate.nodeNameEquals(ele, "alias")) {
this.processAliasRegistration(ele);
//bean元素解析
} else if (delegate.nodeNameEquals(ele, "bean")) {
this.processBeanDefinition(ele, delegate);
//beans元素解析
} else if (delegate.nodeNameEquals(ele, "beans")) {
this.doRegisterBeanDefinitions(ele);
}
}
//进入 processBeanDefinition 方法查看bean元素解析
protected void processBeanDefinition(Element ele, BeanDefinitionParserDelegate delegate) {
//解析bean元素为BeanDefinition,但是此时使用BeanDefinitionHolder又包装为BeanDefinitionHolder对象
BeanDefinitionHolder bdHolder = delegate.parseBeanDefinitionElement(ele);
if (bdHolder != null) {
//如果有自定义标签,则处理自定义标签
bdHolder = delegate.decorateBeanDefinitionIfRequired(ele, bdHolder);
try {
//完成BeanDefinition的注册
BeanDefinitionReaderUtils.registerBeanDefinition(bdHolder, this.getReaderContext().getRegistry());
}
..省略
}
}
至此,注册流程结束,我们发现,所谓的注册就是把封装的 XML 中定义的 Bean信息封装为 BeanDefinition 对象之后放入一个Map中,BeanFactory 是以 Map 的结构组织这些 BeanDefinition 的。
可以在DefaultListableBeanFactory中看到此Map的定义。
/** Map of bean definition objects, keyed by bean name. */
private final Map<String, BeanDefinition> beanDefinitionMap = new ConcurrentHashMap<>(256);
(3) 时序图如下
通过最开始的关键时机点分析,我们知道Bean创建子流程入口在
AbstractApplicationContext#refresh()方法的finishBeanFactoryInitialization(beanFactory) 处
AbstractApplicationContext#refresh()方法 详细方法内容可看上面
// 第十一步:初始化所有剩下的非懒加载的单例bean,初始化创建非懒加载方式的单例Bean实例(未设置属性),填充属性,初始化方法 调用(比如调用afterPropertiesSet方法、init-method方法)调用BeanPostProcessor(后置处理器)对实例bean进行后置处
finishBeanFactoryInitialization(beanFactory);
protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
...省略
beanFactory.setTempClassLoader((ClassLoader)null);
beanFactory.freezeConfiguration();
//实例化所有立即加载的单例bean
beanFactory.preInstantiateSingletons();
}
public void preInstantiateSingletons() throws BeansException {
...省略
if (isEagerInit) {
//实例化当前Bean
this.getBean(beanName);
}
}
//创建单例bean
if (mbd.isSingleton()) {
sharedInstance = this.getSingleton(beanName, () -> {
try {
//创建bean
return this.createBean(beanName, mbd, args);
} catch (BeansException var5) {
this.destroySingleton(beanName);
throw var5;
}
});
bean = this.getObjectForBeanInstance(sharedInstance, name, beanName, mbd);
}
beanInstance = this.doCreateBean(beanName, mbdToUse, args);
if (this.logger.isTraceEnabled()) {
this.logger.trace("Finished creating instance of bean '" + beanName + "'");
}
return beanInstance;
进入doCreateBean方法看看,该方法我们关注两块重点区域
if (instanceWrapper == null) {
instanceWrapper = this.createBeanInstance(beanName, mbd, args);
}
Object exposedObject = bean;
try {
this.populateBean(beanName, mbd, instanceWrapper);
exposedObject = this.initializeBean(beanName, exposedObject, mbd);
}
普通 Bean 的初始化是在容器启动初始化阶段执行的,而被lazy-init=true修饰的 bean 则是在从容器里第一次进行context.getBean() 时进行触发。
Spring 启动的时候会把所有bean信息(包括XML和注解)解析转化成Spring能够识别的BeanDefinition并存到Hashmap里供下面的初始化时用,然后对每个 BeanDefinition 进行处理,如果是懒加载的则在容器初始化阶段不处理,其他的则在容器初始化阶段进 行初始化并依赖注入。
public void preInstantiateSingletons() throws BeansException {
// 所有beanDefinition集合
List<String> beanNames = new ArrayList<String>(this.beanDefinitionNames);
// 触发所有非懒加载单例bean的初始化
for (String beanName : beanNames) {
// 获取bean 定义
RootBeanDefinition bd = getMergedLocalBeanDefinition(beanName);
// 判断是否是懒加载单例bean,如果是单例的并且不是懒加载的则在容器创建时初始化
if (!bd.isAbstract() && bd.isSingleton() && !bd.isLazyInit()) {
// 判断是否是 FactoryBean
if (isFactoryBean(beanName)) {
final FactoryBean<?> factory = (FactoryBean<?>) getBean(FACTORY_BEAN_PREFIX + beanName);
boolean isEagerInit;
if (System.getSecurityManager() != null && factory instanceof SmartFactoryBean) {
isEagerInit = AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
@Override
public Boolean run() {
return ((SmartFactoryBean<?>) factory).isEagerInit();
}
}, getAccessControlContext());
//如果是普通bean则进行初始化并依赖注入,此 getBean(beanName)接下来触发的逻辑懒加载时 context.getBean("beanName") 所触发的逻辑是一样的
getBean(beanName);