https://www.jianshu.com/p/5489e25744a9

https://www.cnblogs.com/warehouse/p/9385110.html

概述

使用 Spring 时,XML 和注解是使用得最多的两种配置方式,虽然是两种完全不同的配置方式,但对于 IOC 容器来说,两种方式的不同主要是在 BeanDefinitionReader 读取不同的配置,解析成 BeanDefinition 对象。而对于核心的容器启动流程,仍然是一致的。

主程序入口

XmlUserTest 测试类

  1. public class XmlUserTest {
  2. public static void main(String[] args) {
  3. ApplicationContext ac = new ClassPathXmlApplicationContext("spring-config.xml");
  4. User user = ac.getBean("user", User.class);
  5. System.out.println(user.toString());
  6. }
  7. }


spring-config.xml 配置

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans
  5. http://www.springframework.org/schema/beans/spring-beans.xsd">
  6. <bean id="user" class="com.zlp.spring.init.entity.User">
  7. <property name="age" value="10"/>
  8. <property name="userName" value="smile"/>
  9. </bean>
  10. </beans>

User 实体类

  1. /**
  2. * User实体类
  3. * @date: 2021/2/24 17:47
  4. */
  5. @Lazy(value = false)
  6. public class User {
  7. private String userName;
  8. private Integer age;
  9. public User(){
  10. System.out.println("空参构造方法");
  11. }
  12. public void setUserName(String userName) {
  13. this.userName = userName;
  14. }
  15. public void setAge(Integer age) {
  16. this.age = age;
  17. }
  18. @Override
  19. public String toString() {
  20. return "User{" +
  21. "userName='" + userName + '\'' +
  22. ", age=" + age +
  23. '}';
  24. }
  25. }

源码调试详解

前期准备环境工作做好,下面进入正题,开始 debug 调试Spring Ioc 启动流程分析。

ClassPathXmlApplicationContext

  1. /**
  2. * 创建一个新的 ClassPathXmlApplicationContext,从给定的XML文件加载定义并自动刷新上下文
  3. * @param configLocation resource location
  4. * @throws BeansException if context creation failed
  5. */
  6. public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
  7. // 本类构造函数
  8. this(new String[] {configLocation}, true, null);
  9. }
  10. /**
  11. * 使用给定的父级创建新的ClassPathXmlApplicationContext,从给定的XML文件加载定义。
  12. * @param 资源位置数组
  13. * @param 是否自动刷新上下文,加载所有bean定义并创建所有单例。或者,在进一步配置上下文后手动调用refresh。
  14. * Alternatively, call refresh manually after further configuring the context.
  15. * @param parent the parent context
  16. * @throws BeansException if context creation failed
  17. * @see #refresh()
  18. */
  19. public ClassPathXmlApplicationContext(String[] configLocations,
  20. boolean refresh,
  21. @Nullable ApplicationContext parent) throws BeansException {
  22. /**
  23. * 1.调用父类构造函数完成一些资源的初始化:
  24. * 1.1 初始化默认的资源文件解析器,默认为:PathMatchingResourcePatternResolver.
  25. * 1.2 初始化默认的类加载器.
  26. */
  27. super(parent);
  28. // 2.解析配置文件的路径,例如:applicationContext-${user.name}.xml这种配置文件名称,并且保存到configLocations中,为下一步的解析bean定义做准备
  29. setConfigLocations(configLocations);
  30. // 3.如果需要刷新容器
  31. if (refresh) {
  32. // 执行刷新容器的操作
  33. refresh();
  34. }
  35. }

细节部分:

  • String [] configLocations :可以是一个数组,可以加载多个配置文件
  • ApplicationContext parent :默认是空(null)
  • super(parent) : 当前类继承了 ClassPathXmlApplicationContext 父类时,可以自动义资源初始化配置操作

refresh()

