java占位符实现原理,Spring占位符Placeholder的实现原理解析

占位符Placeholder的使用

xml中的配置:

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.xsd

http://www.springframework.org/schema/context

http://www.springframework.org/schema/context/spring-context.xsd"

default-lazy-init="false">

实现原理

前面在Spring中自定义标签的解析中分析到context:property-placeholder这种自定义标签的解析流程如下:

基于SPI机制,扫描所有类路径下jar中/META-INFO/spring.handlers文件,并将这些文件读取为一个key为namespace,value为具体NameSpaceHandler的Map结构。

根据bean标签名获得xml上方的namespace,然后根据namespace从第一步中的map中获得具体的NameSpaceHandler。

调用NameSpaceHandler的init()方法进行初始化,此方法一般会将负责解析各种localName的BeanDefinitionParser解析器注册到一个map中。

根据localName=property-placeholder从上一步中获得具体的BeanDefinitionParser解析器,并调用其parse()方法进行解析。

在这里NameSpaceHandler为ContextNamespaceHandler,而BeanDefinitionParser解析器为PropertyPlaceholderBeanDefinitionParser,所以我们观察的重点为PropertyPlaceholderBeanDefinitionParser的parse()方法。

注册PropertySourcesPlaceholderConfigurer

parse()方法位于父类AbstractBeanDefinitionParser,先来看下继承关系,后面的代码使用了大量的模板方法模式,将会在这几个类中来回切换:

433e8f7ca71c41e170c13ea10f90af65.png

org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#parse

public final BeanDefinition parse(Element element, ParserContext parserContext) {

// 调用子类org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser.parseInternal

AbstractBeanDefinition definition = parseInternal(element, parserContext);

if (definition != null && !parserContext.isNested()) {

try {

// 生成一个ID

String id = resolveId(element, definition, parserContext);

if (!StringUtils.hasText(id)) {

parserContext.getReaderContext().error(

"Id is required for element '" + parserContext.getDelegate().getLocalName(element)

+ "' when used as a top-level tag", element);

}

String[] aliases = null;

if (shouldParseNameAsAliases()) {

String name = element.getAttribute(NAME_ATTRIBUTE);

if (StringUtils.hasLength(name)) {

aliases = StringUtils.trimArrayElements(StringUtils.commaDelimitedListToStringArray(name));

}

}

BeanDefinitionHolder holder = new BeanDefinitionHolder(definition, id, aliases);

// 注册BD

registerBeanDefinition(holder, parserContext.getRegistry());

if (shouldFireEvents()) {

BeanComponentDefinition componentDefinition = new BeanComponentDefinition(holder);

postProcessComponentDefinition(componentDefinition);

parserContext.registerComponent(componentDefinition);

}

}

catch (BeanDefinitionStoreException ex) {

String msg = ex.getMessage();

parserContext.getReaderContext().error((msg != null ? msg : ex.toString()), element);

return null;

}

}

return definition;

}

org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#parseInternal

protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {

BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();

String parentName = getParentName(element);

if (parentName != null) {

builder.getRawBeanDefinition().setParentName(parentName);

}

// 获取子类PropertyPlaceholderBeanDefinitionParser返回的PropertySourcesPlaceholderConfigurer

Class> beanClass = getBeanClass(element);

if (beanClass != null) {

builder.getRawBeanDefinition().setBeanClass(beanClass);

}

else {

String beanClassName = getBeanClassName(element);

if (beanClassName != null) {

builder.getRawBeanDefinition().setBeanClassName(beanClassName);

}

}

builder.getRawBeanDefinition().setSource(parserContext.extractSource(element));

BeanDefinition containingBd = parserContext.getContainingBeanDefinition();

if (containingBd != null) {

// Inner bean definition must receive same scope as containing bean.

builder.setScope(containingBd.getScope());

}

if (parserContext.isDefaultLazyInit()) {

// Default-lazy-init applies to custom bean definitions as well.

builder.setLazyInit(true);

}

// 又是一个模板方法模式

/**

* @see org.springframework.context.config.PropertyPlaceholderBeanDefinitionParser#doParse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext, org.springframework.beans.factory.support.BeanDefinitionBuilder)

*/

doParse(element, parserContext, builder);

return builder.getBeanDefinition();

}

org.springframework.context.config.PropertyPlaceholderBeanDefinitionParser#getBeanClass

