https://www.jianshu.com/p/5489e25744a9
https://www.cnblogs.com/warehouse/p/9385110.html
概述
使用 Spring 时,XML 和注解是使用得最多的两种配置方式,虽然是两种完全不同的配置方式,但对于 IOC 容器来说,两种方式的不同主要是在 BeanDefinitionReader 读取不同的配置,解析成 BeanDefinition 对象。而对于核心的容器启动流程,仍然是一致的。
主程序入口
XmlUserTest 测试类
public class XmlUserTest {public static void main(String[] args) {ApplicationContext ac = new ClassPathXmlApplicationContext("spring-config.xml");User user = ac.getBean("user", User.class);System.out.println(user.toString());}}
spring-config.xml 配置
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="user" class="com.zlp.spring.init.entity.User"><property name="age" value="10"/><property name="userName" value="smile"/></bean></beans>
User 实体类
/*** User实体类* @date: 2021/2/24 17:47*/@Lazy(value = false)public class User {private String userName;private Integer age;public User(){System.out.println("空参构造方法");}public void setUserName(String userName) {this.userName = userName;}public void setAge(Integer age) {this.age = age;}@Overridepublic String toString() {return "User{" +"userName='" + userName + '\'' +", age=" + age +'}';}}
源码调试详解
前期准备环境工作做好,下面进入正题,开始 debug 调试Spring Ioc 启动流程分析。
ClassPathXmlApplicationContext
/*** 创建一个新的 ClassPathXmlApplicationContext,从给定的XML文件加载定义并自动刷新上下文* @param configLocation resource location* @throws BeansException if context creation failed*/public ClassPathXmlApplicationContext(String configLocation) throws BeansException {// 本类构造函数this(new String[] {configLocation}, true, null);}/*** 使用给定的父级创建新的ClassPathXmlApplicationContext,从给定的XML文件加载定义。* @param 资源位置数组* @param 是否自动刷新上下文,加载所有bean定义并创建所有单例。或者,在进一步配置上下文后手动调用refresh。* Alternatively, call refresh manually after further configuring the context.* @param parent the parent context* @throws BeansException if context creation failed* @see #refresh()*/public ClassPathXmlApplicationContext(String[] configLocations,boolean refresh,@Nullable ApplicationContext parent) throws BeansException {/*** 1.调用父类构造函数完成一些资源的初始化:* 1.1 初始化默认的资源文件解析器,默认为:PathMatchingResourcePatternResolver.* 1.2 初始化默认的类加载器.*/super(parent);// 2.解析配置文件的路径,例如:applicationContext-${user.name}.xml这种配置文件名称,并且保存到configLocations中,为下一步的解析bean定义做准备setConfigLocations(configLocations);// 3.如果需要刷新容器if (refresh) {// 执行刷新容器的操作refresh();}}
细节部分:
- String [] configLocations :可以是一个数组,可以加载多个配置文件
- ApplicationContext parent :默认是空(null)
- super(parent) : 当前类继承了 ClassPathXmlApplicationContext 父类时,可以自动义资源初始化配置操作
refresh()
总共有 13 个方法
@Overridepublic void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {/*** 1.准备上下文的刷新工作,记录bean容器的启动时间,容器活跃状态* 验证系统中一些属性和属性值的设置等.* 使用LinkedHashSet初始化earlyApplicationListeners和earlyApplicationEvents*/prepareRefresh();/*** 2.获取Bean工厂,期间会做解析和加载bean定义的一些列工作.生成BeanDefinition对象.* 此处返回的beanFactory的真实类型为:DefaultListableBeanFactory*** 自定义的xsd约束文件也会在该步骤进行解析,通过实现BeanDefinitionParser接口,并实现parse方法* 解析自定义标签时通过实现NamespaceHandlerSupport接口,并实现init方法进行实现*/ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();/*** 3.bean工厂的初始化准备工作,设置bean工厂的一些属性* 比如:创建bean工厂时,需要忽略哪些接口,需要注册哪些bean,需要设置哪些Bean的后置处理器等.** 例如常用的:ApplicationContextAwareBeanPostProcessor, ApplicationListenerDetector** 此外,注册一些和环境相关的bean单实例bean.*/prepareBeanFactory(beanFactory);try {/*** 4.Bean定义加载完毕之后实现,目前方法为空实现,留给开发人员进行自定义扩展。* 和BeanFactoryPostProcessor中的方法postProcessBeanFactory相同** 该方法在Bean定义加载完毕之后,Bean实例化之前会执行* 比如在BeanFactory加载完所有的Bean定义之后,想要修改某个bean的定义信息,可以通过重写这个方法实现.* 比如:在xml中配置了<bean id="user"><property name="name" value="wb"></property></bean>* 如果想在不修改配置文件的情况下修改name的值,可以使用如下的方法:* class MyApplicationContext extends ClassPathXmlApplicationContext{public MyApplicationContext(String s){super(s);}@Overrideprotected void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {BeanDefinition beanDefinition = beanFactory.getBeanDefinition("user");PropertyValue propertyValue=new PropertyValue("name", "www.so.com");beanDefinition.getPropertyValues().addPropertyValue(propertyValue);}*/postProcessBeanFactory(beanFactory);/*** 5.执行beanFactory的后置处理器** 先执行BeanDefinitionRegistryPostProcessor接口的实现类的postProcessBeanDefinitionRegistry方法,* 执行过程中,也是先执行实现了优先级接口PriorityOrdered的BeanDefinitionRegistryPostProcessor的postProcessBeanDefinitionRegistry方法* 然后执行实现了Ordered接口的...* 最后执行未实现PriorityOrdered接口和Ordered接口的...** 然后执行BeanFactoryPostProcessor接口的实现类的postProcessBeanFactory方法* 执行过程中,也是先执行实现了优先级接口PriorityOrdered的BeanFactoryPostProcessor的postProcessBeanFactory方法* 然后执行实现了Ordered接口的...* 最后执行未实现PriorityOrdered接口和Ordered接口的...** 其中也涉及到了排序过程*** 配置类中的Selector类型的组件和@Component,@ComponentScan中的元数据信息也会在该步骤中进行解析* 还包括执行条件注解@Condition的回调逻辑*** ImportBeanDefinitionRegistrar对应的registerBeanDefinitions方法也会在该步骤中调用,给容器中注册自定义的组件.*/invokeBeanFactoryPostProcessors(beanFactory);/*** 6.注册所有bean的后置处理器.用来拦截Bean的创建** 注册所有实现了BeanPostProcessor接口的后置处理器* 执行过程中,也是先执行实现了优先级接口PriorityOrdered接口的BeanPostProcessor的addBeanPostProcessor方法* 然后执行实现了Ordered接口的...* 最后执行未实现PriorityOrdered接口和Ordered接口的...** 其中也涉及到了排序过程*/registerBeanPostProcessors(beanFactory);/*** 7.初始化消息源* 用来做国际化,消息绑定,消息解析等功能* 一般在SpringMVC中会使用到.*/initMessageSource();/*** 8.初始化事件派发器,用来发布事件* 如果容器中有类型为ApplicationEventMulticaster的派发器组件,则直接获取使用* 如果容器中没有,则默认创建一个类型为SimpleApplicationEventMulticaster的派发器,供容器派发事件使用*/initApplicationEventMulticaster();/*** 9.用来初始化一些特殊的Bean,目前默认是空方法,未实现,可以通过继承AbstractApplicationContext类,* 然后覆写该方法进行自定义特殊bean的初始化.** 比如:AbstractRefreshableWebApplicationContext中onRefresh方法用来初始化主题能力.** SpringBoot也是在改步骤中启动内嵌Tomcat容器的*/onRefresh();/*** 10.注册监听器* 将监听器绑定到广播器上,将监听器对应的beanName绑定到到第8步初始化的事件派发器中,* 如果之前有发布的事件,则直接通过事件派发器将事件派发出去.*/registerListeners();/*** 11.初始化所有剩余的单实例Bean(没有使用懒加载的Bean).整个Spring IOC的核心.** 包括执行@PostConstruct标注的方法.** 注意:SpringMVC的父子容器创建Bean的过程:* SpringMVC中,存在着父容器和子容器。当父容器启动之后,会通过该方法将所有的Dao和Service对应的Bean创建出来,保存到beanFactory的单例缓存容器中* 当子容器启动之后,也会通过该方法将所有的Controller,viewResolver,HandlerMapping对应的Bean创建出来,然后放入到beanFactory的单例缓存容器中.*/finishBeanFactoryInitialization(beanFactory);/** 12.发布事件。例如容器中的刷新事件:ContextRefreshedEvent就是在这一步中发布. SpringCloud在该步骤中会启动web服务 */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.// 清空单实例bean对应的map及缓存destroyBeans();// 设置容器的活跃状态为falsecancelRefresh(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();}}}
prepareRefresh()
该方法很简单,主要做前期准备工作
- 记录容器的启动时间
- 设置容器的关闭状态closed(false)和激活状态active(true)
- initPropertySources,初始化容器中的一些校验规则,比如字段的校验;使用getEnvironment().setRequiredProperties()进行设置
- 校验容器中的一些必要属性,如果第(3)步中设置了容器中的必要属性,该步骤会对第三步设置的值进行校验
声明容器中早期的应用监听器以及容器中早期的应用事件
/*** 准备此上下文以进行刷新,设置其启动日期和活动标志,以及执行属性源的任何初始化。*/protected void prepareRefresh() {// 记录容器的启动时间.this.startupDate = System.currentTimeMillis();// 记录容器未关闭this.closed.set(false);// 记录容器状态为激活状态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./*** 如果需要在验证系统属性之前,给系统中设置一些默认值。可以通过继承AbstractApplicationContext类,并重写该方法实现。* Spring留给开发人员的一个扩展点.** 例如子类在方法中设置环境属性中必须需要的变量:getEnvironment().setRequiredProperties("myProp");*/initPropertySources();// Validate that all properties marked as required are resolvable:// see ConfigurablePropertyResolver#setRequiredProperties/*** 作用:校验设置的必须属性是否能够在系统环境中找到对应的值** 如果在initPropertySources方法中使用getEnvironment().setRequiredProperties(String... keys)设置了必须的属性,而通过this.getProperty(key)* 没有从系统环境中获取到属性的值,则会抛出MissingRequiredPropertiesException异常*/getEnvironment().validateRequiredProperties();// Store pre-refresh ApplicationListeners...if (this.earlyApplicationListeners == null) {/** 在SpringBoot中会有大量的初始化监听器,用于初始化使用 */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<>();}
网上有人说其实这个函数没什么用,因为最后两句代码才是最为关键的,但是却没有什么逻辑处理,initPropertySources 是空的,没有任何逻辑,而 getEnvironment().validateRequiredProperties 也因为没有需要验证的属性而没有做任何处理。其实这都是因为没有彻底理解才会这么说,这个函数如果用好了作用还是挺大的。那么,该怎么用呢?我们先探索下各个函数的作用。
(1)initPropertySources 正符合 Spring 的开放式结构设计,给用户最大扩展 Spring 的能力。用户可以根据自身的需要重写 initPropertySources 方法,并在方法中进行个性化的属性处理及设置。
(2)validateRequiredProperties 则是对属性进行验证,那么如何验证呢?我们举个融合两句代码的小例子来帮助大家理解。
假如现在有这样一个需求,工程在运行过程中用到的某个设置(例如 VAR )是从系统环境变量中取得的,而如果用户没有在系统环境变量中配置这个参数,那么工程可能不会工作。这一要求可能会有各种各样的解决办法,当然,在 Spring 中可以这样做,你可以直接修改 Spring 的源码,例如修改 ClassPathXmlApplicationContext 。当然,最好的办法还是对源码进行扩展,我们可以自定义
public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {public MyClassPathXmlApplicationContext(String... configLocations) throws BeansException {super(configLocations);}@Overrideprotected void initPropertySources() {// 添加验证要求getEnvironment().setRequiredProperties("VAR");}}
我们自定一义了继承自 ClassPathXmlApplicationContext 的 MyClassPathXmlApplicationContext , 并重写了 initPropertySources 方法,在方法中添加了我们的个性化需求,那么在验证的时候也就是程序走到 getEnvironment().validateRequiredProperties() 代码的时候,如果系统并没有检测到对应 VAR 的环境变量,那么将抛出异常。当然我们还需要在使用的时候替换掉原有的 ClassPathXmlApplicationContext:
public static void main(String[] args) {ApplicationContext ac = new MyClassPathXmlApplicationContext("spring-config.xml");User user = (User) ac.getBean("user");System.out.println(user.toString());}
obtainFreshBeanFactory()
obtainFreshBeanFactory 方法从字面上理解是获取 BeanFactory。之前有说过,ApplicationContext 是对BeanFactory 的功能上的扩展,不但包含了BeanFactory的全部功能更在其基础上添加了大量的扩展功能,那么obtainFreshBeanFactory 正是实现BeanFactory的地方,也就是经过了这个函数后 ApplicationContext 就已经拥有了 BeanFactory 的全部功能。
/*** 告诉子类刷新内部bean工厂* @return the fresh BeanFactory instance* @see #refreshBeanFactory()* @see #getBeanFactory()*/protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {/*** 刷新bean工厂,判断bean工厂是否已经存在,如果存在需要进行销毁和关闭* 默认调用的是AbstractRefreshableApplicationContext的refreshBeanFactory方法.* 刷新Bean工厂时会进行bean定义的加载操作。*/refreshBeanFactory();return getBeanFactory();}
方法中将核心实现委托给了 refreshBeanFactory
/*** This implementation performs an actual refresh of this context's underlying* bean factory, shutting down the previous bean factory (if any) and* initializing a fresh bean factory for the next phase of the context's lifecycle.*/@Overrideprotected final void refreshBeanFactory() throws BeansException {// 判断bean工厂是否存在,如果存在需要先销毁和关闭。否则会出现问题if (hasBeanFactory()) {// 销毁bean,根据bean的名称将bean工厂中的所有bean都从map中移除destroyBeans();// 关闭bean工厂,设置Bean工厂的序列化id为null,并将beanFactory的值赋值为nullcloseBeanFactory();}try {// 重新创建bean工厂,默认返回的是Bean工厂类型是:DefaultListableBeanFactoryDefaultListableBeanFactory beanFactory = createBeanFactory();// 设置序列化ID,ID: class名称 + "@" + 对象的十六进制值beanFactory.setSerializationId(getId());/** 自定义Bean工厂* 设置两个属性:* 1. 是否允许覆盖同名称的不同定义的对象* 2. 是否允许bean之间存在循环依赖*/customizeBeanFactory(beanFactory);// 解析并加载bean的定义,默认是通过AbstractXmlApplicationContext类中的loadBeanDefinitions实现loadBeanDefinitions(beanFactory);synchronized (this.beanFactoryMonitor) {this.beanFactory = beanFactory;}}catch (IOException ex) {throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);}}
我们详细分析上面的每个步骤。
- 创建 DefaultListableBeanFactory。
- 在介绍BeanFactory 的时候,不知道读者是否还有印象,声明方式为:ApplicationContext ac = new ClassPathXmlApplicationContext(“spring-config.xml”); 并 XmlBeanDefinitionReader 提供了读取类型的 reader 属性也就是说 DefaultListableBeanFactory 是容器的基础。必须首先实例化,那么在这里就是实例化DefaultListableBeanFactory 的步骤。
- 指定序列化ID。
- 定制BeanFactory。
- 加载BeanDefinition。
- 使用全局变量记录BeanFactory类实例。
因为DefaultListableBeanFactory类型的变量beanFactory是函数内的局部变量,所以要使用全局变量记录解析结果。
定制化 BeanFactory
/*** Customize the internal bean factory used by this context.* Called for each {@link #refresh()} attempt.* <p>The default implementation applies this context's* {@linkplain #setAllowBeanDefinitionOverriding "allowBeanDefinitionOverriding"}* and {@linkplain #setAllowCircularReferences "allowCircularReferences"} settings,* if specified. Can be overridden in subclasses to customize any of* {@link DefaultListableBeanFactory}'s settings.* @param beanFactory the newly created bean factory for this context* @see DefaultListableBeanFactory#setAllowBeanDefinitionOverriding* @see DefaultListableBeanFactory#setAllowCircularReferences* @see DefaultListableBeanFactory#setAllowRawInjectionDespiteWrapping* @see DefaultListableBeanFactory#setAllowEagerClassLoading*/protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {// 如果属性allowBeanDefinitionOverriding不为空,设置给beanFactory对象相应属性// 此属性的含义:是否允许覆盖同名称的不同定义的对象if (this.allowBeanDefinitionOverriding != null) {beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);}// 如果属性allowCircularReferences不为空,设置给beanFactory对象相应属性// 此属性的含义:是否允许bean之间存在循环依赖if (this.allowCircularReferences != null) {beanFactory.setAllowCircularReferences(this.allowCircularReferences);}}
是否允许覆盖和允许依赖的设置这里这是判断了是否为空,如果不为空则进行设置,但是并没有看到在哪里进行设置,究竟这个设置是在哪里进行设置的呢?还是那句话,使用子类覆盖方法,例如:
public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext {......@Overrideprotected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {super.setAllowBeanDefinitionOverriding(false);super.setAllowCircularReferences(false);super.customizeBeanFactory(beanFactory);}}
加载 BeanDefinition
在第一步中提到了 ClassPathXmlApplicationContext 与 XmlBeanFactory 创建的对比,在实现配置文件的加载功能中除了我们在第一步中已经初始化的 DefaultListableBeanFactory外,还需要 XmlBeanDefinitionReader 来读取XML,那么在这个步骤中首先要做的就是初始化 XmlBeanDefinitionReader。
/*** Loads the bean definitions via an XmlBeanDefinitionReader.* @see org.springframework.beans.factory.xml.XmlBeanDefinitionReader* @see #initBeanDefinitionReader* @see #loadBeanDefinitions*/@Overrideprotected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {// Create a new XmlBeanDefinitionReader for the given BeanFactory.// 为指定 beanFactory 创建 XmlBeanDefinitionReaderXmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);// Configure the bean definition reader with this context's// resource loading environment.// 对beanDefinitionReader进行环境变量的设置beanDefinitionReader.setEnvironment(this.getEnvironment());beanDefinitionReader.setResourceLoader(this);beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));// Allow a subclass to provide custom initialization of the reader,// then proceed with actually loading the bean definitions.// 对beanDefinitionReader进行设置,可以覆盖initBeanDefinitionReader(beanDefinitionReader);loadBeanDefinitions(beanDefinitionReader);}
在初始化了 DefaultListableBeanFactory 和 XmlBeanDefinitionReader 后就可以进行配置文件的读取了。
/*** Load the bean definitions with the given XmlBeanDefinitionReader.* <p>The lifecycle of the bean factory is handled by the {@link #refreshBeanFactory}* method; hence this method is just supposed to load and/or register bean definitions.* @param reader the XmlBeanDefinitionReader to use* @throws BeansException in case of bean registration errors* @throws IOException if the required XML document isn't found* @see #refreshBeanFactory* @see #getConfigLocations* @see #getResources* @see #getResourcePatternResolver*/protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {Resource[] configResources = getConfigResources();if (configResources != null) {reader.loadBeanDefinitions(configResources);}String[] configLocations = getConfigLocations();if (configLocations != null) {reader.loadBeanDefinitions(configLocations);}}
使用 XmlBeanDefinitionReader 的 loadBeanDefinitions 方法进行配置文件的加载注册相信大家已经不陌生,这完全就是开始 BeanFactory 的套路。因为在XmlBeanDefinitionReader 中已经将之前初始化的DefaultListableBeanFactory 注册进去了,所有 XmlBeanDefinitionReader 所读取的 BeanDefinitionHolder 都会注册到 DefaultListableBeanFactory 中,也就是经过此步骤,类型 DefaultListableBeanFactory 的变量beanFactory 已经包含了所有解析好的配置。
prepareBeanFactory
在进入 prepareBeanFactory 前,Spring 已经完成了对配置的解析,而ApplicationContext在功能上的扩展也由此展开。
/*** 配置工厂的标准上下文特征,例如上下文的类加载器和后处理器* @param beanFactory the BeanFactory to configure*/protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {// Tell the internal bean factory to use the context's class loader etc.beanFactory.setBeanClassLoader(getClassLoader());// 设置SPEL表达式解析器,用来支持Spring的SPEL表达式beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));// 添加属性编辑注册器。例如一个字符串类型的地址需要转换为一个Address对象,可以使用该功能.// 可参考示例:spring-source-study模块下的com.wb.spring.propertyeditor包下的示例程序beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));// 添加bean的后置处理器。此处添加的是Spring自己的后置处理器,用来回调bean所实现的aware接口中的方法.beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));// 下面的ignoreDependencyInterface是用来设置bean工厂中需要忽略的接口// 可以通过实现EnvironmentAware接口来获取到当前的环境信息Environment。/*** 如果将EnvironmentAware接口添加到ignoreDependencyInterface中,则在使用的地方通过@Autowired将会无法正常注入* 而是需要通过setEnvironment方法进行注入,下面的其他接口都类似.*/beanFactory.ignoreDependencyInterface(EnvironmentAware.class);// 可以通过实现EmbeddedValueResolverAware接口来获取String类型值的解析器beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);// 资源加载器,例如使用:@Autowired ResourceLoaderAware aware; 将不会被注入beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);// 事件发布器beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);// 消息资源beanFactory.ignoreDependencyInterface(MessageSourceAware.class);// 应用的上下文信息beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);// 注册一些可以自动装配的接口。 当类型为dependencyType时, 注入autowiredValue。为了解决一个类型有多个子类实现时,优先注入那个子类实现的问题。// 例如下面第一个,当注入类型为BeanFactory时,注入的值为beanFactory,默认为DefaultListableBeanFactorybeanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);// 当注入类型为ResourceLoader时,注入的值为ApplicationContextbeanFactory.registerResolvableDependency(ResourceLoader.class, this);// 当注入类型为ApplicationEventPublisher时,注入的值为ApplicationContextbeanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);// 当注入的类型为ApplicationContext时,注入的值为ApplicationContext.beanFactory.registerResolvableDependency(ApplicationContext.class, this);// 增加一个bean的后置处理器,ApplicationListenerDetector// Register early post-processor for detecting inner beans as ApplicationListeners.beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));// Detect a LoadTimeWeaver and prepare for weaving, if found.// 如果bean工厂中存在着名称为loadTimeWeaver的bean定义,则给bean工厂中加入LoaderTimeWeaverAwareProcessor后置处理器// 补充:1、增加对AspectJ的支持,在Java中织入分为3种:(1) 编译期织入; (2) 类加载期织入; (3) 运行期织入。编译期织入指的是在java编译期,// 采用特殊的编译器,将切面织入到java类中;类加载期织入则指的是通过特殊的类加载器,在字节码加载到JVM时,织入切面;运行期织入则是// 通过采用cglib或者jdk进行切面的织入。// 2、aspectJ中提供了两种方式:// (1) 通过特殊编译器,在编译期,将aspectJ语言编写的切面类织入到java类中;// (2) 类加载期织入,就是通过下面的LoadTimeWeavingif (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()));}// 给容器中注册一些与运行环境相关的单实例Beanif (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {// beanName: environment,直接将new出来的Spring内部对象放入到Spring的单实例缓存池中.beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());}if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {// beanName: systemProperties 方法:System.getProperties();beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());}if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {// beanName: systemEnvironment, 方法:System.getEnv();beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());}}
上面函数中主要进行了几个方面的扩展。
- 增加对 SPEL 语言的支持。
- 增加对属性编辑器的支持。
- 增加对一些内置类,比如 EnvironmentAvare、MessageSourceAware 的信息注入。
- 设置了依赖功能可忽略的接口。
- 注册一些固定依赖的属性。
- 增加对AspectJ的支持。
将相关环境变量及属性注册以单例模式注册。
可能读者不是很理解每个步骤的具体含义,接下来我们会对各个步骤进行详细地分析。
增加SPEL语言的支持
Spring 表达式语言全称为 “Spring Expression Language”,缩写为 “SpEL”,类似于Struts 2x 中使用的OGNL表达式语言,能在运行时构建复杂表达式、存取对象图属性、对象方法调用等, 并且能与Spring功能完美整合,比如能用 来配置bean定义。SpEL是单独模块,只依赖于core 模块,不依赖于其他模块,可以单独使用。
SpEL使用#{…}为定界符,所有在花括号中的字符都将被认为是SpEL,使用格式如下:
<bean id = "saxophone" value = "com.xxx.xxx.Xxx"/><bean ><property name="instrument" value="#{saxophone}"/><bean/>
相当于:
<bean id = "saxophone" value = "com.xxx.xxx.Xxx"/><bean ><property name="instrument" ref="saxophone"/><bean/>
当然,上面只是列举了其中最简单的使用方式,SPEL功能非常强大,使用好可以大大提高开发效率,这里只为唤起读者的记忆来帮助我们理解源码,有兴趣的读者可以进一步深入研究。
在源码中通过代码 beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver()) 注册语言解析器,就可以对SPEL进行解析了,那么在注册解析器后 Spring 又是在什么时候调用这个解析器进行解析呢?
之前我们讲解过 Spring 在 bean 进行初始化的时候会有属性填充的一步,而在这一步中 Spring 会调用AbstractAutowireCapableBeanFactory 类的 applyPropertyValues 函数来完成功能。就在这个函数中,会通过构造 BeanDefinitionValueResolver 类型实例 valueResolver 来进行属性值的解析。同时,也是在这个步骤中一般通过 AbstractBeanFactory中的 evaluateBeanDefinitionString 方法去完成 SPEL 的解析。
/*** Evaluate the given String as contained in a bean definition,* potentially resolving it as an expression.* @param value the value to check* @param beanDefinition the bean definition that the value comes from* @return the resolved value* @see #setBeanExpressionResolver*/@Nullableprotected Object evaluateBeanDefinitionString(@Nullable String value, @Nullable BeanDefinition beanDefinition) {if (this.beanExpressionResolver == null) {return value;}Scope scope = null;if (beanDefinition != null) {String scopeName = beanDefinition.getScope();if (scopeName != null) {scope = getRegisteredScope(scopeName);}}return this.beanExpressionResolver.evaluate(value, new BeanExpressionContext(this, scope));}
当调用这个方法时会判断是否存在语言解析器,如果存在则调用语言解析器的方法进行解析,解析的过程是在Spring的 expression 的包内,这里不做过多解释。我们通过查看对evaluateBeanDefinitionString方法的调用层次可以看出,应用语言解析器的调用主要是在解析依赖注入bean的时候,以及在完成bean的初始化和属性获取后进行属性填充的时候。
增加属性注册编辑器
在Spring DI注入的时候可以把普通属性注入进来,但是像Date类型就无法被识别。例如:
public class UserManager {private Date dateValue;public Date getDateValue() {return dateValue;}public void setDateValue(Date dateValue) {this.dateValue = dateValue;}@Overridepublic String toString() {return "dataValue: " + dateValue;}}
上面代码中,需要对日期型属性进行注入:
<?xml version="1.0" encoding="UTF-8"?><beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><bean id="userManager" class="org.cellphone.uc.UserManager"><property name="dateValue"><value>2018-07-29</value></property></bean></beans>
测试代码:
public static void main(String[] args) {ApplicationContext context = new ClassPathXmlApplicationContext("spring/beans-test.xml");UserManager manager = (UserManager) context.getBean("userManager");System.out.println(manager);}
如果直接这样使用,程序则会报异常,类型转换不成功。因为在UserManager中的dataValue属性是Date类型的,而在XML中配置的确实String类型的,所以当然会报异常。
Spring针对此问题提供了两种解决方法。
使用自定义属性编辑器
使用自定义属性编辑器,通过继承PropertyEditorSupport,重写setAsText方法,具体步骤如下。
(1)编写自定义的属性编辑器。
public class DatePropertyEditor extends PropertyEditorSupport {private String format = "yyyy-MM-dd";public void setFormat(String format) {this.format = format;}@Overridepublic void setAsText(String text) throws IllegalArgumentException {System.out.println("text: " + text);SimpleDateFormat sdf = new SimpleDateFormat(format);try {Date d = sdf.parse(text);this.setValue(d);} catch (ParseException e) {e.printStackTrace();}}}
(2)将自定义属性编辑器注册到Spring中。
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer"><property name="customEditors"><map><entry key="java.util.Date" value="org.cellphone.uc.DatePropertyEditor"/></map></property></bean>
在配置文件中引入类型为org.springframework.beans.factory.config.CustomEditorConfigurer的bean,并在属性customEditors中加入自定义的属性编辑器,其中key为属性编辑器所对应的类型。通过这样的配置,当Spring在注入bean的属性时一旦遇到了java.util.Date类型的属性会自动调用自定义的DatePropertyEditor解析器进行解析,并用解析结果代替配置属性进行注入。
注册Spring自带的属性编辑器 CustomDateEditor
通过注册Spring自带的属性编辑器CustomDateEditor,具体步骤如下:
(1)定义属性编辑器。
public class DatePropertyEditorRegistrar implements PropertyEditorRegistrar {@Overridepublic void registerCustomEditors(PropertyEditorRegistry registry) {registry.registerCustomEditor(Date.class, new CustomDateEditor(new SimpleDateFormat("yyyy-MM-dd"), true));}}
(2)注册到Spring中。
<bean class="org.springframework.beans.factory.config.CustomEditorConfigurer"><property name="propertyEditorRegistrars"><list><bean class="org.cellphone.uc.DatePropertyEditorRegistrar"></bean></list></property></bean>
通过在配置文件中将自定义的DatePropertyEditorRegistrar注册进入org.springframework.beans.factory.config.CustomEditorConfigurer 的 propertyEditorRegistrars 属性中,可以具有与方法 1 同样的效果。
我们了解了自定义属性编辑器的使用,但是,似乎这与本节中围绕的核心代码beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()))并无联系,因为在注册自定义属性编辑器的时候使用的是 ResourceEditorRegistrar 的 registerCustomEditors 方法,而这里使用的是ConfigurableListableBeanFactory的addPropertyEditorRegistrar方法。
我们不妨深入探索一下 ResourceEditorRegistrar 的内部实现,在 ResourceEditorRegistrar 中,我们最关心的方法是registerCustomEditors 。
/*** Populate the given {@code registry} with the following resource editors:* ResourceEditor, InputStreamEditor, InputSourceEditor, FileEditor, URLEditor,* URIEditor, ClassEditor, ClassArrayEditor.* <p>If this registrar has been configured with a {@link ResourcePatternResolver},* a ResourceArrayPropertyEditor will be registered as well.* @see org.springframework.core.io.ResourceEditor* @see org.springframework.beans.propertyeditors.InputStreamEditor* @see org.springframework.beans.propertyeditors.InputSourceEditor* @see org.springframework.beans.propertyeditors.FileEditor* @see org.springframework.beans.propertyeditors.URLEditor* @see org.springframework.beans.propertyeditors.URIEditor* @see org.springframework.beans.propertyeditors.ClassEditor* @see org.springframework.beans.propertyeditors.ClassArrayEditor* @see org.springframework.core.io.support.ResourceArrayPropertyEditor*/@Overridepublic void registerCustomEditors(PropertyEditorRegistry registry) {ResourceEditor baseEditor = new ResourceEditor(this.resourceLoader, this.propertyResolver);doRegisterEditor(registry, Resource.class, baseEditor);doRegisterEditor(registry, ContextResource.class, baseEditor);doRegisterEditor(registry, InputStream.class, new InputStreamEditor(baseEditor));doRegisterEditor(registry, InputSource.class, new InputSourceEditor(baseEditor));doRegisterEditor(registry, File.class, new FileEditor(baseEditor));doRegisterEditor(registry, Path.class, new PathEditor(baseEditor));doRegisterEditor(registry, Reader.class, new ReaderEditor(baseEditor));doRegisterEditor(registry, URL.class, new URLEditor(baseEditor));ClassLoader classLoader = this.resourceLoader.getClassLoader();doRegisterEditor(registry, URI.class, new URIEditor(classLoader));doRegisterEditor(registry, Class.class, new ClassEditor(classLoader));doRegisterEditor(registry, Class[].class, new ClassArrayEditor(classLoader));if (this.resourceLoader instanceof ResourcePatternResolver) {doRegisterEditor(registry, Resource[].class,new ResourceArrayPropertyEditor((ResourcePatternResolver) this.resourceLoader, this.propertyResolver));}}/*** Override default editor, if possible (since that's what we really mean to do here);* otherwise register as a custom editor.*/private void doRegisterEditor(PropertyEditorRegistry registry, Class<?> requiredType, PropertyEditor editor) {if (registry instanceof PropertyEditorRegistrySupport) {((PropertyEditorRegistrySupport) registry).overrideDefaultEditor(requiredType, editor);}else {registry.registerCustomEditor(requiredType, editor);}}
在 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 方法的调用层次结构,如下图所示。
发现在AbstractBeanFactory中的registerCustomEditors方法中被调用过,继续查看AbstractBeanFactory中的registerCustomEditors方法的层次结构,如下图所示。
其中我们看到一个方法是我们熟悉的,就是AbstractBeanFactory类中的initBeanWrapper 方法,这是在bean初始化时使用的一个方法,之前巳经使用过大量的篇幅进行讲解,主要是在将BeanDefinition转换为BeanWrapper后用于对属件的填充。到此,逻辑已经明了,在bean的初始化后会调用ResourceEditorRegistrar的registerCustomEditors方法进行批量的通用属性编辑器注册。注册后,在属性填充的环节便可以直接让Spring使用这些编辑器进行属性的解析了。
既然提到了 BeanWrapper,这里也有必要强调下,Spring中用于封装bean的是BeanWrapper 类型,而它又间接继承了 PropertyEditorRegistry类型,也就是我们之前反复看到的方法参数 PropertyEditorRegistry registry,其实大部分情况下都是 BeanWrapper,对于 BeanWrapper 在 Spring 中的默认实现是 BeanWrapperlmpl,而 BeanWrapperlmpl 除了实现 BeanWrapper 接门外还继承了 PropertyEdhorRegistrySupport,在 PropertyEditorRegistrySupport 中有这样一个方法:
/*** Actually register the default editors for this registry instance.*/private void createDefaultEditors() {this.defaultEditors = new HashMap<>(64);// Simple editors, without parameterization capabilities.// The JDK does not contain a default editor for any of these target types.this.defaultEditors.put(Charset.class, new CharsetEditor());this.defaultEditors.put(Class.class, new ClassEditor());this.defaultEditors.put(Class[].class, new ClassArrayEditor());this.defaultEditors.put(Currency.class, new CurrencyEditor());this.defaultEditors.put(File.class, new FileEditor());this.defaultEditors.put(InputStream.class, new InputStreamEditor());this.defaultEditors.put(InputSource.class, new InputSourceEditor());this.defaultEditors.put(Locale.class, new LocaleEditor());this.defaultEditors.put(Path.class, new PathEditor());this.defaultEditors.put(Pattern.class, new PatternEditor());this.defaultEditors.put(Properties.class, new PropertiesEditor());this.defaultEditors.put(Reader.class, new ReaderEditor());this.defaultEditors.put(Resource[].class, new ResourceArrayPropertyEditor());this.defaultEditors.put(TimeZone.class, new TimeZoneEditor());this.defaultEditors.put(URI.class, new URIEditor());this.defaultEditors.put(URL.class, new URLEditor());this.defaultEditors.put(UUID.class, new UUIDEditor());this.defaultEditors.put(ZoneId.class, new ZoneIdEditor());// Default instances of collection editors.// Can be overridden by registering custom instances of those as custom editors.this.defaultEditors.put(Collection.class, new CustomCollectionEditor(Collection.class));this.defaultEditors.put(Set.class, new CustomCollectionEditor(Set.class));this.defaultEditors.put(SortedSet.class, new CustomCollectionEditor(SortedSet.class));this.defaultEditors.put(List.class, new CustomCollectionEditor(List.class));this.defaultEditors.put(SortedMap.class, new CustomMapEditor(SortedMap.class));// Default editors for primitive arrays.this.defaultEditors.put(byte[].class, new ByteArrayPropertyEditor());this.defaultEditors.put(char[].class, new CharArrayPropertyEditor());// The JDK does not contain a default editor for char!this.defaultEditors.put(char.class, new CharacterEditor(false));this.defaultEditors.put(Character.class, new CharacterEditor(true));// Spring's CustomBooleanEditor accepts more flag values than the JDK's default editor.this.defaultEditors.put(boolean.class, new CustomBooleanEditor(false));this.defaultEditors.put(Boolean.class, new CustomBooleanEditor(true));// The JDK does not contain default editors for number wrapper types!// Override JDK primitive number editors with our own CustomNumberEditor.this.defaultEditors.put(byte.class, new CustomNumberEditor(Byte.class, false));this.defaultEditors.put(Byte.class, new CustomNumberEditor(Byte.class, true));this.defaultEditors.put(short.class, new CustomNumberEditor(Short.class, false));this.defaultEditors.put(Short.class, new CustomNumberEditor(Short.class, true));this.defaultEditors.put(int.class, new CustomNumberEditor(Integer.class, false));this.defaultEditors.put(Integer.class, new CustomNumberEditor(Integer.class, true));this.defaultEditors.put(long.class, new CustomNumberEditor(Long.class, false));this.defaultEditors.put(Long.class, new CustomNumberEditor(Long.class, true));this.defaultEditors.put(float.class, new CustomNumberEditor(Float.class, false));this.defaultEditors.put(Float.class, new CustomNumberEditor(Float.class, true));this.defaultEditors.put(double.class, new CustomNumberEditor(Double.class, false));this.defaultEditors.put(Double.class, new CustomNumberEditor(Double.class, true));this.defaultEditors.put(BigDecimal.class, new CustomNumberEditor(BigDecimal.class, true));this.defaultEditors.put(BigInteger.class, new CustomNumberEditor(BigInteger.class, true));// Only register config value editors if explicitly requested.if (this.configValueEditorsActive) {StringArrayPropertyEditor sae = new StringArrayPropertyEditor();this.defaultEditors.put(String[].class, sae);this.defaultEditors.put(short[].class, sae);this.defaultEditors.put(int[].class, sae);this.defaultEditors.put(long[].class, sae);}}
具体的调用方法我们就不去深究了,但是至少通过这个方法我们已经知道了在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 中并没有做过多逻辑处理。
@Overridepublic Object postProcessAfterInitialization(Object bean, String beanName) {return bean;}
那么,我们重点看一下postProcessBeforelnitialization 方法。
@Override@Nullablepublic Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {AccessControlContext acc = null;if (System.getSecurityManager() != null &&(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {acc = this.applicationContext.getBeanFactory().getAccessControlContext();}if (acc != null) {AccessController.doPrivileged((PrivilegedAction<Object>) () -> {invokeAwareInterfaces(bean);return null;}, acc);}else {invokeAwareInterfaces(bean);}return bean;}private void invokeAwareInterfaces(Object bean) {if (bean instanceof Aware) {if (bean instanceof EnvironmentAware) {((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());}if (bean instanceof EmbeddedValueResolverAware) {((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);}if (bean instanceof ResourceLoaderAware) {((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);}if (bean instanceof ApplicationEventPublisherAware) {((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);}if (bean instanceof MessageSourceAware) {((MessageSourceAware) bean).setMessageSource(this.applicationContext);}if (bean instanceof ApplicationContextAware) {((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);}}}
postProcessBeforelnitialization 方法中调用了invokeAwareInterfaces。从invokeAwareInterfaces方法中,我们或许已经或多或少了解了Spring的用意,实现这些Aware接口的bean在被初始化之后,可以取得一些对应的资源。
设置忽略依赖
当 Spring 将 ApplicationContextAwareProcessor 注册后,那么在 invokeAwarelnterfaces 方法中间接调用的 Aware 类已经不是普通的 bean 了,如 ResourceLoaderAware、ApplicationEventPublisher Aware 等,那么当然需要在Spring做bean的依赖注入的时候忽略它们。而ignoreDependencylnterfece的作用正是在此。
// 设置了几个忽略自动装配的接口beanFactory.ignoreDependencyInterface(EnvironmentAware.class);beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);beanFactory.ignoreDependencyInterface(MessageSourceAware.class);beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
注册依赖
Spring中有了忽略依赖的功能,当然也必不可少地会有注册依赖的功能。
// 设置了几个自动装配的特殊规则beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);beanFactory.registerResolvableDependency(ResourceLoader.class, this);beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);beanFactory.registerResolvableDependency(ApplicationContext.class, this);
当注册了依赖解析后,例如当注册了对BeanFactory.class的解析依赖后,当bean的属性注入的时候,一旦检测到属性为BeanFactory类型便会将beanFactory的实例注人进去。
