咱们在基于spring开发应用的时候,通常都会将数据库的配置放置在properties文件中.
代码分析的时候,涉及的知识点概要:java
咱们先来看看具体的使用吧node
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.2.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.2.xsd"> <context:property-placeholder location="classpath:foo.properties" /> </beans>
这样/src/main/resources/foo.properties文件就会被spring加载
若是想使用多个配置文件,能够添加order字段来进行排序spring
Spring3.1添加了@PropertySource注解,方便添加property文件到环境.数据库
@Configuration @PropertySource("classpath:foo.properties") public class PropertiesWithJavaConfig { @Bean public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() { return new PropertySourcesPlaceholderConfigurer(); } }
@Value( "${jdbc.url}" ) private String jdbcUrl;
还能够添加一个默认值设计模式
@Value( "${jdbc.url:aDefaultUrl}" ) private String jdbcUrl;
<bean id="dataSource"> <property name="url" value="${jdbc.url}" /> </bean>
Spring在启动时会经过AbstractApplicationContext#refresh启动容器初始化工做,期间会委托loadBeanDefinitions解析xml配置文件.api
protected final void refreshBeanFactory() throws BeansException { if (hasBeanFactory()) { destroyBeans(); closeBeanFactory(); } try { DefaultListableBeanFactory beanFactory = createBeanFactory(); beanFactory.setSerializationId(getId()); customizeBeanFactory(beanFactory); loadBeanDefinitions(beanFactory); synchronized (this.beanFactoryMonitor) { this.beanFactory = beanFactory; } } catch (IOException ex) { throw new ApplicationContextException("I/O error parsing bean definition source for " + getDisplayName(), ex); } }
loadBeanDefinitions经过层层委托,找到DefaultBeanDefinitionDocumentReader#parseBeanDefinition解析具体的beanide
protected void parseBeanDefinitions(Element root, BeanDefinitionParserDelegate delegate) { if (delegate.isDefaultNamespace(root)) { NodeList nl = root.getChildNodes(); for (int i = 0; i < nl.getLength(); i++) { Node node = nl.item(i); if (node instanceof Element) { Element ele = (Element) node; if (delegate.isDefaultNamespace(ele)) { parseDefaultElement(ele, delegate); } else { delegate.parseCustomElement(ele); } } } } else { delegate.parseCustomElement(root); } }
这边因为
经过NamespaceHandler查找到对应的处理器是ContextNamespaceHandler,再经过id找到PropertyPlaceholderBeanDefinitionParser解析器解析post
@Override public void init() { // 这就是咱们要找的解析器 registerBeanDefinitionParser("property-placeholder", new PropertyPlaceholderBeanDefinitionParser()); registerBeanDefinitionParser("property-override", new PropertyOverrideBeanDefinitionParser()); registerBeanDefinitionParser("annotation-config", new AnnotationConfigBeanDefinitionParser()); registerBeanDefinitionParser("component-scan", new ComponentScanBeanDefinitionParser()); registerBeanDefinitionParser("load-time-weaver", new LoadTimeWeaverBeanDefinitionParser()); registerBeanDefinitionParser("spring-configured", new SpringConfiguredBeanDefinitionParser()); registerBeanDefinitionParser("mbean-export", new MBeanExportBeanDefinitionParser()); registerBeanDefinitionParser("mbean-server", new MBeanServerBeanDefinitionParser()); }
PropertyPlaceholderBeanDefinitionParser是这一轮代码分析的重点.
咱们来看看它的父类吧.ui
public interface BeanDefinitionParser { BeanDefinition parse(Element element, ParserContext parserContext); }
AbstractSingleBeanDefinitionParser
解析,定义单个BeanDefinition的抽象父类
在parseInternal中,解析出parentName,beanClass,source;并使用BeanDefinitionBuilder进行封装this
AbstractPropertyLoadingBeanDefinitionParser
解析property相关的属性,如location,properties-ref,file-encoding,order等
PropertyPlaceholderBeanDefinitionParser
这边处理的事情很少,就是设置ingore-unresolvable和system-properties-mode
接下来,咱们再看看这个bean是在何时实例化的,通常类的实例化有2种,一种是单例系统启动就实例化;一种是非单例(或者单例懒加载)在getBean时实例化.
这边的触发倒是经过BeanFcatoryPostProcessor.
BeanFactoryPostProcessor是在bean实例化前,修改bean definition的,好比bean definition中的占位符就是这边解决的,而咱们如今使用的properties也是这边解决的.
这个是经过PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors实现的.
扫描容器中的BeanFactoryPostProcessor,找到了这边须要的PropertySourcesPlaceholderConfigurer,并经过容器的getBean实例化
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) { PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()); }
PropertySourcesPlaceholderConfigurer实例化完成后,就直接进行触发,并加载信息
OrderComparator.sort(priorityOrderedPostProcessors); invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
咱们再来看看PropertySourcesPlaceholderConfigurer的继承体系把
BeanFactoryPostProcessor
定义一个用于修改容器中bean definition的属性的接口.其实现类在通常类使用前先实例化,并对其余类的属性进行修改.
这跟BeanPostProcessor有明显的区别,BeanPostProcessor是修改bean实例的.
PropertiesLoaderSupport
加载properties文件的抽象类.
这边具体的加载逻辑是委托PropertiesLoaderUtils#fillProperties实现
PropertyResourceConfigurer
bean definition中占位符的替换就是这个抽象类实现的.
实现BeanFactoryPostProcessor#postProcessBeanFactory,迭代容器的中的类定义,进行修改
具体如何修改就经过钩子processProperties交由子类实现
PlaceholderConfigurerSupport
使用visitor设计模式,经过BeanDefinitionVisitor和StringValueResolver更新属性
StringValueResolver是一个转化String类型数据的接口,真正更新属性的api实现居然是在PropertyPlaceholderHelper#parseStringValue
PropertySourcesPlaceholderConfigurer 覆写postProcessorBeanFactory api定义解析流程