博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
Spring中property-placeholder的使用与解析
阅读量:2289 次
发布时间:2019-05-09

本文共 5948 字,大约阅读时间需要 19 分钟。

我们在基于spring开发应用的时候,一般都会将数据库的配置放置在properties文件中. 代码分析的时候,涉及的知识点概要:

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

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

property的使用

在xml文件中配置properties文件

这样/src/main/resources/foo.properties文件就会被spring加载 如果想使用多个配置文件,可以添加order字段来进行排序

使用PropertySource注解配置

,方便添加property文件到环境.

@Configuration@PropertySource("classpath:foo.properties")public class PropertiesWithJavaConfig {   @Bean   public static PropertySourcesPlaceholderConfigurer propertySourcesPlaceholderConfigurer() {      return new PropertySourcesPlaceholderConfigurer();   }}

properties的注入与使用

  1. @Value( "${jdbc.url}" )private String jdbcUrl;

还可以添加一个默认值

@Value( "${jdbc.url:aDefaultUrl}" )private String jdbcUrl;
  1. 在Spring的xml配置文件中获取

源码解析

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解析
    • 生成bean id,使用BeanNameGenerator生成,或者直接读取id属性
    • 处理name 与别名aliases
    • 往容器中注册bean
    • 进行事件触发
  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定义解析流程

转载地址:http://tybnb.baihongyu.com/

你可能感兴趣的文章
[模式识别] [讲义] 学习理论
查看>>
[模式识别] [讲义] 集成学习-Adaboost训练算法
查看>>
[模式识别] [讲义] 错误率-Bayes与最近邻对比
查看>>
[模式识别] [讲义] 深度学习
查看>>
[深入理解Java虚拟机] 第3章 垃圾收集器与内存分配策略
查看>>
[深入理解Java虚拟机] 第4、5章 性能监控与调优
查看>>
[深入理解Java虚拟机] 第6章 类文件结构
查看>>
[深入理解Java虚拟机] 第7章 虚拟机类加载机制
查看>>
[深入理解Java虚拟机] 第8章 虚拟机字节码执行引擎
查看>>
[深入理解Java虚拟机] 第10章 早期(编译期)优化
查看>>
[深入理解Java虚拟机] 第11章 晚期(运行期)优化
查看>>
[深入理解Java虚拟机] 第12章 Java内存模型与线程
查看>>
[深入理解Java虚拟机] 第13章 线程安全与锁优化
查看>>
[Java并发编程实战] 第2章 线程安全性
查看>>
[Java并发编程实战] 第3章 对象的共享
查看>>
[Java并发编程实战] 第4章 对象的组合
查看>>
[Java并发编程实战] 第5章 基础构建模块
查看>>
[Java并发编程实战] 第6章 任务执行
查看>>
[Java并发编程实战] 第7章 取消与关闭
查看>>
[Java并发编程实战] 第8章 线程池的使用
查看>>