一步一步解读 refresh源码
Posted 流楚丶格念
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了一步一步解读 refresh源码相关的知识,希望对你有一定的参考价值。
解读refresh
- refresh()
- 1. prepareRefresh
- 2. obtainFreshBeanFactory
- 3. prepareBeanFactory
- 4. postProcessBeanFactory
- 5. invokeBeanFactoryPostProcessors(beanFactory);
- 6. registerBeanPostProcessors(beanFactory);
- 7. initMessageSource();
- 8. initApplicationEventMulticaster();
- 9. onRefresh();
- 10. registerListeners();
- 11. finishBeanFactoryInitialization(beanFactory);
- 12. finishRefresh();
refresh()
refresh()方法代码如下:
@Override
public void refresh() throws BeansException, IllegalStateException
synchronized (this.startupShutdownMonitor)
// 准备此上下文以进行刷新
prepareRefresh();
// 告诉子类刷新内部 bean 工厂
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// 准备在此上下文中使用的 bean 工厂
prepareBeanFactory(beanFactory);
try
// 允许在上下文子类中对 bean 工厂进行后处理
postProcessBeanFactory(beanFactory);
// 调用在上下文中注册为 bean 的工厂处理器
invokeBeanFactoryPostProcessors(beanFactory);
// 注册 拦截 bean创建的 bean 处理器
registerBeanPostProcessors(beanFactory);
// 初始化此上下文的消息源
initMessageSource();
// 为此上下文初始化事件多播器
initApplicationEventMulticaster();
// 初始化特定上下文子类中的其他特殊bean
onRefresh();
// 检查监听器 bean 并注册它们
registerListeners();
// 实例化所有剩余的(非惰性初始化)单例。
finishBeanFactoryInitialization(beanFactory);
// 最后一步:发布相应的事件
finishRefresh();
catch (BeansException ex)
if (logger.isWarnEnabled())
logger.warn("Exception encountered during context initialization - " +
"cancelling refresh attempt: " + ex);
// 销毁已经创建的单例以避免悬空资源
destroyBeans();
// 重置“活动”标志。
cancelRefresh(ex);
// 将异常传播给调用者
throw ex;
finally
// 重置 Spring 核心中的公共自省缓存,因为我们可能不再需要单例 bean 的元数据...re...
resetCommonCaches();
首先是对上下文进行刷新:
// 准备此上下文以进行刷新
prepareRefresh();
1. prepareRefresh
创建和准备了 Environment 对象:
- Environment 的作用之一是为后续 @Value,值注入时提供键值
流程如下图所示:
prepareRefresh重点是准备了初始环境,我们跟进prepareRefresh源码实现:
可以看到获取环境配置的代码,继续跟进
里面创建了一个标准的环境:
我们在prepareRefresh();这打个断点也能看到里面的environment:
我们将第一步走完,可以看到environment被初始化成了StandardEnvironment
我们执行一个表达式测试一下:
比如说我们测试一下有没有JAVA_HOME的键值信息,我们发现是可以执行查询到的,所以说此时,一些系统的环境都已经加载到Spring中了:
我们将 展开可以看到JAVA环境变量和系统环境变量:
两个环境变量的内容如下:
我们也可以通过下面代码获取这两个环境变量
// 获取系统环境变量
System.getenv().keySet().stream().forEach(s -> System.out.println(s));
// java环境变量
System.getProperties().keySet().stream().forEach(s -> System.out.println(s));
同样,我们也可以使用StandardEnvironment获取这两个环境变量
StandardEnvironment env = new StandardEnvironment();
env.getSystemEnvironment().keySet().stream().forEach(s -> System.out.println(s));
env.getSystemProperties().keySet().stream().forEach(s -> System.out.println(s));
当然也可以加一个环境变量:
// 加一个环境变量
env.getPropertySources().addLast(new ResourcePropertySource("my",new ClassPathResource("1.properties")));
2. obtainFreshBeanFactory
获取(或创建)BeanFactory 来刷新 内部 bean 工厂:
- BeanFactory 的作用是负责 bean 的创建、依赖注入和初始化
- BeanDefinition 作为 bean 的设计蓝图,规定了 bean 的特征,如单例多例、依赖关系、初始销毁方法等
- BeanDefinition 的来源有多种多样,可以是通过 xml 获得、通过配置类获得、通过组件扫描获得,也可以是编程添加
ApplicationContext没有Bean初始化,依赖注入等功能他都没有,他得简介的调用BeanFactory 来对Bean进行管理,ApplicationContext委托给BeanFactory 来干这些活,ApplicationContext做的是扩展的活,Bean的核心功能是在BeanFactory 中。
执行流程如下:
我们继续跟进源码:
/**
* 告诉子类刷新内部 bean 工厂。
* @return 新的 BeanFactory 实例
* @see #refreshBeanFactory()
* @see #getBeanFactory()
*/
protected ConfigurableListableBeanFactory obtainFreshBeanFactory()
refreshBeanFactory();
return getBeanFactory();
里面调用了接口中的两个方法:
/**
* 子类必须实现此方法才能执行实际的配置加载。
* 该方法在任何其他初始化工作之前由 @link #refresh() 调用。
* <p>子类要么创建一个新的bean工厂并持有对它的引用,
* 或返回它拥有的单个 BeanFactory 实例。在后一种情况下,它将
* 如果多次刷新上下文,通常会抛出 IllegalStateException。
* @throws BeansException 如果 bean 工厂的初始化失败
* @throws IllegalStateException 如果已经初始化并且多次刷新
* 不支持尝试
*/
protected abstract void refreshBeanFactory() throw BeansException,IllegalStateException;
/**
* 子类必须在此处返回其内部 bean 工厂。他们应该实施
* 高效查找,因此可以重复调用而不会降低性能。
* <p>注意:子类之前应该检查上下文是否仍然处于活动状态
* 返回内部 bean 工厂。内部工厂一般应该是
* 一旦上下文关闭,则认为不可用。
* @return 此应用程序上下文的内部 bean 工厂(从不 @code null)
* @throws IllegalStateException 如果上下文还没有包含内部 bean 工厂
* (通常如果 @link #refresh() 从未被调用过)或者如果上下文已经
* 已经关闭
* @see #refreshBeanFactory()
* @see #closeBeanFactory()
*/
@Override
公共抽象 ConfigurableListableBeanFactory getBeanFactory() throw IllegalStateException;
我们在这加个断点看看具体干了什么:
断点出我们执行下一步,看一下refreshBeanFactory的实现:
首先是一个Boolean,这个refreshed变量是用来判断有没有调用过refresh
给BeanFactory实现一个序列化id,这个就对于解读没啥大用了:
下面就是getBeanFactory方法:
这里直接返回了beanFactory
这是个啥?他之前创建过吗?怎么直接返回了?
这个其实在我们创建上下文时候就已经创建了,我们可以在GenericApplicationContext.java中找到答案:
在调用构造的时候就已经初始化beanFactory了
注意这里的ClassPathXmlApplicationContext上下文是调用的另一个refreshBeanFactory的实现
@Override protected final void refreshBeanFactory() throws BeansException if (hasBeanFactory()) destroyBeans(); closeBeanFactory(); try DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory); loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) this.beanFactory = beanFactory; catch (IOException ex) throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
会用新的beanFactory覆盖旧的beanFactory
这里面实现获取BeanFactory 的方法是loadBeanDefinitions(beanFactory) ,下面来测试一下:
我们在xml中配置一个bean:
执行后可以看到加载了我们xml文件中的Bean,bean的所有信息都在value里
BeanDefinition测试(读取Bean方式)
1.xml方式读取
public class BeanDefinitionTest
public static void main(String[] args)
DefaultListableBeanFactory beanFactory = new DefaultListableBeanFactory();
// 1.xml方式读取
XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(beanFactory);
reader.loadBeanDefinitions("classpath:app.xml");
System.out.println(reader.getBeanFactory());
得到从app.xml获取的bean
把这个常用的加到监控变量中:
2.组件扫描
加一个Bean3 添加 @Component
编写下面代码:
// 2.组件扫描
ClassPathBeanDefinitionScanner scanner = new ClassPathBeanDefinitionScanner(beanFactory);
scanner.scan("com.yyl.refresh");
执行可以看到Bean3加到了BeanFactory中:
(其他的是一些bean处理器的)
4.编程方式(Bean构建器)
// 4.编程方式添加
beanFactory.registerBeanDefinition("Bean4", BeanDefinitionBuilder.genericBeanDefinition(Bean4.class).getBeanDefinition());
编程方式比较可控,比如我们设置为多例:
// 4.编程方式添加
beanFactory.registerBeanDefinition("Bean4", BeanDefinitionBuilder
.genericBeanDefinition(Bean4.class)
.setScope("prototype")
.getBeanDefinition());
// 再打印一下
System.out.println(beanFactory.getBean("Bean4"));
System.out.println(beanFactory.getBean("Bean4"));
System.out.println(beanFactory.getBean("Bean4"));
结果如下,生成的是三个不同的对象:
再比如我们加一个初始化函数:
public class Bean4
public void initMethod()
System.out.println("Bean init!!!");
// 4.编程方式添加
beanFactory.registerBeanDefinition("Bean4", BeanDefinitionBuilder
.genericBeanDefinition(Bean4.class)
.setScope("prototype")
.setInitMethodName("initMethod")
.getBeanDefinition());
System.out.println(beanFactory.getBean("Bean4"));
System.out.println(beanFactory.getBean("Bean4"));
System.out.println(beanFactory.getBean("Bean4"));
结果如下:
3. prepareBeanFactory
准备在此上下文中使用的 bean 工厂,完善BeanFactory,把一些重要的成员变量初始化:
- StandardBeanExpressionResolver 来解析 SpEL
- ResourceEditorRegistrar 会注册类型转换器,并应用 ApplicationContext 提供的 Environment 完成 $ 解析
- 特殊 bean 指 beanFactory 以及 ApplicationContext,通过 registerResolvableDependency 来注册它们
- ApplicationContextAwareProcessor 用来解析 Aware 接口
- ApplicationListenerDetector 用来识别容器中 ApplicationListener 类型的 bean
执行流程如下:
(右侧绿色表示初始化的)
下面我们追踪一下源码:
都是完善BeanFactory参数:
首先是要找到El解析器里的addPropertyEditorRegistrar:注册一组类型转化器
右键我们执行个语句测试一下这个注册器是干嘛滴:
执行找到我们Bean4的语句
beanFactory.getBeanExpressionResolver().evaluate("#@Bean4",new BeanExpressionContext(beanFactory,null))
发现是能查到我们刚注册的结果的:
可以,能解析EL表达式了
下面我们深入探究下里面的ResourceEditorRegistrar资源类型转化器,我们可以看到不同的资源类型用不同转化器转化
添加Bean后处理器:扩展功能
// Configure the bean factory with context callbacks.
beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
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 interface not registered as resolvable type in a plain factory.
// MessageSource registered (and found for autowiring) as a bean.
beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
beanFactory.registerResolvableDependency(ResourceLoader.class, this);
beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
beanFactory.registerResolvableDependency(ApplicationContext.class, this);
4. postProcessBeanFactory
这一步是空实现,留给子类扩展,做补充
- 一般 Web 环境的 ApplicationContext 都要利用它注册新的 Scope,完善 Web 下的 BeanFactory
- 体现的是模板方法设计模式
直接看源码:
首先我们可以看到确实是空实现:
/**
* 按照标准修改应用上下文的内部bean工厂
* 初始化。所有 bean 定义都将被加载,但没有 bean
* 将被实例化。这允许注册特殊的
* 某些 ApplicationContext 实现中的 BeanPostProcessors 等。
* @param beanFactory 应用上下文使用的bean工厂
*/
protected postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)
来吧看看子类对他补充了啥吧,这里我们主要看一个web里对这个方法的实现:
没有的先要加一个依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
5. invokeBeanFactoryPostProcessors(beanFactory);
调用在上下文中注册的 BeanFactory 的后处理器:进一步完善BeanFactory
- beanFactory 后处理器,充当 beanFactory 的扩展点,可以用来补充或修改 BeanDefinition
- ConfigurationClassPostProcessor – 解析 @Configuration、@Bean、@Import、@PropertySource 等
- PropertySourcesPlaceHolderConfigurer – 替换 BeanDefinition 中的 $
- MapperScannerConfigurer – 补充 Mapper 接口对应的 BeanDefinition
执行流程如下:
我们看一下源码:
首先他调用了配置后处理器的方法,具体实现太多,里面主要配置两个后处理器BeanDefinitionRegistryPostProcessor与BeanFactoryPostProcessor
// First, invoke the BeanDefinitionRegistryPostProcessors that implement PriorityOrdered.
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
// Do not initialize FactoryBeans here: We need to leave all regular beans
// uninitialized to let the bean factory post-processors apply to them!
String[] postProcessorNames =
beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
下面我们来进行一个后处理器小测试
ConfigurationClassPostProcessor后处理器
新建一个测试类编写如下方法:
package com.yyl.refresh;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;
public class AppPostProcess
public static void main(String[] args)
GenericApplicationContext context = new GenericApplicationContext();
context.registerBean("muConfig",MyConfig.class);
// context.registerBean("configurationClassPostProcessor", ConfigurationClassPostProcessor.class);
context.refresh();
System.out.println();
@Configuration
static class MyConfig
public MyConfig()
@Bean
public Bean1 bean1()
return new Bean1();
@Bean
public Bean2 bean2()
return new Bean2();
static class Bean1
static class Bean2
默认没有用到Bean1和Bean2,是不会主动加到BeanFactory中的
我们在第4步与第5步加一个断点,debug下:
我们看到只有一个我们注册的Bean:
当我们加上一个后处理器:ConfigurationClassPostProcessor
context.registerBean("configurationClassPostProcessor", ConfigurationClassPostProcessor.class);
再运行,当执行到第四步时,只有两个我们注册的Bean:
当执行完第五步,我们发现执行了后处理器的方法,把配置中的Bean也加到Bean工厂里来了
下面我们在使用@import测试下
@Configuration
@Import(Bean3.class)
static class MyConfig
public MyConfig()
@Bean
public Bean1 bean1()
return new Bean1();
@Bean
public Bean2 bean2()
return new Bean2();
static class Bean3
重新Debug,可以看到Bean3也能通过后处理器加载进来
再加一个资源文件的注解
@Configuration
@Import(Bean3.class)
@PropertySource("classpath:1.properties")
static class MyConfig
public MyConfig()
@Bean
public Bean1 bean1()
return new Bean1();
@Bean
public Bean2 bean2()
return new Bean2();
运行,可以看到资源文件也能加载到环境当中
MapperScannerConfigurer后处理器
// mybatis后处理器
context.registerBean("mapperScannerConfigurer", MapperScannerConfigurer.class,bd ->
// 找包名
bd.getPropertyValues().add("basePackage","com.yyl.mapper");
);
设置查找包下接口注入BeanFactory
// mybatis后处理器
context.registerBean("mapperScannerConfigurer", MapperScannerConfigurer.class,bd ->
// 找包名
bd.getPropertyValues().add("basePackage","com.yyl.mapper");
// 找配置了@mapper注解的
bd.getPropertyValues().add("annotationClass","org.apache.ibatis.annotations.Mapper");
);
这回就只有roleMapper了
PropertySourcesPlaceholderConfigurer后处理器
解析$的值
context.registerBean("propertySourcesPlaceholderConfigurer", PropertySourcesPlaceholderConfigurer.class);
测试一下,新增一个Bean4:
static class Bean4
private String value;
public String getValue()
return value;
public void setValue(String value)
this.value = value;
将$JAVA_HOME设置到value上
context.registerBean("bean4",Bean4.class,bd ->
bd.getPropertyValues().add("value","$JAVA_HOME");
);
如果没加这个处理器是不能显示的
加上PropertySourcesPlaceholderConfigurer后处理器
context.registerBean("propertySourcesPlaceholderConfigurer", PropertySourcesPlaceholderConfigurer.class);
再运行没有问题:
6. registerBeanPostProcessors(beanFactory);
注册 bean创建的 bean 后处理器(在Bean的声明周期对Bean进行扩展)
- bean 后处理器,充当 bean 的扩展点,可以工作在 bean 的实例化、依赖注入、初始化阶段
- AutowiredAnnotationBeanPostProcessor 功能有:解析 @Autowired,@Value 注解
- CommonAnnotationBeanPostProcessor 功能有:解析 @Resource,@PostConstruct,@PreDestroy
- AnnotationAwareAspectJAutoProxyCreator 功能有:为符合切点的目标 bean 自动创建代理
三个处理器执行时机如下图所示:
执行流程如下:
beanDefinition中有没有Bean实现了PostProcess接口,有就说明不是一般的Bean是一个Bean
以上是关于一步一步解读 refresh源码的主要内容,如果未能解决你的问题,请参考以下文章
RecyclerView 源码学习:一步一步自定义LayoutManager