zoukankan      html  css  js  c++  java
  • spring源码学习之容器的扩展(一)

      在前面的章节,我们一直以BeanFactory接口以及它的默认实现XmlBeanFactory为例进行解析,但是,spring还提供了另一个接口ApplicationContext,用于扩展BeanFactory中现有的功能。ApplicationContext和BeanFactory两者都是用于加载bean的,但是相比之下,ApplicationContext提供了更多的扩展功能,也就是说,ApplicationContext包含BeanFactory的所有功能,通常建议比BeanFactory优先,除非在一些限制的场合,比如字节长度对内存有很大的影响时(Applet)。绝大多数"典型的",企业应用和系统,ApplicationContext就是你需要使用的

    那么究竟ApplicationContext和BeanFactor多出了哪些功能呢?这就是需要进行探索的,首先看一下两个类在加载配置文件写法上的不同
    (1)使用BeanFactory方式加载xml
    BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"));
    (2)使用ApplicationContext方式加载xml
    ApplicationContext bf = new ClassPathXmlApplicationContext("beanFactoryTest.xml");

    我们以ClassPathXmlApplicationContext 作为切入点开始对整体功能进行分析
    org.springframework.context.support包下的ClassPathXmlApplicationContext类

     1 public ClassPathXmlApplicationContext(String configLocation) throws BeansException {
     2     this(new String[] {configLocation}, true, null);
     3 }
     4 
     5 // 所有的构造方法都会调用下面这个方法
     6 public ClassPathXmlApplicationContext(
     7         String[] configLocations, boolean refresh, @Nullable ApplicationContext parent)
     8         throws BeansException {
     9 
    10     super(parent);
    11     setConfigLocations(configLocations);
    12     if (refresh) {
    13         refresh();
    14     }
    15 }

    设置路径是必不可少的步骤,ClassPathXmlApplicationContext中可以将配置文件的路径以数组的方式传入,ClassPathXmlApplicationContext可以对数组进行解析和
    加载,而对于解析及功能实现都是在refresh()方法中实现

    一、设置配置路径
    在ClassPathXmlApplicationContext中支持多个配置路径以数组的方式同时传入
    org.springframework.context.support包下的AbstractRefreshableConfigApplicationContext类中的

     1 public void setConfigLocations(@Nullable String... locations) {
     2     if (locations != null) {
     3         Assert.noNullElements(locations, "Config locations must not be null");
     4         this.configLocations = new String[locations.length];
     5         for (int i = 0; i < locations.length; i++) {
     6             // 循环解析给定的路径
     7             this.configLocations[i] = resolvePath(locations[i]).trim();
     8         }
     9     }
    10     else {
    11         this.configLocations = null;
    12     }
    13 }

    此函数主要用于解析给定的路径数组,当然,如果数组中包含特殊符号,如${var},那么resolvePath方法中会搜寻匹配的系统变量并替换

    二、扩展功能
    设置了路径之后,便可以根据路径做配置文件的解析以及各种功能的实现了,可以说refresh方法中包含了ApplicationContext中提供的全部功能,而且此函数中
    逻辑非常清晰明了,使我们很容易分析对应的层次及逻辑

    org.springframework.context.support包下AbstractApplicationContext类中

     1 @Override
     2 public void refresh() throws BeansException, IllegalStateException {
     3     synchronized (this.startupShutdownMonitor) {
     4         // Prepare this context for refreshing.
     5         // 准备刷新新的上下文
     6         prepareRefresh();
     7 
     8         // Tell the subclass to refresh the internal bean factory.
     9         // 初始化BeanFactory,进行XML文件读取
    10         ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
    11 
    12         // Prepare the bean factory for use in this context.
    13         // 对BeanFactory进行各种功能填充
    14         prepareBeanFactory(beanFactory);
    15 
    16         try {
    17             // Allows post-processing of the bean factory in context subclasses.
    18             // 子类覆盖方法,做额外的处理
    19             postProcessBeanFactory(beanFactory);
    20 
    21             // Invoke factory processors registered as beans in the context.
    22             // 激活各种BeanFactory处理器
    23             invokeBeanFactoryPostProcessors(beanFactory);
    24 
    25             // Register bean processors that intercept bean creation.
    26             // 注册拦截bean创建的bean处理器,这里只是注册,真正的调用是在getBean的时候
    27             registerBeanPostProcessors(beanFactory);
    28 
    29             // Initialize message source for this context.
    30             // 为上下文初始化Message源,即不同语言的消息体,即国际化处理
    31             initMessageSource();
    32 
    33             // Initialize event multicaster for this context.
    34             // 初始化应用消息广播器,并放入"applicationEventMulticaster"bean中
    35             initApplicationEventMulticaster();
    36 
    37             // Initialize other special beans in specific context subclasses.
    38             // 留给子类来初始化其他bean
    39             onRefresh();
    40 
    41             // Check for listener beans and register them.
    42             // 在所有注册的bean中查找Listener bean,注册到消息广播器中
    43             registerListeners();
    44 
    45             // Instantiate all remaining (non-lazy-init) singletons.
    46             // 初始化剩下的单实例(非惰性的)
    47             finishBeanFactoryInitialization(beanFactory);
    48 
    49             // Last step: publish corresponding event.
    50             // 完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别人
    51             finishRefresh();
    52         }
    53 
    54         catch (BeansException ex) {
    55             if (logger.isWarnEnabled()) {
    56                 logger.warn("Exception encountered during context initialization - " +
    57                         "cancelling refresh attempt: " + ex);
    58             }
    59 
    60             // Destroy already created singletons to avoid dangling resources.
    61             destroyBeans();
    62 
    63             // Reset 'active' flag.
    64             cancelRefresh(ex);
    65 
    66             // Propagate exception to caller.
    67             throw ex;
    68         }
    69 
    70         finally {
    71             // Reset common introspection caches in Spring's core, since we
    72             // might not ever need metadata for singleton beans anymore...
    73             resetCommonCaches();
    74         }
    75     }
    76 }

    下面概括一下ClassPathXmlApplicationContext初始化步骤,并从中解释一下,它为我们提供的功能
    (1)初始化前的准备工作,例如对系统属性或者是环境变量进行准备和验证
    在某种情况下,项目的使用需要读取某些系统变量,而这个变量的设置很可能会影响这系统的正确性,那么ClassPathXmlApplicationContext为我们提供的这个准备函数
    就显得非常重要,它可以在spring启动的时候提前对必需的变量进行存在性验证
    (2)初始化bean,并进行XML读取
    之前提到ClassPathXmlApplicationContext包含着BeanFactory所提供的一切特性,那么在这一步骤中将会复用BeanFactory中的配置文件读取解析及其功能,这一步之后
    ClassPathXmlApplicationContext实际上就已经包含了BeanFactory所提供的功能,也就是说可以进行bean的提取等基础操作了
    (3)对BeanFactory进行各种功能的填充
    对@Qualifier与@Autowired应该比较熟悉,那么这两个注解正是在这一步骤中增加的支持
    (4)子类覆盖方法做格外的处理
    spring之所以强大,为世人所推崇,除了它功能上为大家提供了便利外,还有一方面是他的完美架构,开放式的架构让使用它的程序员很容易根据业务需要扩展已经
    存在的功能,这种开放式的设计在spring中随处可见,例如在本例中就提供了一个空的函数的实现postProcessBeanFactory来方便程序员在业务上进一步扩展
    (5)激活各种BeanFactory处理器
    (6)注册拦截bean创建的bean处理器,这里只是注册,真正调用的是在getBean的时候
    (7)为上下文初始化Message源,即对不同的消息体进行国际化处理
    (8)初始化应用消息广播器,并放入"applicationEventMulticaster"bean 中
    (9)留给子类来初始化其他的bean
    (10)在所有的bean中查找listener bean,并注册到消息广播器中
    (11)初始化剩下的单实例(非惰性的)
    (12)完成刷新过程,通知生命周期处理器lifecycleProcessor刷新过程,同时发出ContextRefreshEvent通知别

    三、环境准备
    prepareRefresh方法主要是做些准备工作,例如对系统属性以及环境变量的初始化以及验证
    org.springframework.context.support包下AbstractApplicationContext类中

     1 protected void prepareRefresh() {
     2     // Switch to active.
     3     this.startupDate = System.currentTimeMillis();
     4     this.closed.set(false);
     5     this.active.set(true);
     6 
     7     if (logger.isInfoEnabled()) {
     8         logger.info("Refreshing " + this);
     9     }
    10 
    11     // Initialize any placeholder property sources in the context environment.
    12     // 留给子类覆盖
    13     initPropertySources();
    14 
    15     // Validate that all properties marked as required are resolvable:
    16     // see ConfigurablePropertyResolver#setRequiredProperties
    17     // 验证需要的属性文件是否都放入环境中
    18     getEnvironment().validateRequiredProperties();
    19 
    20     // Store pre-refresh ApplicationListeners...
    21     if (this.earlyApplicationListeners == null) {
    22         this.earlyApplicationListeners = new LinkedHashSet<>(this.applicationListeners);
    23     }
    24     else {
    25         // Reset local application listeners to pre-refresh state.
    26         this.applicationListeners.clear();
    27         this.applicationListeners.addAll(this.earlyApplicationListeners);
    28     }
    29 
    30     // Allow for the collection of early ApplicationEvents,
    31     // to be published once the multicaster is available...
    32     this.earlyApplicationEvents = new LinkedHashSet<>();
    33 }

    虽然在主要的代码initPropertySources()和getEnvironment().validateRequiredProperties()没有什么逻辑代码,没有做任何处理,但是其实我们需要很好的
    深入理解下,其实用好了,还是作用很大的
    (1)initPropertySources()
    initPropertySources正符合spring开放式结构设计,给用户最大扩展spring的能力,用户可以根据自身需要重写initPropertySources方法,并在方法中进行个性化
    属性设置和处理
    (2)validateRequiredProperties
    则是对属性进行验证,如何验证呢?作者提供了一个简单的例子:
    需求:工程运行过程中用到的某一个设置(例如VAR)是从系统环境变零中取得的,而如果用户没有在系统环境变量中配置这个参数,那么功能可能不会工作。
    说下在spring中的解决方案:思路就是对源码进行扩展,自定义一个类继承自ClassPathXmlApplicationContext

    1 public class MyClassPathXmlApplicationContext extends ClassPathXmlApplicationContext{
    2     public MyClassPathXmlApplicationContext(String... configLocations){
    3         super(configLocations);
    4     }
    5     
    6     protected void initPropertySources(){
    7         getEnvironment().setRequiredProperties("VAR");
    8     }
    9 }

    我们自定义了继承自ClassPathXmlApplicationContext的MyClassPathXmlApplicationContext,并重写了initPropertySources方法,在方法中添加了我们的个性化需求
    那么在程序走到getEnvironment().validateRequiredProperties();代码的时候,如果系统没有检测到对应VAR 环境变量,那么抛出异常,当然我们使用的时候需要
    将ClassPathXmlApplicationContext替换掉
    ApplicationContext bf = new MyClassPathXmlApplicationContext("beanFactoryTest.xml");

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

     1 org.springframework.context.support包下AbstractApplicationContext类中
     2 protected ConfigurableListableBeanFactory obtainFreshBeanFactory() {
     3     // 初始化BeanFactory,并进行XML文件读取,并将得到的BeanFactory记录到当前实体的属性中
     4     refreshBeanFactory();
     5     // 返回当前实体的beanFactory属性
     6     ConfigurableListableBeanFactory beanFactory = getBeanFactory();
     7     if (logger.isDebugEnabled()) {
     8         logger.debug("Bean factory for " + getDisplayName() + ": " + beanFactory);
     9     }
    10     return beanFactory;
    11 }

    具体的实现逻辑是在refreshBeanFactory方法中
    org.springframework.context.support包下的AbstractRefreshableApplicationContext类中:

     1 @Override
     2 protected final void refreshBeanFactory() throws BeansException {
     3     if (hasBeanFactory()) {
     4         destroyBeans();
     5         closeBeanFactory();
     6     }
     7     try {
     8         // 创建DefaultListableBeanFactory
     9         DefaultListableBeanFactory beanFactory = createBeanFactory();
    10         // 为了序列化指定id,如果需要的话,让这个BeanFactory从id反序列化到BeanFactory对象
    11         beanFactory.setSerializationId(getId());
    12         // 定制beanFactory,设置相关属性,包括是否允许覆盖同名称的不同定义的对象以及循环依赖以及设置
    13         // @Autowired @Qualifier注解解析器QualifierAnnotationAutowireCandidateResolver
    14         customizeBeanFactory(beanFactory);
    15         // 初始化DodumentReader,并进行XML读取和解析
    16         loadBeanDefinitions(beanFactory);
    17         synchronized (this.beanFactoryMonitor) {
    18             this.beanFactory = beanFactory;
    19         }
    20     }
    21     catch (IOException ex) {
    22         throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex);
    23     }
    24 }

    详细分析上面的那几个步骤:
    (1)创建DefaultListableBeanFactory
    在介绍BeanFactory的时候,声明方式为:BeanFactory bf = new XmlBeanFactory(new ClassPathResource("beanFactoryTest.xml"));
    其中XmlBeanFactory继承自DefaultListableBeanFactory,并提供了XmlBeanDefinitionReader类型的reader属性,也就是说DefaultListableBeanFactory是容器的
    基础,必须首先要实例化,那么在这里就是实例化DefaultListableBeanFactory的步骤
    (2)指定序列化ID
    (3)定制BeanFactory
    (4)加载BeanDefinition
    (5)使用全局变量记录BeanFactory类实例
    因为DefaultListableBeanFactory类型的变量beanFactory是函数内的局部变量,所以要使用全局变量记录解析结果

    4.1 定制BeanFactory
    这里已经开始了对BeanFactory的扩展,在基本容器的基础上,增加了是否允许覆盖是否允许扩展的设置并提供了注解@Autowired和@Qualifier支持
    org.springframework.context.support包下的AbstractRefreshableApplicationContext类中:

     1 protected void customizeBeanFactory(DefaultListableBeanFactory beanFactory) {
     2     // 如果allowBeanDefinitionOverriding不为空,设置beanFactory相关属性,此属性的含义是允许覆盖同名称的不同定义的对象
     3     if (this.allowBeanDefinitionOverriding != null) {
     4         beanFactory.setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
     5     }
     6     // allowCircularReferences不为空,设置beanFactory相关属性,此属性的含义是否允许bean之间存在循环依赖
     7     if (this.allowCircularReferences != null) {
     8         beanFactory.setAllowCircularReferences(this.allowCircularReferences);
     9     }
    10 }

    对于允许覆盖和允许依赖这里只是判断了是否为空,如果不为空,则需要设置,但是并没有看到在哪里设置,具体实现是使用子类覆盖:

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

    对于定制BeanFactory,spring中还提供了另外一个重要的扩展,就是设置AutowireCandidateResolver,在bean加载部分中讲解创建bean时,如果采用autowireType
    方式注入,那么默认会使用spring提供的SimpleAutowireCandidateResolver,而对于默认的实现并没有过多的逻辑处理,在这里spring使用了QualifierAnnotationAutowireCandidateResolver
    设置这个解析器后,spring就可以支持注解的方式注入了。
    在讲解根据类型自定注入的时候,我们说过解析autowire类型时首先会调用方法:
    Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
    因此我们知道在QualifierAnnotationAutowireCandidateResolver中一定会提供解析Qualifier和Autowired注解的方法

    org.springframework.beans.factory.annotation包下QualifierAnnotationAutowireCandidateResolver类中:

     1 @Override
     2 @Nullable
     3 public Object getSuggestedValue(DependencyDescriptor descriptor) {
     4     Object value = findValue(descriptor.getAnnotations());
     5     if (value == null) {
     6         MethodParameter methodParam = descriptor.getMethodParameter();
     7         if (methodParam != null) {
     8             value = findValue(methodParam.getMethodAnnotations());
     9         }
    10     }
    11     return value;
    12 }

    4.2 加载BeanDefinition
    XmlBeanDefinitionReader的初始化以及读取XML文件
    org.springframework.context.support包下AbstractXmlApplicationContext类中

     1 @Override
     2 protected void loadBeanDefinitions(DefaultListableBeanFactory beanFactory) throws BeansException, IOException {
     3     // Create a new XmlBeanDefinitionReader for the given BeanFactory.
     4     // 为指定的BeanFactory创建XmlBeanDefinitionReader
     5     XmlBeanDefinitionReader beanDefinitionReader = new XmlBeanDefinitionReader(beanFactory);
     6 
     7     // Configure the bean definition reader with this context's
     8     // resource loading environment.
     9     // 对beanDefinitionReader进行环境变量的设置
    10     beanDefinitionReader.setEnvironment(this.getEnvironment());
    11     beanDefinitionReader.setResourceLoader(this);
    12     beanDefinitionReader.setEntityResolver(new ResourceEntityResolver(this));
    13 
    14     // Allow a subclass to provide custom initialization of the reader,
    15     // then proceed with actually loading the bean definitions.
    16     // 对BeanDefinitionReader进行设置,可以覆盖
    17     initBeanDefinitionReader(beanDefinitionReader);
    18     // 配置文件的读取
    19     loadBeanDefinitions(beanDefinitionReader);
    20 }
    21 // 与上一个方法在同一个类中
    22 protected void loadBeanDefinitions(XmlBeanDefinitionReader reader) throws BeansException, IOException {
    23     Resource[] configResources = getConfigResources();
    24     if (configResources != null) {
    25         reader.loadBeanDefinitions(configResources);
    26     }
    27     String[] configLocations = getConfigLocations();
    28     if (configLocations != null) {
    29         reader.loadBeanDefinitions(configLocations);
    30     }
    31 }

    XmlBeanDefinitionReader已经将之前的初始化的DefaultListableBeanFactory注册进去了,所以XmlBeanDefinitionReader所读取的BeanDefinitionHolder都会注册
    到DefaultListableBeanFactory,也就是经过这个步骤之后,DefaultListableBeanFactory类型的beanFactory已经包含了所有解析好的配置

    五、功能扩展
    进入函数prepareBeanFactory前,spring已经完成了对配置的解析,而ApplicationContext在功能上的扩展也由此展开
    org.springframework.context.support包下的AbstractApplicationContext类中

     1 protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
     2     // Tell the internal bean factory to use the context's class loader etc.
     3     // 设置beanFactory的classLoader为当前context的classLoader
     4     beanFactory.setBeanClassLoader(getClassLoader());
     5     // 设置beanFactory的表达式语言处理器,spring3增加了表达式语言的支持
     6     // 默认使用#{bean.xxx}的形式来调用相关属性值
     7     beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
     8     // 为beanFactory增加了一个默认的propertyEditor,这个主要是对bean属性等设置管理的一个工具
     9     beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
    10 
    11     // Configure the bean factory with context callbacks.
    12     // 添加BeanPostProcessor
    13     beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
    14     // 设置几个忽略自动装配的接口
    15     beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
    16     beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
    17     beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
    18     beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
    19     beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
    20     beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
    21 
    22     // BeanFactory interface not registered as resolvable type in a plain factory.
    23     // MessageSource registered (and found for autowiring) as a bean.
    24     // 设置了几个自动装备的特殊规则
    25     beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
    26     beanFactory.registerResolvableDependency(ResourceLoader.class, this);
    27     beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
    28     beanFactory.registerResolvableDependency(ApplicationContext.class, this);
    29 
    30     // Register early post-processor for detecting inner beans as ApplicationListeners.
    31     beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
    32 
    33     // Detect a LoadTimeWeaver and prepare for weaving, if found.
    34     // 增加对AspectJ的支持
    35     if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
    36         beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
    37         // Set a temporary ClassLoader for type matching.
    38         beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    39     }
    40 
    41     // Register default environment beans.
    42     // 添加默认的系统环境bean
    43     if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
    44         beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
    45     }
    46     if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
    47         beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
    48     }
    49     if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
    50         beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
    51     }
    52 }

    上面函数主要进行了几个方面的扩展:
    (1)增加对SpEL语言的支持
    (2)增加对属性编辑器的支持
    (3)增加对一些内置类,比如EnvironmentAware、MessageSourceAware的信息注入
    (4)设置了依赖功能可忽略接口
    (5)注册了一些固定依赖的属性
    (6)增加了对AspectJ的支持
    (7)将相关环境变量及属性注册以单例模式注册

    5.1 增加SpEL语言支持
    spring表达式语言全称是 spring Expression Language,缩写为SpEL,能在运行时构建复杂表达式,存取对象图属性、对象方法调用等,并且能与spring功能
    完美结合,比如能用来配置bean的定义,SpEL是单独模块,值依赖core模块,不依赖其他模块,可以单独使用
    SpEL使用#{...},作为界定符,所有在大括号中的字符都将被认为是SpEL,使用格式如下:

    1 <bean id="saxophone" value="com.xxx.xxx.xxx" />
    2 <bean>
    3     <property name="instrument" value="#{saxophone}"></property>
    4 </bean>
    5 // 相当于:
    6 <bean id="saxophone" value="com.xxx.xxx.xxx" />
    7 <bean>
    8     <property name="instrument" ref="saxophone"></property>
    9 </bean>

    当然上面只是列举的简单情况,SpEL功能是非常强大的,使用好,可大大提高开发效率
    在源码中通过代码beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));注册语言解析器
    就可以对SpEL语言进行解析了,但是注册解析器后spring又是在什么时候调用这个解析器进行解析的呢?
    之前我们讲解过spring在bean进行初始化的时候会有属性填充的这一步,而在这一步中spring会调用AbstractAutowireCapableBeanFactory类的applyPropertyValues函数去
    完成这一功能,就在这个函数中,会通过构造函数BeanDefinitionValueResolver类型的valueResolver来进行属性值的解析,同时,也是在这个步骤中一般通过
    AbstractBeanFactory类中的evaluateBeanDefinitionString去完成SpEL解析

     1 @Nullable
     2 protected Object evaluateBeanDefinitionString(@Nullable String value, @Nullable BeanDefinition beanDefinition) {
     3     if (this.beanExpressionResolver == null) {
     4         return value;
     5     }
     6 
     7     Scope scope = null;
     8     if (beanDefinition != null) {
     9         String scopeName = beanDefinition.getScope();
    10         if (scopeName != null) {
    11             scope = getRegisteredScope(scopeName);
    12         }
    13     }
    14     return this.beanExpressionResolver.evaluate(value, new BeanExpressionContext(this, scope));
    15 }

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

    5.2 增加属性注册编辑器
    在spring依赖注入的时候可以把普通属性注入进来,但是像Date类型就无法识别,例如:

    public class UserManager {
        private Date dateValue;
        
        public Date getDateValue(){
            return dateValue;
        }
        
        public void setDateValue(Date dateValue){
            this.dateValue = dateValue;
        }
        
        public String toString(){
            return "dateValue:" + dateValue;
        }
    }
    
    // 上面代码中,需要对日期型属性进行注入:
    <bean id="userManager" class="com.test.UserManager">
        <property name="dateValue">
            <value>2019-7-23</value>
        </property>
    </bean>
    // 测试代码:
    @Test
    public void testDate(){
        ApplicationContext = ctx = new ClassPathXmlApplicationContext("beans.xml");
        UserManager userManager = (UserManager) ctx.getBean("userManager");
        System.out.println(userManager);
    }

    如果直接这样使用,程序则会报错,类型转换不成功,因为带UserManager中的dateValue属性是Date类型的,而在XML中配置的却是String类型的,所以会报错!

    spring中如何解决这种问题:两种方案

    1、使用自定义属性编辑器
    使用自定义属性编辑器,通过继承PropertyEditorSupport,这个类是在jdk中java.beans包下,重写setAsText方法
    (1)编写自定义的属性编辑器

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

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

     1 <!--自定义属性编辑器-->
     2 <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
     3     <property name="customEditors">
     4         <map>
     5             <entry key="java.util,Date" />
     6             <bean class="com.test.DatePropertyEditor">
     7                 <property name="format" value="yyyy-MM-dd"/>
     8             </bean>
     9         </map>
    10     </property>
    11 </bean>

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

    2、注册spring自带的属性编辑器CustomDateEditor
    具体步骤如下:

     1 (1)定义属性编辑器
     2 public class DatePropertyEditorRegistrar implements PropertyEditorRegistrar{
     3     public void registerCustomEditors(PropertyEditorRegistry registry){
     4         registry.registerCustomEditors(Date.Class,new CustomEditor(new SimpleDateFormat("yyyy-MM-dd"),true));
     5     }
     6 }
     7 (2)注册到spring中
     8 <bean class="org.springframework.beans.factory.config.CustomEditorConfigurer">
     9     <property name=""propertyEditorRegistrars>
    10         <list>
    11             <bean class="com.test.DatePropertyEditorRegistrar"></bean>
    12         </list>
    13     </property>
    14 </bean>

    通过在配置文件中将自定义的DatePropertyEditorRegistrar注册进入org.springframework.beans.factory.config.CustomEditorConfigurer中

    我们了解自定义编辑器额使用,但是似乎与本节的核心代码beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
    并无关联,因为在注册自定义属性编辑器的时候使用的是PropertyEditorRegistrar的registerCustomEditors方法,而这里使用的是ConfigurableListableBeanFactory
    的addPropertyEditorRegistrar方法,我们看ResourceEditorRegistrar类,在ResourceEditorRegistrar中,有一个最主要的registerCustomEditors方法,

    org.springframework.beans.support包下的ResourceEditorRegistrar类

     1 @Override
     2 public void registerCustomEditors(PropertyEditorRegistry registry) {
     3     ResourceEditor baseEditor = new ResourceEditor(this.resourceLoader, this.propertyResolver);
     4     doRegisterEditor(registry, Resource.class, baseEditor);
     5     doRegisterEditor(registry, ContextResource.class, baseEditor);
     6     doRegisterEditor(registry, InputStream.class, new InputStreamEditor(baseEditor));
     7     doRegisterEditor(registry, InputSource.class, new InputSourceEditor(baseEditor));
     8     doRegisterEditor(registry, File.class, new FileEditor(baseEditor));
     9     doRegisterEditor(registry, Path.class, new PathEditor(baseEditor));
    10     doRegisterEditor(registry, Reader.class, new ReaderEditor(baseEditor));
    11     doRegisterEditor(registry, URL.class, new URLEditor(baseEditor));
    12 
    13     ClassLoader classLoader = this.resourceLoader.getClassLoader();
    14     doRegisterEditor(registry, URI.class, new URIEditor(classLoader));
    15     doRegisterEditor(registry, Class.class, new ClassEditor(classLoader));
    16     doRegisterEditor(registry, Class[].class, new ClassArrayEditor(classLoader));
    17 
    18     if (this.resourceLoader instanceof ResourcePatternResolver) {
    19         doRegisterEditor(registry, Resource[].class,
    20                 new ResourceArrayPropertyEditor((ResourcePatternResolver) this.resourceLoader, this.propertyResolver));
    21     }
    22 }
    23 
    24 private void doRegisterEditor(PropertyEditorRegistry registry, Class<?> requiredType, PropertyEditor editor) {
    25     if (registry instanceof PropertyEditorRegistrySupport) {
    26         ((PropertyEditorRegistrySupport) registry).overrideDefaultEditor(requiredType, editor);
    27     }
    28     else {
    29         registry.registerCustomEditor(requiredType, editor);
    30     }
    31 }

    在doRegisterEditor函数中,可以看到之前提到的自定义属性中使用的关键代码,registry.registerCustomEditor(requiredType, editor);回过头来看ResourceEditorRegistrar
    类的registerCustomEditors方法的核心功能,其实无非是注册了一系列的常用类型的属性编辑器,例如,代码doRegisterEditor(registry, Class.class, baseEditor);
    实现的功能就是注册Class类对应的属性编辑器,那么,注册后,一旦某个实体bean中,那么,注册后,一旦某个实体bean中存在一些Class属性,那么spring就会调用
    ClassEditor将配置中定义的String类型转换为Class类型并赋值

    分析到这里,我们不禁有个疑问,虽说ResourceEditorRegistrar类的registerCustomEditors方法实现了批量注册的功能,但是beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()))
    仅仅是注册了ResourceEditorRegistrar实例,却并没有调用ResourceEditorRegistrar的registerCustomEditors方法进行注册,那么到底什么时候注册呢?

    选中registerCustomEditors,Ctrl+Alt+H 查看一下该方法的调用层次,这里又学了一招,查看方法调用层次的快捷键,也可以右键open call hierarchy
    查看结果,可以知道在AbstractBeanFactory类中被调用过,其中还有一个熟悉的initBeanWrapper方法,这是在bean初始化时候使用的一个方法,在将BeanDefinition装换为
    BeanWapper后对属性进行填充,至此,bean的初始化后会调用ResourceEditorRegistrar类的registerCustomEditors方法进行批量的通用属性编辑器注册,注册后,
    在属性填充的环节便可以直接让spring使用这些编辑器属性的解析了

    既然提到了BeanWapper,这里说明一下,spring中用于封装的bean是BeanWapper类型,而它又间接实现了PropertyEditorRegistry接口,也就是我们之前反复看到的参数
    PropertyEditorRegistry registry,其实大部分情况下都是BeanWapper,对于BeanWapper在spring中的实现是BeanWapperImpl,而BeanWapperImpl除了实现BeanWapper,
    还继承了PropertyEditorRegistrySupport类,在PropertyEditorRegistrySupport中有一个这样的方法:

    org.springframework.beans包下的PropertyEditorRegistrySupport类

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

    这里就是spring总定义的默认的属性编辑器,但是没有定义的哪些,就需要进行自定义设置了

    5.3 添加ApplicationContextAwareProcessor处理器
    了解了属性编辑器的使用后,接下来我们继续通过AbstractApplicationContext的prepareBeanFactory进行函数跟踪,对于beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
    其实主要目的就是注册个BeanPostProcessor,而真正的逻辑是在ApplicationContextAwareProcessor中
    ApplicationContextAwareProcessor实现BeanPostProcessor接口,对于这个类,我们也只关心postProcessBeforeInitialization和postProcessAfterInitialization这
    两个方法

    org.springframework.context.support包下的ApplicationContextAwareProcessor类中

     1 // 在postProcessAfterInitialization方法中没有做任何处理
     2 @Override
     3 public Object postProcessAfterInitialization(Object bean, String beanName) {
     4     return bean;
     5 }
     6 
     7 // 重点看一下postProcessBeforeInitialization方法
     8 @Override
     9 @Nullable
    10 public Object postProcessBeforeInitialization(final Object bean, String beanName) throws BeansException {
    11     AccessControlContext acc = null;
    12 
    13     if (System.getSecurityManager() != null &&
    14             (bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
    15                     bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
    16                     bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)) {
    17         acc = this.applicationContext.getBeanFactory().getAccessControlContext();
    18     }
    19 
    20     if (acc != null) {
    21         AccessController.doPrivileged((PrivilegedAction<Object>) () -> {
    22             invokeAwareInterfaces(bean);
    23             return null;
    24         }, acc);
    25     }
    26     else {
    27         invokeAwareInterfaces(bean);
    28     }
    29 
    30     return bean;
    31 }
    32 // 最终会调用invokeAwareInterfaces方法,这个在同一个类中
    33 private void invokeAwareInterfaces(Object bean) {
    34     if (bean instanceof Aware) {
    35         if (bean instanceof EnvironmentAware) {
    36             ((EnvironmentAware) bean).setEnvironment(this.applicationContext.getEnvironment());
    37         }
    38         if (bean instanceof EmbeddedValueResolverAware) {
    39             ((EmbeddedValueResolverAware) bean).setEmbeddedValueResolver(this.embeddedValueResolver);
    40         }
    41         if (bean instanceof ResourceLoaderAware) {
    42             ((ResourceLoaderAware) bean).setResourceLoader(this.applicationContext);
    43         }
    44         if (bean instanceof ApplicationEventPublisherAware) {
    45             ((ApplicationEventPublisherAware) bean).setApplicationEventPublisher(this.applicationContext);
    46         }
    47         if (bean instanceof MessageSourceAware) {
    48             ((MessageSourceAware) bean).setMessageSource(this.applicationContext);
    49         }
    50         if (bean instanceof ApplicationContextAware) {
    51             ((ApplicationContextAware) bean).setApplicationContext(this.applicationContext);
    52         }
    53     }
    54 }

    postProcessBeforeInitialization方法中调用了invokeAwareInterfaces方法,实现这些Aware接口的bean在初始化之后,可以取得对应的一些资源

    5.4 设置忽略依赖
    当spring将ApplicationContextAwareProcessor注册后,那么在invokeAwareInterfaces方法中简介调用Aware类已经不是普通的bean了,那么需要spring在做bean
    依赖的时候忽略他们,ignoreDependencyInterface方法正是这个作用
    org.springframework.context.support包下的AbstractApplicationContext类中prepareBeanFactory方法中
    // 设置几个忽略自动装配的接口
    beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
    beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
    beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
    beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);

    5.5 注册依赖
    spring中有了忽略依赖的功能,当然必不可少会有注册依赖的功能

    org.springframework.context.support包下的AbstractApplicationContext类中prepareBeanFactory方法中
    // 设置了几个自动装备的特殊规则
    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注入进去

  • 相关阅读:
    留言板
    文件操作1
    JQUERY与JS的区别
    PHP 练习租房
    PHP 投票练习
    PHP,单项查询及多项查询
    PHP 增删改查 import!!
    PHP 数据访问
    PHP 对象及其三大特性
    正则表达式和数组
  • 原文地址:https://www.cnblogs.com/ssh-html/p/11257198.html
Copyright © 2011-2022 走看看