spring源码分析:PropertyPlaceholderConfigurer

简介

最近工做中须要使用zookeeper配置中心管理各系统的配置,也就是须要在项目启动时,加载zookeeper中节点的子节点的数据(例如数据库的地址,/config/db.properties/db.addr),并替代spring xml里的占位符。既然须要替代占位符,那么天然会想到PropertyPlaceholderConfigurer这个类,该类实现了在容器的bean初始化前,替代spring容器的BeanDefinition中的值。git

本文将对PropertyPlaceholderConfigurer源码进行解析。程序员

为了简化整个分析流程,假设定义了一个bean ZookeeperUtil,须要PropertyPlaceholderConfigurer类修改beanDefinition定义,替换${zookeeper.addr}。github

 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 class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
 7  
 8         <property name="ignoreUnresolvablePlaceholders" value="true"/>
 9         <property name="locations">
10             <array>
11                 <!--不一样容器之间的属性不能相互访问-->
12                 <value>classpath:config.properties</value>
13             </array>
14         </property>
15     </bean>
16  
17     <bean class="com.github.thinwonton.spring.source.analysis.ZookeeperUtil">
18         <property name="addr" value="${zookeeper.addr}"/>
19     </bean>
20 </beans>

什么是BeanFactoryPostProcessor

org.springframework.beans.factory.config.BeanFactoryPostProcessor接口是spring的一个扩展点。它提供了在容器建立bean以前,对bean的定义(配置元数据)进行处理的方法。spring

BeanFactoryPostProcessor接口定义了一个抽象方法:数据库

void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;

其中,beanFactory是bean工厂,里面封装了beanDefinition,也就是bean在xml的定义。缓存

postProcessBeanFactory 何时调用呢?并发

在spring容器初始化时,AbstractApplicationContext.class的refresh()方法会被调用,该refresh()方法以下post

 1 public void refresh() throws BeansException, IllegalStateException {  2         synchronized (this.startupShutdownMonitor) {  3             // Prepare this context for refreshing.  4             // 记录启动时间,设置启动标识
 5  prepareRefresh();  6  
 7             // 建立beanFactory;解析spring配置文件;获取bean的定义,注册BeanDefinition
 8             ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();  9  
10             //为BeanFactory配置容器特性,例如类加载器、事件处理器等 
11  prepareBeanFactory(beanFactory); 12  
13             try 
14                 // 内容为空的方法,留给子类按需覆写
15  postProcessBeanFactory(beanFactory); 16  
17                 //在这里调用每一个BeanFactoryPostProcessor实现类的postProcessBeanFactory
18  invokeBeanFactoryPostProcessors(beanFactory); 19  
20                 //为BeanFactory注册BeanPost事件处理器. 
21  registerBeanPostProcessors(beanFactory); 22  
23                 //初始化信息源,和国际化相关. 
24  initMessageSource(); 25  
26                 //初始化容器事件传播器. 
27  initApplicationEventMulticaster(); 28  
29                 //调用子类的某些特殊Bean初始化方法 
30  onRefresh(); 31  
32                 //为事件传播器注册事件监听器. 
33  registerListeners(); 34  
35                 //初始化全部剩余的单例Bean. 
36  finishBeanFactoryInitialization(beanFactory); 37  
38                 //初始化容器的生命周期事件处理器,并发布容器的生命周期事件 
39  finishRefresh(); 40  } 41  
42             catch (BeansException ex) { 43                 //销毁以建立的单态Bean 
44  destroyBeans(); 45                             //取消refresh操做,重置容器的同步标识. 
46  cancelRefresh(ex); 47                            throw ex; 48  } 49  } 50     }