总共有 13 个方法

  1. @Override
  2. public void refresh() throws BeansException, IllegalStateException {
  3. synchronized (this.startupShutdownMonitor) {
  4. /**
  5. * 1.准备上下文的刷新工作,记录bean容器的启动时间,容器活跃状态
  6. * 验证系统中一些属性和属性值的设置等.
  7. * 使用LinkedHashSet初始化earlyApplicationListeners和earlyApplicationEvents
  8. */
  9. prepareRefresh();
  10. /**
  11. * 2.获取Bean工厂,期间会做解析和加载bean定义的一些列工作.生成BeanDefinition对象.
  12. * 此处返回的beanFactory的真实类型为:DefaultListableBeanFactory
  13. *
  14. *
  15. * 自定义的xsd约束文件也会在该步骤进行解析,通过实现BeanDefinitionParser接口,并实现parse方法
  16. * 解析自定义标签时通过实现NamespaceHandlerSupport接口,并实现init方法进行实现
  17. */
  18. ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
  19. /**
  20. * 3.bean工厂的初始化准备工作,设置bean工厂的一些属性
  21. * 比如:创建bean工厂时,需要忽略哪些接口,需要注册哪些bean,需要设置哪些Bean的后置处理器等.
  22. *
  23. * 例如常用的:ApplicationContextAwareBeanPostProcessor, ApplicationListenerDetector
  24. *
  25. * 此外,注册一些和环境相关的bean单实例bean.
  26. */
  27. prepareBeanFactory(beanFactory);
  28. try {
  29. /**
  30. * 4.Bean定义加载完毕之后实现,目前方法为空实现,留给开发人员进行自定义扩展。
  31. * 和BeanFactoryPostProcessor中的方法postProcessBeanFactory相同
  32. *
  33. * 该方法在Bean定义加载完毕之后,Bean实例化之前会执行
  34. * 比如在BeanFactory加载完所有的Bean定义之后,想要修改某个bean的定义信息,可以通过重写这个方法实现.
  35. * 比如:在xml中配置了<bean id="user"><property name="name" value="wb"></property></bean>
  36. * 如果想在不修改配置文件的情况下修改name的值,可以使用如下的方法:
  37. * class MyApplicationContext extends ClassPathXmlApplicationContext{
  38. public MyApplicationContext(String s){
  39. super(s);
  40. }
  41. @Override
  42. protected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
  43. BeanDefinition beanDefinition = beanFactory.getBeanDefinition("user");
  44. PropertyValue propertyValue=new PropertyValue("name", "www.so.com");
  45. beanDefinition.getPropertyValues().addPropertyValue(propertyValue);
  46. }
  47. */
  48. postProcessBeanFactory(beanFactory);
  49. /**
  50. * 5.执行beanFactory的后置处理器
  51. *
  52. * 先执行BeanDefinitionRegistryPostProcessor接口的实现类的postProcessBeanDefinitionRegistry方法,
  53. * 执行过程中,也是先执行实现了优先级接口PriorityOrdered的BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法
  54. * 然后执行实现了Ordered接口的...
  55. * 最后执行未实现PriorityOrdered接口和Ordered接口的...
  56. *
  57. * 然后执行BeanFactoryPostProcessor接口的实现类的postProcessBeanFactory方法
  58. * 执行过程中,也是先执行实现了优先级接口PriorityOrdered的BeanFactoryPostProcessor的postProcessBeanFactory方法
  59. * 然后执行实现了Ordered接口的...
  60. * 最后执行未实现PriorityOrdered接口和Ordered接口的...
  61. *
  62. * 其中也涉及到了排序过程
  63. *
  64. *
  65. * 配置类中的Selector类型的组件和@Component,@ComponentScan中的元数据信息也会在该步骤中进行解析
  66. * 还包括执行条件注解@Condition的回调逻辑
  67. *
  68. *
  69. * ImportBeanDefinitionRegistrar对应的registerBeanDefinitions方法也会在该步骤中调用,给容器中注册自定义的组件.
  70. */
  71. invokeBeanFactoryPostProcessors(beanFactory);
  72. /**
  73. * 6.注册所有bean的后置处理器.用来拦截Bean的创建
  74. *
  75. * 注册所有实现了BeanPostProcessor接口的后置处理器
  76. * 执行过程中,也是先执行实现了优先级接口PriorityOrdered接口的BeanPostProcessor的addBeanPostProcessor方法
  77. * 然后执行实现了Ordered接口的...
  78. * 最后执行未实现PriorityOrdered接口和Ordered接口的...
  79. *
  80. * 其中也涉及到了排序过程
  81. */
  82. registerBeanPostProcessors(beanFactory);
  83. /**
  84. * 7.初始化消息源
  85. * 用来做国际化,消息绑定,消息解析等功能
  86. * 一般在SpringMVC中会使用到.
  87. */
  88. initMessageSource();
  89. /**
  90. * 8.初始化事件派发器,用来发布事件
  91. * 如果容器中有类型为ApplicationEventMulticaster的派发器组件,则直接获取使用
  92. * 如果容器中没有,则默认创建一个类型为SimpleApplicationEventMulticaster的派发器,供容器派发事件使用
  93. */
  94. initApplicationEventMulticaster();
  95. /**
  96. * 9.用来初始化一些特殊的Bean,目前默认是空方法,未实现,可以通过继承AbstractApplicationContext类,
  97. * 然后覆写该方法进行自定义特殊bean的初始化.
  98. *
  99. * 比如:AbstractRefreshableWebApplicationContext中onRefresh方法用来初始化主题能力.
  100. *
  101. * SpringBoot也是在改步骤中启动内嵌Tomcat容器的
  102. */
  103. onRefresh();
  104. /**
  105. * 10.注册监听器
  106. * 将监听器绑定到广播器上,将监听器对应的beanName绑定到到第8步初始化的事件派发器中,
  107. * 如果之前有发布的事件,则直接通过事件派发器将事件派发出去.
  108. */
  109. registerListeners();
  110. /**
  111. * 11.初始化所有剩余的单实例Bean(没有使用懒加载的Bean).整个Spring IOC的核心.
  112. *
  113. * 包括执行@PostConstruct标注的方法.
  114. *
  115. * 注意:SpringMVC的父子容器创建Bean的过程:
  116. * SpringMVC中,存在着父容器和子容器。当父容器启动之后,会通过该方法将所有的Dao和Service对应的Bean创建出来,保存到beanFactory的单例缓存容器中
  117. * 当子容器启动之后,也会通过该方法将所有的Controller,viewResolver,HandlerMapping对应的Bean创建出来,然后放入到beanFactory的单例缓存容器中.
  118. */
  119. finishBeanFactoryInitialization(beanFactory);
  120. /** 12.发布事件。例如容器中的刷新事件:ContextRefreshedEvent就是在这一步中发布. SpringCloud在该步骤中会启动web服务 */
  121. finishRefresh();
  122. }
  123. catch (BeansException ex) {
  124. if (logger.isWarnEnabled()) {
  125. logger.warn("Exception encountered during context initialization - " +
  126. "cancelling refresh attempt: " + ex);
  127. }
  128. // Destroy already created singletons to avoid dangling resources.
  129. // 清空单实例bean对应的map及缓存
  130. destroyBeans();
  131. // 设置容器的活跃状态为false
  132. cancelRefresh(ex);
  133. // Propagate exception to caller.
  134. throw ex;
  135. }
  136. finally {
  137. // Reset common introspection caches in Spring's core, since we
  138. // might not ever need metadata for singleton beans anymore...
  139. resetCommonCaches();
  140. }
  141. }
  142. }

prepareRefresh()

该方法很简单,主要做前期准备工作

  1. 记录容器的启动时间
  2. 设置容器的关闭状态closed(false)和激活状态active(true)
  3. initPropertySources,初始化容器中的一些校验规则,比如字段的校验;使用getEnvironment().setRequiredProperties()进行设置
  4. 校验容器中的一些必要属性,如果第(3)步中设置了容器中的必要属性,该步骤会对第三步设置的值进行校验
  5. 声明容器中早期的应用监听器以及容器中早期的应用事件

    1. /**
    2. * 准备此上下文以进行刷新,设置其启动日期和活动标志,以及执行属性源的任何初始化。
    3. */
    4. protected void prepareRefresh() {
    5. // 记录容器的启动时间.
    6. this.startupDate = System.currentTimeMillis();
    7. // 记录容器未关闭
    8. this.closed.set(false);
    9. // 记录容器状态为激活状态
    10. this.active.set(true);
    11. if (logger.isDebugEnabled()) {
    12. if (logger.isTraceEnabled()) {
    13. logger.trace("Refreshing " + this);
    14. }
    15. else {
    16. logger.debug("Refreshing " + getDisplayName());
    17. }
    18. }
    19. // Initialize any placeholder property sources in the context environment.
    20. /**
    21. * 如果需要在验证系统属性之前,给系统中设置一些默认值。可以通过继承AbstractApplicationContext类,并重写该方法实现。
    22. * Spring留给开发人员的一个扩展点.
    23. *
    24. * 例如子类在方法中设置环境属性中必须需要的变量:getEnvironment().setRequiredProperties("myProp");
    25. */
    26. initPropertySources();
    27. // Validate that all properties marked as required are resolvable:
    28. // see ConfigurablePropertyResolver#setRequiredProperties
    29. /**
    30. * 作用:校验设置的必须属性是否能够在系统环境中找到对应的值
    31. *
    32. * 如果在initPropertySources方法中使用getEnvironment().setRequiredProperties(String... keys)设置了必须的属性,而通过this.getProperty(key)
    33. * 没有从系统环境中获取到属性的值,则会抛出MissingRequiredPropertiesException异常
    34. */
    35. getEnvironment().validateRequiredProperties();
    36. // Store pre-refresh ApplicationListeners...
    37. if (this.earlyApplicationListeners == null) {
    38. /** 在SpringBoot中会有大量的初始化监听器,用于初始化使用 */
    39. this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
    40. }
    41. else {
    42. // Reset local application listeners to pre-refresh state.
    43. this.applicationListeners.clear();
    44. this.applicationListeners.addAll(this.earlyApplicationListeners);
    45. }
    46. // Allow for the collection of early ApplicationEvents,
    47. // to be published once the multicaster is available...
    48. /** 定义早期的应用事件 */
    49. this.earlyApplicationEvents = new LinkedHashSet<>();
    50. }

