property-placeholder标签解析

赵云胡说--property-placeholder标签解析

上篇博客中,介绍了对于component-scan标签的解析,此篇博客会主要分析下property-placeholder 标签的解析过程中做了哪些事情.

解析前的准备

首先在xml文件的beans标签中引入context标签
在这里插入图片描述

  <context:property-placeholder location="classpath*:a.properties"></context:property-placeholder>

在xml文件中加入上面的代码 location指的是扫描的文件
当spring启动后,解析到该段时,是会进入到自定义标签的解析的.

public class ContextNamespaceHandler extends NamespaceHandlerSupport {
	@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());
	}
}

对标签的解析

context所对应的handler会注册上述的八个标签,其中当使用的是property-placeholder,PropertyPlaceholderBeanDefinitionParser会负责去解析该标签
在这里插入图片描述
PropertyPlaceholderBeanDefinitionParser类中都没有重写parse() 方法,最后重写了 parse() 方法的类是AbstractBeanDefinitionParser,所以在此列出
AbstractBeanDefinitionParser的parse()

	public final BeanDefinition parse(Element element, ParserContext parserContext) {
		//获取到一个BeanDefinition,这个BeanDefinition实际上就是PropertySourcesPlaceholderConfigurer这个类
		AbstractBeanDefinition definition = parseInternal(element, parserContext);
		if (definition != null && !parserContext.isNested()) {
			try {
				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);
				//在此将BeanDefinition注册到容器中
				registerBeanDefinition(holder, parserContext.getRegistry());
			....
		return definition;
	}

AbstractSingleBeanDefinitionParser的parseInternal(Element element, ParserContext parserContext)

protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
		//创建一个BeanDefinition构建器
		BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
		String parentName = getParentName(element);
		if (parentName != null) {
			builder.getRawBeanDefinition().setParentName(parentName);
		}
		//这是个钩子方法,使用了模板模式,实际上执行的是PropertyPlaceholderBeanDefinitionParser的getBeanClass方法
		//返回的是PropertySourcesPlaceholderConfigurer.class或者PropertyPlaceholderConfigurer.class
		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);
		}
		//这是个钩子方法,使用了模板模式,实际上执行的是PropertyPlaceholderBeanDefinitionParser
		//的doParse方法去解析标签属性这其中会把location标签解析并存到BeanDenitions中
		doParse(element, parserContext, builder);
		return builder.getBeanDefinition();
	}

PropertyPlaceholderBeanDefinitionParser的getBeanClass(Element element)

protected Class<?> getBeanClass(Element element) {
		//一般默认返回的都是PropertySourcesPlaceholderConfigurer.class
		if (SYSTEM_PROPERTIES_MODE_DEFAULT.equals(element.getAttribute(SYSTEM_PROPERTIES_MODE_ATTRIBUTE))) {
			return PropertySourcesPlaceholderConfigurer.class;
		}
		//这是以前版本使用的解析类PropertyPlaceholderConfigurer.class;
		return org.springframework.beans.factory.config.PropertyPlaceholderConfigurer.class;
	}

注册类的分析

实质上对于property-placeholder的解析就是将为了向spring容器中添加PropertySourcesPlaceholderConfigurer这个类,那么这个类有哪些作用呢?
在这里插入图片描述
看一眼该类的继承图,首先实现了EnvironmentAware.因此该类中是有容器中关于Environment的信息,它实现了BeanFactoryPostProcessor接口,实现该接口的类,会优先实例化,并在其他类执行前执行各自的postProcessBeanFactory方法,这些内容将在后续博客中介绍.只要知道,因为实现了BeanFactoryPostProcessor接口,这个类会被实例化,同时postProcessBeanFactory方法会被执行就可以了
PropertySourcesPlaceholderConfigurer的postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory)的方法

	public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
		if (this.propertySources == null) {
			this.propertySources = new MutablePropertySources();
			if (this.environment != null) {
				//将environment存入到PropertySourcesPlaceholderConfigurer对象中
				this.propertySources.addLast(
					new PropertySource<Environment>(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, this.environment) {
						@Override
						@Nullable
						public String getProperty(String key) {
							return this.source.getProperty(key);
						}
					}
				);
			}
			try {
				//读取properties文件的信息,并整合,放入到propertySources中
				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;
	}

PropertiesLoaderSupport的mergeProperties()方法

	protected Properties mergeProperties() throws IOException {
		Properties result = new Properties();
		
		if (this.localOverride) {
			// Load properties from file upfront, to let local properties override.
			loadProperties(result);
		}

		if (this.localProperties != null) {
			for (Properties localProp : this.localProperties) {
				CollectionUtils.mergePropertiesIntoMap(localProp, result);
			}
		}

		if (!this.localOverride) {
			// Load properties from file afterwards, to let those properties override.
			loadProperties(result);
		}

		return result;
	}
	
	protected void loadProperties(Properties props) throws IOException {
		if (this.locations != null) {
			for (Resource location : this.locations) {
				if (logger.isTraceEnabled()) {
					logger.trace("Loading properties file from " + location);
				}
				try {
					PropertiesLoaderUtils.fillProperties(
							props, new EncodedResource(location, this.fileEncoding), this.propertiesPersister);
				}
				catch (FileNotFoundException | UnknownHostException ex) {
					if (this.ignoreResourceNotFound) {
						if (logger.isDebugEnabled()) {
							logger.debug("Properties resource not found: " + ex.getMessage());
						}
					}
					else {
						throw ex;
					}
				}
			}
		}
	}

在上述方法中,比较核心的方法就是loadProperties(result)了,在该方法中,可以看到是对this.locations的遍历,之后会加载每个properties,而this.locations这个对象是在哪里被添加的呢?在本篇博客AbstractSingleBeanDefinitionParser的parseInternal(Element element, ParserContext parserContext)中的doParse()方法里面会解析标签的locations,然后在实例化bean的时候会初始化的.

PropertySourcesPlaceholderConfigurer的processProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
final ConfigurablePropertyResolver propertyResolver)方法

	protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
			final ConfigurablePropertyResolver propertyResolver) throws BeansException {
		//设置property的解释器的字符前缀("${")
		propertyResolver.setPlaceholderPrefix(this.placeholderPrefix);
		//设置property的解释器的字符后缀("}")
		propertyResolver.setPlaceholderSuffix(this.placeholderSuffix);
		//设置分割符号":"
		propertyResolver.setValueSeparator(this.valueSeparator);
		//这里使用匿名对象的方法去实现了一个StringValueResolver 
		//这里面resolveStringValue(String strVal)的逻辑是
		//ConfigurablePropertyResolver 的两个方法,其大致就是分析出来
		//带有${}的特殊字符串,取出里面的值,然后从this.propertySources中去拿到
		//这个值的配置值
		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);
		};
		//在这里面将valueResolver添加到了beanFactoryToProcess的embeddedValueResolvers中
		doProcessProperties(beanFactoryToProcess, valueResolver);
	}

当valueResolver被加入到容器时,它会在依赖注入@value注解等场景下被调用resolveStringValue(String strVal)方法,最终会把properties文件中配置的值给解析赋值进去

对于property-placeholder标签解析,只是将PropertySourcesPlaceholderConfigurer或者PropertyPlaceholderConfigurer类设置好并且放入到容器里面,又因为PropertySourcesPlaceholderConfigurer实现了BeanFactoryPostProcessor接口,所以会提前实例化执行实现的方法,在执行方法时完成了对配置文件的读取加载,并把property的Resolver注册到容器中.当调用到resolveStringValue(String strVal)方法时,就可以依靠PropertySourcesPlaceholderConfigurer类去注入值了.

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值