上面的流程中,在invokeBeanFactoryPostProcessors()被调用以前,spring容器建立了beanFactory,并在beanFactory中保存了spring配置文件中bean的定义。该定义包括了前面xml中定义的两个bean,一个是PropertyPlaceholderConfigurer,另外一个是ZookeeperUtil。注意:是定义不是初始化后的实例。this

 1 protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {  2     // Invoke BeanDefinitionRegistryPostProcessors first, if any.
 3     Set<String> processedBeans = new HashSet<String>();  4  
 5     // 忽略代码,不影响下面分析  6  
 7     // 根据类型,从bean factory中获取bean名称的列表。bean names在建立bean factory这个容器的时候,已经从xml中读取并缓存了。  8     // 在这里是须要获取BeanFactoryPostProcessor.class类型的bean name
 9     String[] postProcessorNames =
10             beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false); 11  
12     // 筛选出哪些BeanFactoryPostProcessor的实现类实现了PriorityOrdered、Ordered接口,并把它们的bean name放到相应的列表中 13     // PriorityOrdered、Ordered以及在xml中声明的顺序,影响BeanFactoryPostProcessor的实现类被调用的顺序 14     // PropertyPlaceholderConfigurer的父类PropertyResourceConfigurer实现了PriorityOrdered接口,因此它会被加入到priorityOrderedPostProcessors列表中
15  
16     List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>(); 17     List<String> orderedPostProcessorNames = new ArrayList<String>(); 18     List<String> nonOrderedPostProcessorNames = new ArrayList<String>(); 19     for (String ppName : postProcessorNames) { 20         if (processedBeans.contains(ppName)) { 21             // skip - already processed in first phase above
22  } 23         else if (isTypeMatch(ppName, PriorityOrdered.class)) { 24             //这个分支很是奇怪,竟然在这里就实例化,而后把实例化的实现类放到集合中,并与下面的分支处理方式不一样,确定不是同一个程序员写的
25             priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class)); 26  } 27         else if (isTypeMatch(ppName, Ordered.class)) { 28  orderedPostProcessorNames.add(ppName); 29  } 30         else { 31  nonOrderedPostProcessorNames.add(ppName); 32  } 33  } 34  
35     // 首先,调用实现了优先级接口的BeanFactoryPostProcessor实现类,一样实现优先级接口的类经过getOrder()的返回值进行排序,决定调用顺序
36  OrderComparator.sort(priorityOrderedPostProcessors); 37  invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory); 38  
39     // 而后,调用实现了Ordered接口的BeanFactoryPostProcessor实现类,一样实现Ordered接口的类经过getOrder()的返回值进行排序,决定调用顺序
40     List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>(); 41     for (String postProcessorName : orderedPostProcessorNames) { 42         orderedPostProcessors.add(getBean(postProcessorName, BeanFactoryPostProcessor.class)); 43  } 44  OrderComparator.sort(orderedPostProcessors); 45  invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory); 46  
47     // 最后,调用普通的的BeanFactoryPostProcessor实现类,它的顺序由配置文件XML的声明顺序决定
48     List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<BeanFactoryPostProcessor>(); 49     for (String postProcessorName : nonOrderedPostProcessorNames) { 50         nonOrderedPostProcessors.add(getBean(postProcessorName, BeanFactoryPostProcessor.class)); 51  } 52  invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory); 53 }

 

上面的注释详细解析了 invokeBeanFactoryPostProcessors 方法的流程,该方法将从bean factory中获取全部实现BeanFactoryPostProcessor接口的bean名称,并对bean name列表进行了排序,最后根据bean name实例化它们,并调用BeanFactoryPostProcessor接口的postProcessBeanFactory方法。spa

在这里,实例化BeanFactoryPostProcessor是经过 beanFactory的getBean() 方法实现的,该方法很是复杂,不是本文的讨论范畴。


PropertyPlaceholderConfigurer源码解析

PropertyPlaceholderConfigurer,用于将properties文件中定义的属性替换到bean定义的property占位符。

看下它的类图:

输入图片说明

  • PropertiesLoaderSupport:属性加载帮助类,提供从properties文件中读取配置信息的能力,该类的属性locations指定须要加载的文件所在的路径。

  • PropertyResourceConfigurer:属性资源的配置类,实现了BeanFactoryPostProcessor接口,所以,在容器初始化的时候,调用的就是该类的实现方法 postProcessBeanFactory() 。在 postProcessBeanFactory()方法中,从配置文件中读取了配置项,最后调用了它的抽象方法 processProperties(),由子类决定怎么处理这些配置属性。除此以外,提供了convertProperty()方法,该方法是个扩展点,其实里面什么都没作,它能够用来子类在处理这些配置信息前,对配置信息进行一些转换,例如配置属性的解密。

  • PropertyPlaceholderConfigurer:该类实现了父类PropertyResourceConfigurer的抽象方法processProperties()。processProperties()方法会建立PlaceholderResolvingStringValueResolver类,该类提供解析字符串的方法resolveStringValue。建立了StringValueResolver实现类后,交由它的父类PlaceholderConfigurerSupport的doProcessProperties()处理。另外,占位符的值替换为properties中的值的实际处理类。

  • PlaceholderConfigurerSupport:该类持有占位符符号的前缀、后缀,并在doProcessProperties()模板方法中,对BeanDefinition实例中的占位符进行替换。

  • BeanDefinition:在spring容器初始化时,扫描并获取每一个bean的声明(例如在xml中声明、经过注解声明等),而后组装成BeanDefinition,它描述了一个bean实例,拥有属性值,构造参数值和具体实现提供的其余信息。

  • BeanDefinitionVisitor:负责访问BeanDefinition,包括(1)从beanDefinition实例中,获取spring约定的能够替换的参数;(2)使用占位符解析器解析占位符,并从properties中获取它对应的值,最后把值设置到BeanDefinition中。

  • PropertyPlaceholderHelper:持有占位符的前缀、后缀、多值的分隔符,负责把占位符的字符串去除前缀、后缀,对于字符串的替换,委托给PropertyPlaceholderConfigurerResolver类处理。

  • PropertyPlaceholderConfigurerResolver:该类委托给PropertyPlaceholderConfigurer类处理。

接下来咱们看一下时序图,帮助理解上述的类图和整个解析占位符的过程。 输入图片说明

时序图,配合上面的类图看,效果更佳!

相关文章
相关标签/搜索