网上有人说其实这个函数没什么用,因为最后两句代码才是最为关键的,但是却没有什么逻辑处理,initPropertySources 是空的,没有任何逻辑,而 getEnvironment().validateRequiredProperties 也因为没有需要验证的属性而没有做任何处理。其实这都是因为没有彻底理解才会这么说,这个函数如果用好了作用还是挺大的。那么,该怎么用呢?我们先探索下各个函数的作用。
(1)initPropertySources 正符合 Spring 的开放式结构设计,给用户最大扩展 Spring 的能力。用户可以根据自身的需要重写 initPropertySources 方法,并在方法中进行个性化的属性处理及设置。
(2)validateRequiredProperties 则是对属性进行验证,那么如何验证呢?我们举个融合两句代码的小例子来帮助大家理解。

假如现在有这样一个需求,工程在运行过程中用到的某个设置(例如 VAR )是从系统环境变量中取得的,而如果用户没有在系统环境变量中配置这个参数,那么工程可能不会工作。这一要求可能会有各种各样的解决办法,当然,在 Spring 中可以这样做,你可以直接修改 Spring 的源码,例如修改 ClassPathXmlApplicationContext 。当然,最好的办法还是对源码进行扩展,我们可以自定义

  1. public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {
  2. public MyClassPathXmlApplicationContext(String... configLocations) throws BeansException {
  3. super(configLocations);
  4. }
  5. @Override
  6. protected void initPropertySources() {
  7. // 添加验证要求
  8. getEnvironment().setRequiredProperties("VAR");
  9. }
  10. }

我们自定一义了继承自 ClassPathXmlApplicationContext 的 MyClassPathXmlApplicationContext , 并重写了 initPropertySources 方法,在方法中添加了我们的个性化需求,那么在验证的时候也就是程序走到 getEnvironment().validateRequiredProperties() 代码的时候,如果系统并没有检测到对应 VAR 的环境变量,那么将抛出异常。当然我们还需要在使用的时候替换掉原有的 ClassPathXmlApplicationContext:

  1. public static void main(String[] args) {
  2. ApplicationContext ac = new MyClassPathXmlApplicationContext("spring-config.xml");
  3. User user = (User) ac.getBean("user");
  4. System.out.println(user.toString());
  5. }

obtainFreshBeanFactory()

obtainFreshBeanFactory 方法从字面上理解是获取 BeanFactory。之前有说过,ApplicationContext 是对BeanFactory 的功能上的扩展,不但包含了BeanFactory的全部功能更在其基础上添加了大量的扩展功能,那么obtainFreshBeanFactory 正是实现BeanFactory的地方,也就是经过了这个函数后 ApplicationContext 就已经拥有了 BeanFactory 的全部功能。

  1. /**
  2. * 告诉子类刷新内部bean工厂
  3. * @return the fresh BeanFactory instance
  4. * @see #refreshBeanFactory()
  5. * @see #getBeanFactory()
  6. */
  7. protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
  8. /**
  9. * 刷新bean工厂,判断bean工厂是否已经存在,如果存在需要进行销毁和关闭
  10. * 默认调用的是AbstractRefreshableApplicationContext的refreshBeanFactory方法.
  11. * 刷新Bean工厂时会进行bean定义的加载操作。
  12. */
  13. refreshBeanFactory();
  14. return getBeanFactory();
  15. }

方法中将核心实现委托给了 refreshBeanFactory

  1. /**
  2. * This implementation performs an actual refresh of this context's underlying
  3. * bean factory, shutting down the previous bean factory (if any) and
  4. * initializing a fresh bean factory for the next phase of the context's lifecycle.
  5. */
  6. @Override
  7. protected final void refreshBeanFactory() throws BeansException {
  8. // 判断bean工厂是否存在,如果存在需要先销毁和关闭。否则会出现问题
  9. if (hasBeanFactory()) {
  10. // 销毁bean,根据bean的名称将bean工厂中的所有bean都从map中移除
  11. destroyBeans();
  12. // 关闭bean工厂,设置Bean工厂的序列化id为null,并将beanFactory的值赋值为null
  13. closeBeanFactory();
  14. }
  15. try {
  16. // 重新创建bean工厂,默认返回的是Bean工厂类型是:DefaultListableBeanFactory
  17. DefaultListableBeanFactory beanFactory = createBeanFactory();
  18. // 设置序列化ID,ID: class名称 + "@" + 对象的十六进制值
  19. beanFactory.setSerializationId(getId());
  20. /** 自定义Bean工厂
  21. * 设置两个属性:
  22. * 1. 是否允许覆盖同名称的不同定义的对象
  23. * 2. 是否允许bean之间存在循环依赖
  24. */
  25. customizeBeanFactory(beanFactory);
  26. // 解析并加载bean的定义,默认是通过AbstractXmlApplicationContext类中的loadBeanDefinitions实现
  27. loadBeanDefinitions(beanFactory);
  28. synchronized (this.beanFactoryMonitor) {
  29. this.beanFactory = beanFactory;
  30. }
  31. }
  32. catch (IOException ex) {
  33. throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
  34. }
  35. }

