总
上篇博客中,介绍了对于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类去注入值了.