1.前言
2.spring源码外围办法refresh()介绍
3.总结
1.前言
github源码地址(带正文):
https://github.com/su15967456…
在进入明天的spring外围办法介绍之前,咱们先来温习一下上次咱们学习的内容,Spring源码剖析——spring容器总览,如图所示:
大抵流程为:
1.IOC容器从xml,注解等中央获取类的信息。
2.将类的信息封装成BeanDefinition
3.beanFactoryPostProcessor前置解决
4.ioc容器获取类信息通过反射进行实例化
5.执行实例化
6.执行初始化逻辑(填充属性(populateBean)->执行aware接口办法->beanPostProcessor:before->init-method->beanPostProcessor:after)
以上,咱们只是将spring容器的执行流程大体介绍了一下,接下来,咱们要进入源码中,大抵(因为咱们当初是第一回看源码,心愿不要被卷入一细节,而是大抵走一遍就能够了,卷入细节的旋涡很容易劝退。)理解一下spring启动的程序代码。
spring源码中,最重要的外围办法,就是refresh()了,只有看懂了refresh()中的外围的13个办法,差不多就看懂spring了!
咱们在开始看源码前,能够看着执行过程图猜想一下,第一步会执行什么?
个别咱们都会感觉是读取配置文件
然而其实是————要把beanFactory创立好。因为只有把组件初始化结束之后,咱们才会有工具类去读配置文件,去实例化等操作。
2.spring源码外围办法refresh()介绍
2.1 debug的前置工作
在开始看spring源码之前,咱们先筹备一下前置工作。
咱们先写两个一般的类A和B(为了体现出实例化和循环依赖的成果,循环依赖咱们前期会讲),
类A:
public class A {
private B b;
public void init(){
System.out.println("执行init()");
}
public void destroy(){
System.out.println("执行destroy()");
}
public A(B b) {
this.b = b;
}
public A() {
System.out.println("A 创立了");
}
public B getB() {
return b;
}
public void setB(B b) {
this.b = b;
}
}
类B:
public class B {
private A a;
public B() {
System.out.println("B 创立了");
}
public A getA() {
return a;
}
public void setA(A a) {
this.a = a;
}
}
再写一个配置文件:
applicationContext.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://www.springframework.org/schema/beans"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="a" class="slf.A" >
<property name="b" ref="b"/>
</bean>
<bean id="b" class="slf.B">
<property name="a" ref="a"/>
</bean>
</beans>
最初写一个启动demo类,就算功败垂成了!
public static void main(String[] args) throws Exception {
//启动容器
ClassPathXmlApplicationContext classPathXmlApplicationContext = new ClassPathXmlApplicationContext("applicationContext.xml");
A a = (A) classPathXmlApplicationContext.getBean(A.class);
B b = classPathXmlApplicationContext.getBean(B.class);
}
咱们能够在启动容器那一行下面打上断点,而后debug启动!
读spring源码,如何debug很重要,这里我放上一张截图,咱们能够依据须要抉择哪个性能。
2.2spring源码外围办法refresh()介绍
好的,那咱们先进入这个办法,点击下面那张图的第二个箭头:
能够看出咱们进入了一个办法,然而不是ClassPathXmlApplicationContext的构造方法。
这里咱们能够不必理睬,点击步出按钮,再点步入按钮。
就会进入到咱们所心愿看到的办法了:
然而当初还看不出什么端倪,所以咱们能够持续步入:
好了,终于看到了咱们的外围refresh()办法了。
public ClassPathXmlApplicationContext(
String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
throws BeansException {
//调用父类构造方法,进行相干的对象创立操作
super(parent);
//设置配置文件门路,放入一个对象中,不便后续的读取和应用
setConfigLocations(configLocations);
if (refresh) {
refresh();
}
}
咱们能够看出,除了refresh()办法,还有一些其它的办法,咱们大抵地介绍一下:
2.2.1 super()办法介绍
super()次要用来调用父类构造方法,进行一些属性的对象创立,前期应用。
往super()外面点,点到最外面,咱们发现它调用了两个办法:
/**
* Create a new AbstractApplicationContext with the given parent context.
* @param parent the parent context
*/
public AbstractApplicationContext(@Nullable ApplicationContext parent) {
this();
setParent(parent);
}
首先咱们看this()办法:
/**
* Create a new AbstractApplicationContext with no parent.
*/
public AbstractApplicationContext() {
//加载资源模式解析器 用来加载咱们零碎运行的时候须要的资源(xml)
this.resourcePatternResolver = getResourcePatternResolver();
}
通过getResourcePatternResolver办法的正文咱们能够看出,它创立了一个资源模式解析器,是前面用来加xml等载资源用的。上文提到过,咱们容器的初始化后面的阶段次要就是要将容器,组件进行实例化。
2.2.2 setParent(parent)办法介绍
接下来看咱们的setParent(parent)办法,往里面点咱们能够看出,如果父类环境属性不等于空,那么久把父类容器的环境和子类容器合并。
public void setParent(@Nullable ApplicationContext parent) {
this.parent = parent;
if (parent != null) {
Environment parentEnvironment = parent.getEnvironment();
if (parentEnvironment instanceof ConfigurableEnvironment) {
getEnvironment().merge((ConfigurableEnvironment) parentEnvironment);
}
}
}
咱们可能搞不清楚父子容器的概念,因为目前spring只有一个容器,能够临时先略过。
2.2.3 setConfigLocations(configLocations)办法介绍
这个办法比较简单,就是将传进来的 applicationContext.xml 这个字符串保留一下,不便后续读取xml,能够间接从数组中获取。
2.2.4 refresh()办法介绍
好了,接下来终于要到咱们的refresh()办法了,咱们间接进入这个办法。
@Override
public void refresh() throws BeansException, IllegalStateException {
synchronized (this.startupShutdownMonitor) {
StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh");
// Prepare this context for refreshing.
//容器刷新前的筹备工作
/*
1.设置容器的启动工夫
2.设置沉闷状态为true
3.设置敞开状态为false
4.获取Environment对象,并加载以后零碎的属性到Environment中
5.筹备监听器和事件的汇合对象,默认为空的对象汇合
*/
prepareRefresh();
// Tell the subclass to refresh the internal bean factory.
//创立容器对象,DefaultListableBeanFactory
//加载xml配置文件的属性到以后工厂中,最重要的就是BeanDefinition
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// Prepare the bean factory for use in this context.
//筹备bean工厂,实现bean工厂属性的一些初始化操作
prepareBeanFactory(beanFactory);
try {
// Allows post-processing of the bean factory in context subclasses.
//空办法
//子类笼罩做额定的解决,此处咱们个别不做任何扩大工作,这个办法具体能够做什么,能够进去看正文,springmvc/springboot里有具体的扩大实现
postProcessBeanFactory(beanFactory);
StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process");
// Invoke factory processors registered as beans in the context.
//执行beanFactoryPostProcessors,调用各种beanFactory处理器,比方替换工作 ${username}
invokeBeanFactoryPostProcessors(beanFactory);
// Register bean processors that intercept bean creation.
//注册BeanPostProcessors
registerBeanPostProcessors(beanFactory);
beanPostProcess.end();
// 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();
contextRefresh.end();
}
}
}
能够看出,一共调用了13个办法,这13个办法就是咱们要了解的要害,我曾经相应地输入了一些注解,接下来咱们挨个进行解释:
2.2.4.1 prepareRefresh()办法介绍
protected void prepareRefresh() {
// Switch to active.
//创立一下开启工夫
this.startupDate = System.currentTimeMillis();
//敞开状态设置为false
this.closed.set(false);
//激活状态设置为true
this.active.set(true);
if (logger.isDebugEnabled()) {
if (logger.isTraceEnabled()) {
logger.trace("Refreshing " + this);
} else {
logger.debug("Refreshing " + getDisplayName());
}
}
// Initialize any placeholder property sources in the context environment.
//留给子类笼罩,初始化属性资源
initPropertySources();
// Validate that all properties marked as required are resolvable:
// see ConfigurablePropertyResolver#setRequiredProperties
//获取环境对象并验证,验证所须要的属性文件是否曾经放入环境中
getEnvironment().validateRequiredProperties();
// Store pre-refresh ApplicationListeners...
//创立一些根本的监听器
if (this.earlyApplicationListeners == null) {
this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
} else {
// Reset local application listeners to pre-refresh state.
this.applicationListeners.clear();
this.applicationListeners.addAll(this.earlyApplicationListeners);
}
// Allow for the collection of early ApplicationEvents,
// to be published once the multicaster is available...
//筹备监听器事件的汇合
this.earlyApplicationEvents = new LinkedHashSet<>();
}
咱们能够看出prepareRefresh()办法次要还是做了一些筹备工作,比方:
1.创立一下开启的工夫
2.设置敞开状态
3.设置激活状态
4.打印日志
5.获取环境对象并验证,验证所须要的属性文件是否曾经放入环境中
6.创立一些根本的监听器,筹备监听器事件的汇合(监听器在前面的公布->订阅模式中有用到,这里只是初始化一下类。)
咱们能够看出,这个办法,次要还是做一些初始化上下文的工作,也就是刷新前的筹备工作!
2.2.4.2 obtainFreshBeanFactory()办法介绍
// Tell the subclass to refresh the internal bean factory.
//创立容器对象,DefaultListableBeanFactory
//加载xml配置文件的属性到以后工厂中,最重要的就是BeanDefinition
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
这个办法就比拟重要了,大略做了两步操作:
1.创立一个容器对象(DefaultListableBeanFactory)
2.读取xml的bean信息封装成beanDefinition的操作。
咱们能够点进去看一下:
protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
//初始化BeanFactory,并进行xml文件读取,并将失去的BeanFactory记录在以后实体的属性中
refreshBeanFactory();
return getBeanFactory();//返回以后实体的beanFactory属性
}
咱们先对容器进行初始化操作(因为容器到当初还没进行过初始化呢),而后再读取xml中的bean并封装成BeanDefinition。
debug 步入 refreshBeanFactory();
@Override
protected final void refreshBeanFactory() throws BeansException {
//如果有bean工厂了,先销毁掉
if (hasBeanFactory()) {
destroyBeans();
closeBeanFactory();
}
try {
//创立DefaultListableBeanFactory对象
DefaultListableBeanFactory beanFactory = createBeanFactory();
//每个容器都有本人的id,为了序列化指定id,能够从id反序列化到beanFactory对象
beanFactory.setSerializationId(getId());
//定制beanFactory,设置相干属性,包含是否容许笼罩同名称的不同定义的对象以及循环依赖,能够通过子类重写
customizeBeanFactory(beanFactory);
//初始化documentReader,并进行对xml文件进行解析
loadBeanDefinitions(beanFactory);
this.beanFactory = beanFactory;
}
catch (IOException ex) {
throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
}
}
1.如果有了容器的话,先销毁容器。
2.创立一个DefaultListableBeanFactory对象
3.接着进行相干属性设置
4.最重要的就是对xml文件进行解析的loadBeanDefinitions办法,把xml文件进行解析读取bean并封装。(解析过程比较复杂,所以这次先不点进去看了)
2.2.4.3 prepareBeanFactory()办法介绍
接下来是对bean工厂的属性进行一些初始化操作:
// Prepare the bean factory for use in this context.
//筹备bean工厂,实现bean工厂属性的一些初始化操作
prepareBeanFactory(beanFactory);
咱们能够点击进去看看:
protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
// Tell the internal bean factory to use the context's class loader etc.
//设置beanFactory的类加载器为以后context的类加载器
beanFactory.setBeanClassLoader(getClassLoader());
if (!shouldIgnoreSpel) {
//设置beanfactory的表达式语言处理器 解决类->解析类->配置类
beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
}
//为beanFactory减少一个默认的PropertyEditor,这个次要是对bean的属性设置治理的一个工具类 属性编辑类,比方date的format
beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
// Configure the bean factory with context callbacks.
//增加beanPostProcessor 此类用来实现某些aware对象的注入
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
//设置疏忽主动拆卸的接口,这些接口的实现是容器是须要 beanPostProcessor 注入的
//所以在应用@Autowired进行注入的时候须要对这些接口进行疏忽
beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
beanFactory.ignoreDependencyInterface(ApplicationStartupAware.class);
// BeanFactory interface not registered as resolvable type in a plain factory.
// MessageSource registered (and found for autowiring) as a bean.
//设置几个主动拆卸的非凡规定,当在进行IOC的时候,如果有多个实现,就按指定的对象注入
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
// Register early post-processor for detecting inner beans as ApplicationListeners.
beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
// Detect a LoadTimeWeaver and prepare for weaving, if found.
//减少aspectJ的反对,在java中织入有三种形式,别离为编译器织入,类加载器织入,运行时织入
//编译器织入指通过非凡的编译器,将切面织入到java中
//类加载器织入指通过非凡的类加载器,在类字节码加载入JVM的时候,织入切面,相似cglib
//aspectj采纳了两种织入形式,非凡编译器和类加载器,类加载器就是上面的load_time_wave
if (!NativeDetector.inNativeImage() && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
// Set a temporary ClassLoader for type matching.
beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
}
// Register default environment beans.
// 注册默认的零碎环境bean到一级缓存中
if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
}
if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
}
if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
}
if (!beanFactory.containsLocalBean(APPLICATION_STARTUP_BEAN_NAME)) {
beanFactory.registerSingleton(APPLICATION_STARTUP_BEAN_NAME, getApplicationStartup());
}
}
这个看不懂没关系,就是对beanFactory的一些属性值进行了一些操作。咱们后续会进行解说。
2.2.4.4 postProcessBeanFactory()办法介绍
//空办法
//子类笼罩做额定的解决,此处咱们个别不做任何扩大工作,这个办法具体能够做什么,能够进去看正文,springmvc/springboot里有具体的扩大实现
postProcessBeanFactory(beanFactory);
2.2.4.5 invokeBeanFactoryPostProcessors()办法介绍
// Invoke factory processors registered as beans in the context.
//执行beanFactoryPostProcessors,调用各种beanFactory处理器,比方替换工作 ${username}
invokeBeanFactoryPostProcessors(beanFactory);
这个办法次要用来执行BeanFactoryPostProcessor对BeanDefinition进行解决。
咱们当前调用的时候再来解说。
2.2.4.6 registerBeanPostProcessors()办法介绍
//注册BeanPostProcessors
registerBeanPostProcessors(beanFactory);
这个步骤初始化了一下所有的beanPostProcessor,不便后续初始化的时候进行调用。
2.2.4.7 beanPostProcess.end()办法介绍
空办法,模板办法,在容器刷新的时候能够自定义逻辑,不同的Spring容器做不同的事件。
2.2.4.8 initMessageSource()办法介绍
// Initialize message source for this context.
//初始化音讯资源 国际化设置
initMessageSource();
为上下文初始化 Message 源,即对不同语言的音讯体进行国际化解决。
2.2.4.9 initApplicationEventMulticaster()办法介绍
// Initialize event multicaster for this context.
//初始化利用的多播器
initApplicationEventMulticaster();
初始化事件播送器,并放入 applicationEventMulticaster bean 中,用于前面的公布,订阅模式事件播送。
2.2.4.10 onRefresh()办法介绍
// Initialize other special beans in specific context subclasses.
//模板办法,在容器刷新的时候能够自定义逻辑,不同的Spring容器做不同的事件。
onRefresh();
2.2.4.11 registerListeners()办法介绍
// Check for listener beans and register them.
//注册监听器到多播器外面
registerListeners();
2.2.4.12 finishBeanFactoryInitialization()办法介绍
// Instantiate all remaining (non-lazy-init) singletons.
//实例化非懒加载的实例对象
finishBeanFactoryInitialization(beanFactory);
这个办法就是refresh()的外围办法,创立bean,解决循环依赖,AOP都在这外面。也是当前要重点解说的办法。
2.2.4.13 finishRefresh()办法介绍
// Last step: publish corresponding event.
//事件公布
finishRefresh();
后面的事件实现了,启动公布订阅模式,告诉另外一些类实现一些事件。
3.总结
明天咱们介绍了refresh()的外围十三个办法,十分重要,咱们能够debug跟一遍,而后打上正文。
执行的流程如下:
1.容器刷新前的筹备工作
2.创立bean工厂
3.加载xml配置文件到bean工厂中
4.对bean工厂进行赋值
5.执行beanFactoryPostProcessors
6.注册BeanPostProcessors
7.初始化音讯资源 国际化设置
8.初始化利用的多播器
9.注册监听器到多播器外面
10.实例化非懒加载的实例对象
11.事件公布
发表回复