我们详细分析上面的每个步骤。

  1. 创建 DefaultListableBeanFactory。
  2. 在介绍BeanFactory 的时候,不知道读者是否还有印象,声明方式为:ApplicationContext ac = new ClassPathXmlApplicationContext(“spring-config.xml”); 并 XmlBeanDefinitionReader 提供了读取类型的 reader 属性也就是说 DefaultListableBeanFactory 是容器的基础。必须首先实例化,那么在这里就是实例化DefaultListableBeanFactory 的步骤。
  3. 指定序列化ID。
  4. 定制BeanFactory。
  5. 加载BeanDefinition。
  6. 使用全局变量记录BeanFactory类实例。
  7. 因为DefaultListableBeanFactory类型的变量beanFactory是函数内的局部变量,所以要使用全局变量记录解析结果。

    定制化 BeanFactory

    1. /**
    2. * Customize the internal bean factory used by this context.
    3. * Called for each {@link #refresh()} attempt.
    4. * <p>The default implementation applies this context's
    5. * {@linkplain #setAllowBeanDefinitionOverriding "allowBeanDefinitionOverriding"}
    6. * and {@linkplain #setAllowCircularReferences "allowCircularReferences"} settings,
    7. * if specified. Can be overridden in subclasses to customize any of
    8. * {@link DefaultListableBeanFactory}'s settings.
    9. * @param beanFactory the newly created bean factory for this context
    10. * @see DefaultListableBeanFactory#setAllowBeanDefinitionOverriding
    11. * @see DefaultListableBeanFactory#setAllowCircularReferences
    12. * @see DefaultListableBeanFactory#setAllowRawInjectionDespiteWrapping
    13. * @see DefaultListableBeanFactory#setAllowEagerClassLoading
    14. */
    15. protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
    16. // 如果属性allowBeanDefinitionOverriding不为空,设置给beanFactory对象相应属性
    17. // 此属性的含义:是否允许覆盖同名称的不同定义的对象
    18. if (this.allowBeanDefinitionOverriding != null) {
    19. beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    20. }
    21. // 如果属性allowCircularReferences不为空,设置给beanFactory对象相应属性
    22. // 此属性的含义:是否允许bean之间存在循环依赖
    23. if (this.allowCircularReferences != null) {
    24. beanFactory.setAllowCircularReferences(this.allowCircularReferences);
    25. }
    26. }

    是否允许覆盖和允许依赖的设置这里这是判断了是否为空,如果不为空则进行设置,但是并没有看到在哪里进行设置,究竟这个设置是在哪里进行设置的呢?还是那句话,使用子类覆盖方法,例如:

    1. public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {
    2. ......
    3. @Override
    4. protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
    5. super.setAllowBeanDefinitionOverriding(false);
    6. super.setAllowCircularReferences(false);
    7. super.customizeBeanFactory(beanFactory);
    8. }
    9. }

    加载 BeanDefinition

    在第一步中提到了 ClassPathXmlApplicationContext XmlBeanFactory 创建的对比,在实现配置文件的加载功能中除了我们在第一步中已经初始化的 DefaultListableBeanFactory外,还需要 XmlBeanDefinitionReader 来读取XML,那么在这个步骤中首先要做的就是初始化 XmlBeanDefinitionReader

    1. /**
    2. * Loads the bean definitions via an XmlBeanDefinitionReader.
    3. * @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader
    4. * @see #initBeanDefinitionReader
    5. * @see #loadBeanDefinitions
    6. */
    7. @Override
    8. protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
    9. // Create a new XmlBeanDefinitionReader for the given BeanFactory.
    10. // 为指定 beanFactory 创建 XmlBeanDefinitionReader
    11. XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
    12. // Configure the bean definition reader with this context's
    13. // resource loading environment.
    14. // 对beanDefinitionReader进行环境变量的设置
    15. beanDefinitionReader.setEnvironment(this.getEnvironment());
    16. beanDefinitionReader.setResourceLoader(this);
    17. beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
    18. // Allow a subclass to provide custom initialization of the reader,
    19. // then proceed with actually loading the bean definitions.
    20. // 对beanDefinitionReader进行设置,可以覆盖
    21. initBeanDefinitionReader(beanDefinitionReader);
    22. loadBeanDefinitions(beanDefinitionReader);
    23. }

    在初始化了 DefaultListableBeanFactory XmlBeanDefinitionReader 后就可以进行配置文件的读取了。

    1. /**
    2. * Load the bean definitions with the given XmlBeanDefinitionReader.
    3. * <p>The lifecycle of the bean factory is handled by the {@link #refreshBeanFactory}
    4. * method; hence this method is just supposed to load and/or register bean definitions.
    5. * @param reader the XmlBeanDefinitionReader to use
    6. * @throws BeansException in case of bean registration errors
    7. * @throws IOException if the required XML document isn't found
    8. * @see #refreshBeanFactory
    9. * @see #getConfigLocations
    10. * @see #getResources
    11. * @see #getResourcePatternResolver
    12. */
    13. protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
    14. Resource[] configResources = getConfigResources();
    15. if (configResources != null) {
    16. reader.loadBeanDefinitions(configResources);
    17. }
    18. String[] configLocations = getConfigLocations();
    19. if (configLocations != null) {
    20. reader.loadBeanDefinitions(configLocations);
    21. }
    22. }

    使用 XmlBeanDefinitionReader loadBeanDefinitions 方法进行配置文件的加载注册相信大家已经不陌生,这完全就是开始 BeanFactory 的套路。因为在XmlBeanDefinitionReader 中已经将之前初始化的DefaultListableBeanFactory 注册进去了,所有 XmlBeanDefinitionReader 所读取的 BeanDefinitionHolder 都会注册到 DefaultListableBeanFactory 中,也就是经过此步骤,类型 DefaultListableBeanFactory 的变量beanFactory 已经包含了所有解析好的配置。

prepareBeanFactory