protected Class> getBeanClass(Element element) {

if (SYSTEM_PROPERTIES_MODE_DEFAULT.equals(element.getAttribute(SYSTEM_PROPERTIES_MODE_ATTRIBUTE))) {

return PropertySourcesPlaceholderConfigurer.class; // 新版返回这个

}

return org.springframework.beans.factory.config.PropertyPlaceholderConfigurer.class;

}

org.springframework.context.config.PropertyPlaceholderBeanDefinitionParser#doParse

protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {

// 调用父类的doParse

super.doParse(element, parserContext, builder);

builder.addPropertyValue("ignoreUnresolvablePlaceholders",

Boolean.valueOf(element.getAttribute("ignore-unresolvable")));

String systemPropertiesModeName = element.getAttribute(SYSTEM_PROPERTIES_MODE_ATTRIBUTE);

if (StringUtils.hasLength(systemPropertiesModeName) &&

!systemPropertiesModeName.equals(SYSTEM_PROPERTIES_MODE_DEFAULT)) {

builder.addPropertyValue("systemPropertiesModeName", "SYSTEM_PROPERTIES_MODE_" + systemPropertiesModeName);

}

if (element.hasAttribute("value-separator")) {

builder.addPropertyValue("valueSeparator", element.getAttribute("value-separator"));

}

if (element.hasAttribute("trim-values")) {

builder.addPropertyValue("trimValues", element.getAttribute("trim-values"));

}

if (element.hasAttribute("null-value")) {

builder.addPropertyValue("nullValue", element.getAttribute("null-value"));

}

}

org.springframework.context.config.AbstractPropertyLoadingBeanDefinitionParser#doParse

protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {

// 解析标签的各种属性

String location = element.getAttribute("location");

if (StringUtils.hasLength(location)) {

location = parserContext.getReaderContext().getEnvironment().resolvePlaceholders(location);

String[] locations = StringUtils.commaDelimitedListToStringArray(location);

builder.addPropertyValue("locations", locations);

}

String propertiesRef = element.getAttribute("properties-ref");

if (StringUtils.hasLength(propertiesRef)) {

builder.addPropertyReference("properties", propertiesRef);

}

String fileEncoding = element.getAttribute("file-encoding");

if (StringUtils.hasLength(fileEncoding)) {

builder.addPropertyValue("fileEncoding", fileEncoding);

}

String order = element.getAttribute("order");

if (StringUtils.hasLength(order)) {

builder.addPropertyValue("order", Integer.valueOf(order));

}

builder.addPropertyValue("ignoreResourceNotFound",

Boolean.valueOf(element.getAttribute("ignore-resource-not-found")));

builder.addPropertyValue("localOverride",

Boolean.valueOf(element.getAttribute("local-override")));

builder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);

}

总结一下,其实上面这么多代码调来调去,只有一个目的,就是向spring容器中注入一个BeanDefinition,这个BeanDefinition有两个最重要的属性:

BeanClass为PropertySourcesPlaceholderConfigurer。

有一个属性为location,对应properties文件的位置。

PropertySourcesPlaceholderConfigurer的调用

上面向spring容器中注入一个PropertySourcesPlaceholderConfigurer类型BeanDefinition,先来看下这个类的继承关系:

2805d45b16ba68a0c698e671be6624e2.png

从上图的继承关系可以看出PropertySourcesPlaceholderConfigurer实现了BeanFactoryPostProcessor,所以这个类的核心方法为postProcessBeanFactory()。

public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {

if (this.propertySources == null) { // null

this.propertySources = new MutablePropertySources();

if (this.environment != null) {

// environment中存储的是系统属性和环境变量

this.propertySources.addLast(

new PropertySource(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, this.environment) {

@Override

@Nullable

public String getProperty(String key) {

return this.source.getProperty(key);

}

}

);

}

try {

// 加载application.properties为Properties,包装为PropertySource

PropertySource> localPropertySource =

new PropertiesPropertySource(LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME, mergeProperties());

if (this.localOverride) {

this.propertySources.addFirst(localPropertySource);

}

else {

this.propertySources.addLast(localPropertySource);

}

}

catch (IOException ex) {

throw new BeanInitializationException("Could not load properties", ex);

}

}

// 处理占位符

processProperties(beanFactory, new PropertySourcesPropertyResolver(this.propertySources));

this.appliedPropertySources = this.propertySources;

}

