Spring中property-placeholder的使用与解析详解

 更新时间:2018年05月08日 15:30:56   作者:出门向左  
本篇文章主要介绍了Spring中property-placeholder的使用与解析详解,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧

我们在基于spring开发应用的时候,一般都会将数据库的配置放置在properties文件中.

代码分析的时候,涉及的知识点概要:

1.NamespaceHandler 解析xml配置文件中的自定义命名空间
2.ContextNamespaceHandler 上下文相关的解析器,这边定义了具体如何解析property-placeholder的解析器
3.BeanDefinitionParser 解析bean definition的接口
4.BeanFactoryPostProcessor 加载好bean definition后可以对其进行修改
5.PropertySourcesPlaceholderConfigurer 处理bean definition 中的占位符

我们先来看看具体的使用吧

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解析

  1. 生成bean id,使用BeanNameGenerator生成,或者直接读取id属性
  2. 处理name 与别名aliases
  3. 往容器中注册bean
  4. 进行事件触发

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实现的.
扫描容器中的BeanFactoryPostProcessor,找到了这边需要的PropertySourcesPlaceholderConfigurer,并通过容器的getBean实例化

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定义解析流程

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持脚本之家。

相关文章

  • 基于Springboot的高校社团管理系统的设计与实现

    基于Springboot的高校社团管理系统的设计与实现

    本文将基于Springboot+Mybatis开发实现一个高校社团管理系统,系统包含三个角色:管理员、团长、会员。文中采用的技术有Springboot、Mybatis、Jquery、AjAX、JSP等,感兴趣的可以了解一下
    2022-07-07
  • 基于SSM框架之个人相册示例代码

    基于SSM框架之个人相册示例代码

    本篇文章主要介绍了基于SSM框架之个人相册示例代码,具有一定的参考价值,感兴趣的小伙伴们可以参考一下。
    2017-03-03
  • Java8新特性之字符串去重介绍

    Java8新特性之字符串去重介绍

    这篇文章主要介绍了Java8新特性之字符串去重介绍,新的字符串去重特性可以帮助减少应用中String对象的内存占用,目前该特性只适用于G1垃圾收集器,并且默认不被开启,需要的朋友可以参考下
    2014-09-09
  • mybatis-plus排除非表中字段的操作

    mybatis-plus排除非表中字段的操作

    这篇文章主要介绍了mybatis-plus排除非表中字段的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
    2020-12-12
  • 一篇文章带你入门Java字面量和常量

    一篇文章带你入门Java字面量和常量

    这篇文章主要介绍了探究Java的常量,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2021-08-08
  • 带你快速入门掌握Spring的那些注解使用

    带你快速入门掌握Spring的那些注解使用

    注解是个好东西,注解是Java语法,被Java编译器检查,可以减少配置错误,这篇文章主要给大家介绍了关于Spring的那些注解使用的相关资料,文中通过实例代码介绍的非常详细,需要的朋友可以参考下
    2022-02-02
  • 亲手带你解决Debug Fastjson的安全漏洞

    亲手带你解决Debug Fastjson的安全漏洞

    这篇文章主要介绍了亲手带你解决Debug Fastjson的安全漏洞,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2020-06-06
  • Spring Boot Web应用开发 CORS 跨域请求支持

    Spring Boot Web应用开发 CORS 跨域请求支持

    本篇文章主要介绍了Spring Boot Web应用开发 CORS 跨域请求支持,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2017-05-05
  • 详解java如何集成swagger组件

    详解java如何集成swagger组件

    今天给大家带来的是关于Java的相关知识,文章围绕着java如何集成swagger组件展开,文中有非常详细的介绍及代码示例,需要的朋友可以参考下
    2021-06-06
  • java字符串遍历以及统计字符串中各类字符

    java字符串遍历以及统计字符串中各类字符

    这篇文章主要为大家详细介绍了java字符串遍历以及字符串中各类字符统计,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2018-03-03

最新评论