在进入 prepareBeanFactory 前,Spring 已经完成了对配置的解析,而ApplicationContext在功能上的扩展也由此展开。

  1. /**
  2. * 配置工厂的标准上下文特征,例如上下文的类加载器和后处理器
  3. * @param beanFactory the BeanFactory to configure
  4. */
  5. protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
  6. // Tell the internal bean factory to use the context's class loader etc.
  7. beanFactory.setBeanClassLoader(getClassLoader());
  8. // 设置SPEL表达式解析器,用来支持Spring的SPEL表达式
  9. beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
  10. // 添加属性编辑注册器。例如一个字符串类型的地址需要转换为一个Address对象,可以使用该功能.
  11. // 可参考示例:spring-source-study模块下的com.wb.spring.propertyeditor包下的示例程序
  12. beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
  13. // 添加bean的后置处理器。此处添加的是Spring自己的后置处理器,用来回调bean所实现的aware接口中的方法.
  14. beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
  15. // 下面的ignoreDependencyInterface是用来设置bean工厂中需要忽略的接口
  16. // 可以通过实现EnvironmentAware接口来获取到当前的环境信息Environment。
  17. /**
  18. * 如果将EnvironmentAware接口添加到ignoreDependencyInterface中,则在使用的地方通过@Autowired将会无法正常注入
  19. * 而是需要通过setEnvironment方法进行注入,下面的其他接口都类似.
  20. */
  21. beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
  22. // 可以通过实现EmbeddedValueResolverAware接口来获取String类型值的解析器
  23. beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
  24. // 资源加载器,例如使用:@Autowired ResourceLoaderAware aware; 将不会被注入
  25. beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
  26. // 事件发布器
  27. beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
  28. // 消息资源
  29. beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
  30. // 应用的上下文信息
  31. beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
  32. // 注册一些可以自动装配的接口。 当类型为dependencyType时, 注入autowiredValue。为了解决一个类型有多个子类实现时,优先注入那个子类实现的问题。
  33. // 例如下面第一个,当注入类型为BeanFactory时,注入的值为beanFactory,默认为DefaultListableBeanFactory
  34. beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
  35. // 当注入类型为ResourceLoader时,注入的值为ApplicationContext
  36. beanFactory.registerResolvableDependency(ResourceLoader.class, this);
  37. // 当注入类型为ApplicationEventPublisher时,注入的值为ApplicationContext
  38. beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
  39. // 当注入的类型为ApplicationContext时,注入的值为ApplicationContext.
  40. beanFactory.registerResolvableDependency(ApplicationContext.class, this);
  41. // 增加一个bean的后置处理器,ApplicationListenerDetector
  42. // Register early post-processor for detecting inner beans as ApplicationListeners.
  43. beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
  44. // Detect a LoadTimeWeaver and prepare for weaving, if found.
  45. // 如果bean工厂中存在着名称为loadTimeWeaver的bean定义,则给bean工厂中加入LoaderTimeWeaverAwareProcessor后置处理器
  46. // 补充:1、增加对AspectJ的支持,在Java中织入分为3种:(1) 编译期织入; (2) 类加载期织入; (3) 运行期织入。编译期织入指的是在java编译期,
  47. // 采用特殊的编译器,将切面织入到java类中;类加载期织入则指的是通过特殊的类加载器,在字节码加载到JVM时,织入切面;运行期织入则是
  48. // 通过采用cglib或者jdk进行切面的织入。
  49. // 2、aspectJ中提供了两种方式:
  50. // (1) 通过特殊编译器,在编译期,将aspectJ语言编写的切面类织入到java类中;
  51. // (2) 类加载期织入,就是通过下面的LoadTimeWeaving
  52. if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
  53. beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
  54. // Set a temporary ClassLoader for type matching.
  55. beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
  56. }
  57. // 给容器中注册一些与运行环境相关的单实例Bean
  58. if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
  59. // beanName: environment,直接将new出来的Spring内部对象放入到Spring的单实例缓存池中.
  60. beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
  61. }
  62. if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
  63. // beanName: systemProperties 方法:System.getProperties();
  64. beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
  65. }
  66. if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
  67. // beanName: systemEnvironment, 方法:System.getEnv();
  68. beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
  69. }
  70. }

上面函数中主要进行了几个方面的扩展。

  • 增加对 SPEL 语言的支持。
  • 增加对属性编辑器的支持。
  • 增加对一些内置类,比如 EnvironmentAvareMessageSourceAware 的信息注入。
  • 设置了依赖功能可忽略的接口。
  • 注册一些固定依赖的属性。
  • 增加对AspectJ的支持。
  • 将相关环境变量及属性注册以单例模式注册。

    可能读者不是很理解每个步骤的具体含义,接下来我们会对各个步骤进行详细地分析。

增加SPEL语言的支持

Spring 表达式语言全称为 “Spring Expression Language”,缩写为 “SpEL”,类似于Struts 2x 中使用的OGNL表达式语言,能在运行时构建复杂表达式、存取对象图属性、对象方法调用等, 并且能与Spring功能完美整合,比如能用 来配置bean定义。SpEL是单独模块,只依赖于core 模块,不依赖于其他模块,可以单独使用。
SpEL使用#{…}为定界符,所有在花括号中的字符都将被认为是SpEL,使用格式如下:

  1. <bean id = "saxophone" value = "com.xxx.xxx.Xxx"/>
  2. <bean >
  3. <property name="instrument" value="#{saxophone}"/>
  4. <bean/>

相当于:

  1. <bean id = "saxophone" value = "com.xxx.xxx.Xxx"/>
  2. <bean >
  3. <property name="instrument" ref="saxophone"/>
  4. <bean/>

当然,上面只是列举了其中最简单的使用方式,SPEL功能非常强大,使用好可以大大提高开发效率,这里只为唤起读者的记忆来帮助我们理解源码,有兴趣的读者可以进一步深入研究。

在源码中通过代码 beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver()) 注册语言解析器,就可以对SPEL进行解析了,那么在注册解析器后 Spring 又是在什么时候调用这个解析器进行解析呢?

之前我们讲解过 Spring 在 bean 进行初始化的时候会有属性填充的一步,而在这一步中 Spring 会调用AbstractAutowireCapableBeanFactory 类的 applyPropertyValues 函数来完成功能。就在这个函数中,会通过构造 BeanDefinitionValueResolver 类型实例 valueResolver 来进行属性值的解析。同时,也是在这个步骤中一般通过 AbstractBeanFactory中的 evaluateBeanDefinitionString 方法去完成 SPEL 的解析。

  1. /**
  2. * Evaluate the given String as contained in a bean definition,
  3. * potentially resolving it as an expression.
  4. * @param value the value to check
  5. * @param beanDefinition the bean definition that the value comes from
  6. * @return the resolved value
  7. * @see #setBeanExpressionResolver
  8. */
  9. @Nullable
  10. protected Object evaluateBeanDefinitionString(@Nullable String value, @Nullable BeanDefinition beanDefinition) {
  11. if (this.beanExpressionResolver == null) {
  12. return value;
  13. }
  14. Scope scope = null;
  15. if (beanDefinition != null) {
  16. String scopeName = beanDefinition.getScope();
  17. if (scopeName != null) {
  18. scope = getRegisteredScope(scopeName);
  19. }
  20. }
  21. return this.beanExpressionResolver.evaluate(value, new BeanExpressionContext(this, scope));
  22. }

