Spring中property-placeholder的使用与解析详解
|
我们在基于spring开发应用的时候,一般都会将数据库的配置放置在properties文件中. 代码分析的时候,涉及的知识点概要: 1.NamespaceHandler 解析xml配置文件中的自定义命名空间 我们先来看看具体的使用吧 property的使用 在xml文件中配置properties文件 <?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字段来进行排序 使用PropertySource注解配置 Spring3.1添加了@PropertySource注解,方便添加property文件到环境.
@Configuration
@PropertySource("classpath:foo.properties")
public class PropertiesWithJavaConfig {
@Bean
public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {
return new PropertySourcesPlaceholderConfigurer();
}
}
properties的注入与使用 1.java中使用@Value注解获取
@Value( "${jdbc.url}" )
private String jdbcUrl;
还可以添加一个默认值
@Value( "${jdbc.url:aDefaultUrl}" )
private String jdbcUrl;
1.在Spring的xml配置文件中获取
<bean id="dataSource">
<property name="url" value="${jdbc.url}" />
</bean>
源码解析 properties配置信息的加载 Spring在启动时会通过AbstractApplicationContext#refresh启动容器初始化工作,期间会委托loadBeanDefinitions解析xml配置文件.
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解析具体的bean
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);
}
}
这边由于不是标准类定义,所以委托BeanDefinitionParserDelegate解析 通过NamespaceHandler查找到对应的处理器是ContextNamespaceHandler,再通过id找到PropertyPlaceholderBeanDefinitionParser解析器解析
@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是这一轮代码分析的重点. 我们来看看它的父类吧. 1.BeanDefinitionParser 被DefaultBeanDefinitionDocumentReader用于解析个性化的标签 这边只定义了一个解析Element的parse api
public interface BeanDefinitionParser {
BeanDefinition parse(Element element,ParserContext parserContext);
}
2.AbstractBeanDefinitionParser BeanDefinitionParser接口的默认抽象实现.spring的拿手好戏,这边提供了很多方便使用的api,并使用模板方法设计模式给子类提供自定义实现的钩子 我们来看看parse时具体的处理逻辑把: 调用钩子parseInternal解析
3.AbstractSingleBeanDefinitionParser 解析,定义单个BeanDefinition的抽象父类 在parseInternal中,解析出parentName,beanClass,source;并使用BeanDefinitionBuilder进行封装 4.AbstractPropertyLoadingBeanDefinitionParser 解析property相关的属性,如location,properties-ref,file-encoding,order等 5.PropertyPlaceholderBeanDefinitionParser 这边处理的事情不多,就是设置ingore-unresolvable和system-properties-mode properties文件的加载,bean的实例化 接下来,我们再看看这个bean是在什么时候实例化的,一般类的实例化有2种,一种是单例系统启动就实例化;一种是非单例(或者单例懒加载)在getBean时实例化. 这边的触发却是通过BeanFcatoryPostProcessor. BeanFactoryPostProcessor是在bean实例化前,修改bean definition的,比如bean definition中的占位符就是这边解决的,而我们现在使用的properties也是这边解决的. 这个是通过PostProcessorRegistrationDelegate#invokeBeanFactoryPostProcessors实现的.
protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory,getBeanFactoryPostProcessors());
}
PropertySourcesPlaceholderConfigurer实例化完成后,就直接进行触发,并加载信息 OrderComparator.sort(priorityOrderedPostProcessors); invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors,beanFactory); 我们再来看看PropertySourcesPlaceholderConfigurer的继承体系把 1.BeanFactoryPostProcessor 定义一个用于修改容器中bean definition的属性的接口.其实现类在一般类使用前先实例化,并对其他类的属性进行修改. 这跟BeanPostProcessor有明显的区别,BeanPostProcessor是修改bean实例的. 2.PropertiesLoaderSupport 加载properties文件的抽象类. 这边具体的加载逻辑是委托PropertiesLoaderUtils#fillProperties实现 3.PropertyResourceConfigurer bean definition中占位符的替换就是这个抽象类实现的. 实现BeanFactoryPostProcessor#postProcessBeanFactory,迭代容器的中的类定义,进行修改 具体如何修改就通过钩子processProperties交由子类实现 4.PlaceholderConfigurerSupport 使用visitor设计模式,通过BeanDefinitionVisitor和StringValueResolver更新属性 StringValueResolver是一个转化String类型数据的接口,真正更新属性的api实现竟然是在 PropertyPlaceholderHelper#parseStringValue 5.PropertySourcesPlaceholderConfigurer 覆写postProcessorBeanFactory api定义解析流程 (编辑:安卓应用网) 【声明】本站内容均来自网络,其相关言论仅代表作者个人观点,不代表本站立场。若无意侵犯到您的权利,请及时与联系站长删除相关内容! |