上面的方法的前面一大截的主要作用为将系统属性、环境变量以及properties文件中的属性整合到MutablePropertySources中,这样就可以直接调用MutablePropertySources.getProperties()方法根据属性名拿到对应的属性值了。MutablePropertySources里面其实是一个Map的链表,这样就可以先遍历链表,然后再根据属性名从Map中找到对应的属性值。

protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess,

final ConfigurablePropertyResolver propertyResolver) throws BeansException {

propertyResolver.setPlaceholderPrefix(this.placeholderPrefix); // ${

propertyResolver.setPlaceholderSuffix(this.placeholderSuffix); // }

propertyResolver.setValueSeparator(this.valueSeparator); // :

// 下面的doProcessProperties会回调这个lambda表达式

// 真正的解析逻辑在resolveRequiredPlaceholders

/**

* @see AbstractPropertyResolver#resolveRequiredPlaceholders(java.lang.String)

*/

StringValueResolver valueResolver = strVal -> {

String resolved = (this.ignoreUnresolvablePlaceholders ?

propertyResolver.resolvePlaceholders(strVal) :

propertyResolver.resolveRequiredPlaceholders(strVal));

if (this.trimValues) {

resolved = resolved.trim();

}

return (resolved.equals(this.nullValue) ? null : resolved);

};

// 这里会遍历所有的BD,挨个处理占位符

doProcessProperties(beanFactoryToProcess, valueResolver);

}

org.springframework.beans.factory.config.PlaceholderConfigurerSupport#doProcessProperties

protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,

StringValueResolver valueResolver) {

BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);

String[] beanNames = beanFactoryToProcess.getBeanDefinitionNames();

for (String curName : beanNames) {

// Check that we're not parsing our own bean definition,

// to avoid failing on unresolvable placeholders in properties file locations.

if (!(curName.equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) {

BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName);

try {

// 遍历BD

visitor.visitBeanDefinition(bd);

}

catch (Exception ex) {

throw new BeanDefinitionStoreException(bd.getResourceDescription(), curName, ex.getMessage(), ex);

}

}

}

// New in Spring 2.5: resolve placeholders in alias target names and aliases as well.

beanFactoryToProcess.resolveAliases(valueResolver);

// New in Spring 3.0: resolve placeholders in embedded values such as annotation attributes.

beanFactoryToProcess.addEmbeddedValueResolver(valueResolver);

}

org.springframework.beans.factory.config.BeanDefinitionVisitor#visitBeanDefinition

public void visitBeanDefinition(BeanDefinition beanDefinition) {

visitParentName(beanDefinition);

visitBeanClassName(beanDefinition);

visitFactoryBeanName(beanDefinition);

visitFactoryMethodName(beanDefinition);

visitScope(beanDefinition);

if (beanDefinition.hasPropertyValues()) {

// 遍历所有的属性

visitPropertyValues(beanDefinition.getPropertyValues());

}

if (beanDefinition.hasConstructorArgumentValues()) {

ConstructorArgumentValues cas = beanDefinition.getConstructorArgumentValues();

visitIndexedArgumentValues(cas.getIndexedArgumentValues());

visitGenericArgumentValues(cas.getGenericArgumentValues());

}

}

org.springframework.beans.factory.config.BeanDefinitionVisitor#visitPropertyValues

protected void visitPropertyValues(MutablePropertyValues pvs) {

PropertyValue[] pvArray = pvs.getPropertyValues();

for (PropertyValue pv : pvArray) {

// 解析占位符

Object newVal = resolveValue(pv.getValue());

if (!ObjectUtils.nullSafeEquals(newVal, pv.getValue())) {

// 将新的value替换BD中旧的

pvs.add(pv.getName(), newVal);

}

}

}

resolveValue()方法中会回调到之前的lambda表达式StringValueResolv真正开始解析,也就是根据属性名从PropertySources中取值。

总结一下PropertySourcesPlaceholderConfigurer#postProcessBeanFactory()方法:这个方法会在Bean实例化之前完成对Spring容器中所有BeanDefinition中带有占位符的属性进行解析,这样在Bean实例化后就能被赋予正确的属性了。

到此这篇关于Spring占位符Placeholder的实现原理的文章就介绍到这了,更多相关Spring占位符Placeholder内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值