当调用这个方法时会判断是否存在语言解析器,如果存在则调用语言解析器的方法进行解析,解析的过程是在Spring的 expression 的包内,这里不做过多解释。我们通过查看对evaluateBeanDefinitionString方法的调用层次可以看出,应用语言解析器的调用主要是在解析依赖注入bean的时候,以及在完成bean的初始化和属性获取后进行属性填充的时候。

增加属性注册编辑器

在Spring DI注入的时候可以把普通属性注入进来,但是像Date类型就无法被识别。例如:

  1. public class UserManager {
  2. private Date dateValue;
  3. public Date getDateValue() {
  4. return dateValue;
  5. }
  6. public void setDateValue(Date dateValue) {
  7. this.dateValue = dateValue;
  8. }
  9. @Override
  10. public String toString() {
  11. return "dataValue: " + dateValue;
  12. }
  13. }

上面代码中,需要对日期型属性进行注入:

  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://www.springframework.org/schema/beans
  5. http://www.springframework.org/schema/beans/spring-beans.xsd">
  6. <bean id="userManager" class="org.cellphone.uc.UserManager">
  7. <property name="dateValue">
  8. <value>2018-07-29</value>
  9. </property>
  10. </bean>
  11. </beans>

测试代码:

  1. public static void main(String[] args) {
  2. ApplicationContext context = new ClassPathXmlApplicationContext("spring/beans-test.xml");
  3. UserManager manager = (UserManager) context.getBean("userManager");
  4. System.out.println(manager);
  5. }

如果直接这样使用,程序则会报异常,类型转换不成功。因为在UserManager中的dataValue属性是Date类型的,而在XML中配置的确实String类型的,所以当然会报异常。
Spring针对此问题提供了两种解决方法。

使用自定义属性编辑器

使用自定义属性编辑器,通过继承PropertyEditorSupport,重写setAsText方法,具体步骤如下。
(1)编写自定义的属性编辑器。

  1. public class DatePropertyEditor extends PropertyEditorSupport {
  2. private String format = "yyyy-MM-dd";
  3. public void setFormat(String format) {
  4. this.format = format;
  5. }
  6. @Override
  7. public void setAsText(String text) throws IllegalArgumentException {
  8. System.out.println("text: " + text);
  9. SimpleDateFormat sdf = new SimpleDateFormat(format);
  10. try {
  11. Date d = sdf.parse(text);
  12. this.setValue(d);
  13. } catch (ParseException e) {
  14. e.printStackTrace();
  15. }
  16. }
  17. }

(2)将自定义属性编辑器注册到Spring中。

  1. <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
  2. <property name="customEditors">
  3. <map>
  4. <entry key="java.util.Date" value="org.cellphone.uc.DatePropertyEditor"/>
  5. </map>
  6. </property>
  7. </bean>

在配置文件中引入类型为org.springframework.beans.factory.config.CustomEditorConfigurer的bean,并在属性customEditors中加入自定义的属性编辑器,其中key为属性编辑器所对应的类型。通过这样的配置,当Spring在注入bean的属性时一旦遇到了java.util.Date类型的属性会自动调用自定义的DatePropertyEditor解析器进行解析,并用解析结果代替配置属性进行注入。

注册Spring自带的属性编辑器 CustomDateEditor

通过注册Spring自带的属性编辑器CustomDateEditor,具体步骤如下:
(1)定义属性编辑器。

  1. public class DatePropertyEditorRegistrar implements PropertyEditorRegistrar {
  2. @Override
  3. public void registerCustomEditors(PropertyEditorRegistry registry) {
  4. registry.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true));
  5. }
  6. }

(2)注册到Spring中。

  1. <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
  2. <property name="propertyEditorRegistrars">
  3. <list>
  4. <bean class="org.cellphone.uc.DatePropertyEditorRegistrar"></bean>
  5. </list>
  6. </property>
  7. </bean>

通过在配置文件中将自定义的DatePropertyEditorRegistrar注册进入org.springframework.beans.factory.config.CustomEditorConfigurer 的 propertyEditorRegistrars 属性中,可以具有与方法 1 同样的效果。

我们了解了自定义属性编辑器的使用,但是,似乎这与本节中围绕的核心代码beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()))并无联系,因为在注册自定义属性编辑器的时候使用的是 ResourceEditorRegistrar 的 registerCustomEditors 方法,而这里使用的是ConfigurableListableBeanFactory的addPropertyEditorRegistrar方法。

我们不妨深入探索一下 ResourceEditorRegistrar 的内部实现,在 ResourceEditorRegistrar 中,我们最关心的方法是registerCustomEditors 。

  1. /**
  2. * Populate the given {@code registry} with the following resource editors:
  3. * ResourceEditor, InputStreamEditor, InputSourceEditor, FileEditor, URLEditor,
  4. * URIEditor, ClassEditor, ClassArrayEditor.
  5. * <p>If this registrar has been configured with a {@link ResourcePatternResolver},
  6. * a ResourceArrayPropertyEditor will be registered as well.
  7. * @see org.springframework.core.io.ResourceEditor
  8. * @see org.springframework.beans.propertyeditors.InputStreamEditor
  9. * @see org.springframework.beans.propertyeditors.InputSourceEditor
  10. * @see org.springframework.beans.propertyeditors.FileEditor
  11. * @see org.springframework.beans.propertyeditors.URLEditor
  12. * @see org.springframework.beans.propertyeditors.URIEditor
  13. * @see org.springframework.beans.propertyeditors.ClassEditor
  14. * @see org.springframework.beans.propertyeditors.ClassArrayEditor
  15. * @see org.springframework.core.io.support.ResourceArrayPropertyEditor
  16. */
  17. @Override
  18. public void registerCustomEditors(PropertyEditorRegistry registry) {
  19. ResourceEditor baseEditor = new ResourceEditor(this.resourceLoader, this.propertyResolver);
  20. doRegisterEditor(registry, Resource.class, baseEditor);
  21. doRegisterEditor(registry, ContextResource.class, baseEditor);
  22. doRegisterEditor(registry, InputStream.class, new InputStreamEditor(baseEditor));
  23. doRegisterEditor(registry, InputSource.class, new InputSourceEditor(baseEditor));
  24. doRegisterEditor(registry, File.class, new FileEditor(baseEditor));
  25. doRegisterEditor(registry, Path.class, new PathEditor(baseEditor));
  26. doRegisterEditor(registry, Reader.class, new ReaderEditor(baseEditor));
  27. doRegisterEditor(registry, URL.class, new URLEditor(baseEditor));
  28. ClassLoader classLoader = this.resourceLoader.getClassLoader();
  29. doRegisterEditor(registry, URI.class, new URIEditor(classLoader));
  30. doRegisterEditor(registry, Class.class, new ClassEditor(classLoader));
  31. doRegisterEditor(registry, Class[].class, new ClassArrayEditor(classLoader));
  32. if (this.resourceLoader instanceof ResourcePatternResolver) {
  33. doRegisterEditor(registry, Resource[].class,
  34. new ResourceArrayPropertyEditor((ResourcePatternResolver) this.resourceLoader, this.propertyResolver));
  35. }
  36. }
  37. /**
  38. * Override default editor, if possible (since that's what we really mean to do here);
  39. * otherwise register as a custom editor.
  40. */
  41. private void doRegisterEditor(PropertyEditorRegistry registry, Class<?> requiredType, PropertyEditor editor) {
  42. if (registry instanceof PropertyEditorRegistrySupport) {
  43. ((PropertyEditorRegistrySupport) registry).overrideDefaultEditor(requiredType, editor);
  44. }
  45. else {
  46. registry.registerCustomEditor(requiredType, editor);
  47. }
  48. }

