Spring容器就像一台构造精妙的机器,我们通过配置文件向机器传达控制信息,机器就能够按照设定的模式工作。如果将Spring 容器比作一辆汽车,那么可以将BeanFactory看成汽车的发动机,而ApplicationContext则是一辆完整的汽车,它不但包括发动机,还包括离合器、变速器及底盘、车身、电气设备等其他组件。在ApplicationContext内,各个组件按部就班、有条不紊地完成汽车的各项功能。
6.1.1内部工作机制
Spring 的 AbstractApplicationContext是 ApplicationContext的抽象实现类,该抽象类的refresh()方法定义了Spring容器在加载配置文件后的各项处理过程,这些处理过程清晰地刻画了Spring容器启动时所执行的各项操作。下面来看一下refresh()内部定义了哪些执行逻辑,如代码所示。
(1)初始化 BeanFactory:根据配置文件实例化 BeanFactory,在 obtainFreshBeanFactory()方法中,首先调用refreshBeanFactory()方法刷新 BeanFactory,然后调用getBeanFactory()方法获取 BeanFactory,这两个方法都是由具体子类实现的。在这一步里,Spring 将配置文件的信息装入容器的Bean定义注册表(BeanDefinitionRegistry)中,但此时 Bean还未初始化。
(2)调用工厂后处理器:根据反射机制从 BeanDefinitionRegistry中找出所有实现了BeanFactoryPostProcessor接口的 Bean,并调用其postProcessBeanFactory()接口方法。
(3)注册 Bean后处理器:根据反射机制从 BeanDefinitionRegistry中找出所有实现了BeanPostProcessor接口的 Bean,并将它们注册到容器Bean后处理器的注册表中。
(4)初始化消息源:初始化容器的国际化消息资源
(5)初始化应用上下文事件广播器。
(6)初始化其他特殊的 Bean:这是一个钩子方法,子类可以借助这个方法执行一些特殊的操作,如 AbstractRefreshableWebApplicationContext就使用该方法执行初始化ThemeSource的操作。
(7)注册事件监听器。
(8)初始化所有单实例的 Bean,使用懒加载模式的Bean 除外:初始化 Bean后,将它们放入 Spring容器的缓存池中。
6.1.2BeanDefinition
Spring通过BeanDefinition将配置文件中的<bean>配置信息转换为容器的内部表示,并将这些 BeanDefiniton注册到 BeanDefinitonRegistry 中。Spring容器的BeanDefinitionRegistry就像Spring配置信息的内存数据库,后续操作直接从BeanDefinitionRegistry中读取配置信息。一般情况下,BeanDefinition只在容器启动时加载并解析,除非容器刷新或重启,这些信息不会发生变化。当然,如果用户有特殊的需求,也可以通过编程的方式在运行期调整BeanDefinition的定义。
创建最终的 BeanDefinition主要包括两个步骤。
(1)利用 BeanDefinitionReader读取承载配置信息的Resource,通过XML解析器解析配置信息的DOM对象,简单地为每个<bean>生成对应的 BeanDefinition对象。但是这里生成的 BeanDefinition可能是半成品,因为在配置文件中,可能通过占位符变量引用外部属性文件的属性,这些占位符变量在这一步里还没有被解析出来。
(2)利用容器中注册的 BeanFactoryPostProcessor对半成品的BeanDefinition进行加工处理,将以占位符表示的配置解析为最终的实际值,这样半成品的 BeanDefinition就成为成品的 BeanDefinition.
6.1.3InstantiationStrategy
InstantiationStrategy仅负责实例化 Bean的操作,相当于执行Java语言中new 的功能,它并不会参与Bean属性的设置工作。所以由InstantiationStrategy返回的Bean 实例实际上是一个半成品的 Bean实例,属性填充的工作留待BeanWrapper来完成。
6.1.4BeanWrapper
要顺利地填充 Bean属性,除了目标 Bean实例和属性编辑器外,还需要获取 Bean对应的 BeanDefinition,它从 Spring容器的 BeanDefinitionRegistry中直接获取。Spring主控程序从 BeanDefinition中获取 Bean属性的配置信息PropertyValue,并使用属性编辑器对PropertyValue进行转换以得到 Bean的属性值。对 Bean 的其他属性重复这样的步骤,就可以完成 Bean所有属性的注入工作。BeanWrapperImpl在内部使用Spring 的BeanUtils 工具类对Bean进行反射操作,设置属性。下一节将详细介绍属性编辑器的原理,并讲解如何通过配置的方式注册自定义的属性编辑器。
6.2属性编辑器
在Spring 配置文件里,往往通过字面值为 Bean各种类型的属性提供设置值:不管是double类型还是 int类型,在配置文件中都对应字符串类型的字面值。BeanWrapper在填充Bean属性时如何将这个字面值正确地转换为对应的double或int等内部类型呢?我们可以隐约地感觉到一定有一个转换器在“暗中相助”,这个转换器就是属性编辑器。
“属性编辑器”这个名字可能会让人误以为是一个带用户界面的输入器,其实属性编辑器不一定非得有用户界面,任何实现java.beans.PropertyEditor接口的类都是属性编辑器。属性编辑器的主要功能就是将外部的设置值转换为JVM内部的对应类型,所以属性编辑器其实就是一个类型转换器。
PropertyEditor是 JavaBean规范定义的接口,JavaBean规范中还有其他一些PropertyEditor配置的接口。为了彻底地理解属性编辑器,必须对JavaBean中有关属性编辑器的规范进行学习,相信这些知识对学习和掌握Spring中的属性编辑器会大有帮助。
在基于XML的配置文件中,通过“${propName}”形式引用属性值。类似的,基于注解配置的 Bean可以通过@Value注解为 Bean 的成员变量或方法入参自动注入容器已有的属性,如下:
@Value注解可以为 Bean注入一个字面值,也可以通过@Value("S propName}")的形式根据属性名注入属性值。由于标注@Configuration的类本身就相当标注了@Component,所以在标注@Configuration的类中引用属性的方式和基于注解配置的引用方式是完全一样的,此处不再赘述。使用@Value注解来引用属性值带来很大的便利,但在使用过程中,一定要确保所引用的属性值在属性文件中已经存在且数值匹配,否则会造成Bean创建错误,引发意想不到的异常。
这里放一个解释@Autowired和@Value的博客,我看到这里的时候是对这个问题产生了疑惑的:
https://www.cnblogs.com/yichunguo/p/12110755.html
Spring既允许在 Bean定义中通过$ {propName}引用属性值,也允许在属性文件中使用${propName}实现属性之间的相互引用。
参考:
https://www.cnblogs.com/yichunguo/p/12110755.html
原文:https://www.cnblogs.com/hellostranger/p/14000238.html