doRegisterEditor 函数中,可以看到在之前提到的自定义属性中使用的关键代码: registry.registerCustomEditor(requiredType, editor),回过头来看 ResourceEditorRegistrar 类的 registerCustomEditors方法的核心功能,其实无非是注册了一系列的常用类型的属性编辑器,例 如,代码 doRegisterEditor(registry,Class.class, new ClassEditor(classLoader))实现的功能就是注册 Class类对应的属件编辑器。那么,注册后,一旦某个实体bean中存在一些Class类型的属性, 那么Spring会调用ClassEditor将配置中定义的String类型转换为Class类型并进行陚值。
分析到这里,我们不禁有个疑问,虽说ResourceEditorRegistrar类的registerCustomEditors方法实现了批量注册的功能,但是beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment())仅仅是注册了 ResourceEditorRegistrar 实例,却并没有调用ResourceEditorRegistrar 的registerCustomEditors方法进行注册,那么到底是在什么时候进行注册的呢?进一步查看 ResourceEditorRegistrar 的 registerCustomEditors 方法的调用层次结构,如下图所示。
Spring IOC 容器启动流程分析 - 图1
发现在AbstractBeanFactory中的registerCustomEditors方法中被调用过,继续查看AbstractBeanFactory中的registerCustomEditors方法的层次结构,如下图所示。
Spring IOC 容器启动流程分析 - 图2
其中我们看到一个方法是我们熟悉的,就是AbstractBeanFactory类中的initBeanWrapper 方法,这是在bean初始化时使用的一个方法,之前巳经使用过大量的篇幅进行讲解,主要是在将BeanDefinition转换为BeanWrapper后用于对属件的填充。到此,逻辑已经明了,在bean的初始化后会调用ResourceEditorRegistrar的registerCustomEditors方法进行批量的通用属性编辑器注册。注册后,在属性填充的环节便可以直接让Spring使用这些编辑器进行属性的解析了。
既然提到了 BeanWrapper,这里也有必要强调下,Spring中用于封装bean的是BeanWrapper 类型,而它又间接继承了 PropertyEditorRegistry类型,也就是我们之前反复看到的方法参数 PropertyEditorRegistry registry,其实大部分情况下都是 BeanWrapper,对于 BeanWrapper 在 Spring 中的默认实现是 BeanWrapperlmpl,而 BeanWrapperlmpl 除了实现 BeanWrapper 接门外还继承了 PropertyEdhorRegistrySupport,在 PropertyEditorRegistrySupport 中有这样一个方法:

  1. /**
  2. * Actually register the default editors for this registry instance.
  3. */
  4. private void createDefaultEditors() {
  5. this.defaultEditors = new HashMap<>(64);
  6. // Simple editors, without parameterization capabilities.
  7. // The JDK does not contain a default editor for any of these target types.
  8. this.defaultEditors.put(Charset.class, new CharsetEditor());
  9. this.defaultEditors.put(Class.class, new ClassEditor());
  10. this.defaultEditors.put(Class[].class, new ClassArrayEditor());
  11. this.defaultEditors.put(Currency.class, new CurrencyEditor());
  12. this.defaultEditors.put(File.class, new FileEditor());
  13. this.defaultEditors.put(InputStream.class, new InputStreamEditor());
  14. this.defaultEditors.put(InputSource.class, new InputSourceEditor());
  15. this.defaultEditors.put(Locale.class, new LocaleEditor());
  16. this.defaultEditors.put(Path.class, new PathEditor());
  17. this.defaultEditors.put(Pattern.class, new PatternEditor());
  18. this.defaultEditors.put(Properties.class, new PropertiesEditor());
  19. this.defaultEditors.put(Reader.class, new ReaderEditor());
  20. this.defaultEditors.put(Resource[].class, new ResourceArrayPropertyEditor());
  21. this.defaultEditors.put(TimeZone.class, new TimeZoneEditor());
  22. this.defaultEditors.put(URI.class, new URIEditor());
  23. this.defaultEditors.put(URL.class, new URLEditor());
  24. this.defaultEditors.put(UUID.class, new UUIDEditor());
  25. this.defaultEditors.put(ZoneId.class, new ZoneIdEditor());
  26. // Default instances of collection editors.
  27. // Can be overridden by registering custom instances of those as custom editors.
  28. this.defaultEditors.put(Collection.class, new CustomCollectionEditor(Collection.class));
  29. this.defaultEditors.put(Set.class, new CustomCollectionEditor(Set.class));
  30. this.defaultEditors.put(SortedSet.class, new CustomCollectionEditor(SortedSet.class));
  31. this.defaultEditors.put(List.class, new CustomCollectionEditor(List.class));
  32. this.defaultEditors.put(SortedMap.class, new CustomMapEditor(SortedMap.class));
  33. // Default editors for primitive arrays.
  34. this.defaultEditors.put(byte[].class, new ByteArrayPropertyEditor());
  35. this.defaultEditors.put(char[].class, new CharArrayPropertyEditor());
  36. // The JDK does not contain a default editor for char!
  37. this.defaultEditors.put(char.class, new CharacterEditor(false));
  38. this.defaultEditors.put(Character.class, new CharacterEditor(true));
  39. // Spring's CustomBooleanEditor accepts more flag values than the JDK's default editor.
  40. this.defaultEditors.put(boolean.class, new CustomBooleanEditor(false));
  41. this.defaultEditors.put(Boolean.class, new CustomBooleanEditor(true));
  42. // The JDK does not contain default editors for number wrapper types!
  43. // Override JDK primitive number editors with our own CustomNumberEditor.
  44. this.defaultEditors.put(byte.class, new CustomNumberEditor(Byte.class, false));
  45. this.defaultEditors.put(Byte.class, new CustomNumberEditor(Byte.class, true));
  46. this.defaultEditors.put(short.class, new CustomNumberEditor(Short.class, false));
  47. this.defaultEditors.put(Short.class, new CustomNumberEditor(Short.class, true));
  48. this.defaultEditors.put(int.class, new CustomNumberEditor(Integer.class, false));
  49. this.defaultEditors.put(Integer.class, new CustomNumberEditor(Integer.class, true));
  50. this.defaultEditors.put(long.class, new CustomNumberEditor(Long.class, false));
  51. this.defaultEditors.put(Long.class, new CustomNumberEditor(Long.class, true));
  52. this.defaultEditors.put(float.class, new CustomNumberEditor(Float.class, false));
  53. this.defaultEditors.put(Float.class, new CustomNumberEditor(Float.class, true));
  54. this.defaultEditors.put(double.class, new CustomNumberEditor(Double.class, false));
  55. this.defaultEditors.put(Double.class, new CustomNumberEditor(Double.class, true));
  56. this.defaultEditors.put(BigDecimal.class, new CustomNumberEditor(BigDecimal.class, true));
  57. this.defaultEditors.put(BigInteger.class, new CustomNumberEditor(BigInteger.class, true));
  58. // Only register config value editors if explicitly requested.
  59. if (this.configValueEditorsActive) {
  60. StringArrayPropertyEditor sae = new StringArrayPropertyEditor();
  61. this.defaultEditors.put(String[].class, sae);
  62. this.defaultEditors.put(short[].class, sae);
  63. this.defaultEditors.put(int[].class, sae);
  64. this.defaultEditors.put(long[].class, sae);
  65. }
  66. }

具体的调用方法我们就不去深究了,但是至少通过这个方法我们已经知道了在Spring中定义了上面一系列常用的属性编辑器使我们可以方便地进行配置。如果我们定义的bean中的某个属性的类型不在上面的常用配置中的话,才需要我们进行个性化属性编辑器的注册。

添加 ApplicationContextAwareProcessor 处理器

了解了属性编辑器的使用后,接下来我们继续通过 AbstractApplicationContext prepareBeanFactory 方法的主线来进行函数跟踪。对于beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this))其实主要目的就是注册个 BneaPostProcessor,而真正的逻辑还是在 ApplicationContextAwareProcessor 中。

ApplicationContextAwareProcessor 实现 BeanPostProcessor 接口,我们回顾下之前讲过的内容,在bean实例化的时候,也就是Spring激活bean的init-method的前后会调用BeanPostProcessor 的 postProcessBeforelnitialization 方法和 postProcessAfterlnitialization 方法

问样,对于ApplicationContextAwareProcessor我们也关心这两个方法。
对于postProcessAfterlnitialization 方法,在ApplicationContextAwareProcessor 中并没有做过多逻辑处理。

  1. @Override
  2. public Object postProcessAfterInitialization(Object bean, String beanName) {
  3. return bean;
  4. }

那么,我们重点看一下postProcessBeforelnitialization 方法。

  1. @Override
  2. @Nullable
  3. public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
  4. AccessControlContext acc = null;
  5. if (System.getSecurityManager() != null &&
  6. (bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
  7. bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
  8. bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
  9. acc = this.applicationContext.getBeanFactory().getAccessControlContext();
  10. }
  11. if (acc != null) {
  12. AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
  13. invokeAwareInterfaces(bean);
  14. return null;
  15. }, acc);
  16. }
  17. else {
  18. invokeAwareInterfaces(bean);
  19. }
  20. return bean;
  21. }
  22. private void invokeAwareInterfaces(Object bean) {
  23. if (bean instanceof Aware) {
  24. if (bean instanceof EnvironmentAware) {
  25. ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
  26. }
  27. if (bean instanceof EmbeddedValueResolverAware) {
  28. ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
  29. }
  30. if (bean instanceof ResourceLoaderAware) {
  31. ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
  32. }
  33. if (bean instanceof ApplicationEventPublisherAware) {
  34. ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
  35. }
  36. if (bean instanceof MessageSourceAware) {
  37. ((MessageSourceAware) bean).setMessageSource(this.applicationContext);
  38. }
  39. if (bean instanceof ApplicationContextAware) {
  40. ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
  41. }
  42. }
  43. }

postProcessBeforelnitialization 方法中调用了invokeAwareInterfaces。从invokeAwareInterfaces方法中,我们或许已经或多或少了解了Spring的用意,实现这些Aware接口的bean在被初始化之后,可以取得一些对应的资源。

设置忽略依赖

当 Spring 将 ApplicationContextAwareProcessor 注册后,那么在 invokeAwarelnterfaces 方法中间接调用的 Aware 类已经不是普通的 bean 了,如 ResourceLoaderAware、ApplicationEventPublisher Aware 等,那么当然需要在Spring做bean的依赖注入的时候忽略它们。而ignoreDependencylnterfece的作用正是在此。

  1. // 设置了几个忽略自动装配的接口
  2. beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
  3. beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
  4. beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
  5. beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
  6. beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
  7. beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

注册依赖

Spring中有了忽略依赖的功能,当然也必不可少地会有注册依赖的功能。

  1. // 设置了几个自动装配的特殊规则
  2. beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
  3. beanFactory.registerResolvableDependency(ResourceLoader.class, this);
  4. beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
  5. beanFactory.registerResolvableDependency(ApplicationContext.class, this);

当注册了依赖解析后,例如当注册了对BeanFactory.class的解析依赖后,当bean的属性注入的时候,一旦检测到属性为BeanFactory类型便会将beanFactory的实例注人进去。