示例:
@Data
public class StudentA {
private String username;
private String password;
}
<!--spring配置文件-->
<context:property-placeholder location="classpath:application.properties"/>
<bean id = "studentA" class="com.study.dongsq.bean.StudentA">
<property name="username" value="${test.name}"/>
<property name="password" value="${test.password}"/>
</bean>
源码解析:
在spring源码之标签解析中总结过标签解析过程:
1、根据当前解析标签的头信息找到对应的namespaceUri
2、加载spring所有jar中spring.handlers文件,并建立映射关系
3、根据namespaceUri从映射关系中找到对应的实现了NameSpaceHandler接口的类
4、调用init()方法并返回NamespaceHandler(注:init()注册了各种自定义标签的解析类)
5、调用NamespaceHandler(和namespaceUri进行了映射)根据标签注册的解析类的parse()方法
所以根据标签<context:property-placeholder >会找到这个解析类:
调用PropertyPlaceholderBeanDefinitionParser的父类AbstractBeanDefinitionParser的parse()方法
public final BeanDefinition parse(Element element, ParserContext parserContext) {
AbstractBeanDefinition definition = parseInternal(element, parserContext);
@Override
protected final AbstractBeanDefinition parseInternal(Element element, ParserContext parserContext) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition();
...
...
Class<?> beanClass = getBeanClass(element);
if (beanClass != null) {
builder.getRawBeanDefinition().setBeanClass(beanClass);
}
...
...
doParse(element, parserContext, builder);
return builder.getBeanDefinition();
}
getBeanClass返回PropertySourcesPlaceholderConfigurer
所以在这里把PropertySourcesPlaceholderConfigurer变成BeanDefinition,
并调用super.doParse把标签 <context:property-placeholder> 的 location属性放入locations数组保存到PropertySourcesPlaceholderConfigurer的父类PropertiesLoaderSupport中
标签解析完了之后是调用postProcessBeanFactory方法
因为PropertySourcesPlaceholderConfigurer实现了BeanFactoryPostProcessor接口
所以会调用到postProcessBeanFactory()方法中,但是在调这个方法之前,先将
PropertySourcesPlaceholderConfigurer进行实例化,再调用postProcessBeanFactory()方法
因为PropertySourcesPlaceholderConfigurer实现了EnvironmentAware接口,所以实例化的时候,会调用到ApplicationContextAwareProcessor中的postProcessBeforeInitialization的方法,对实现各种aware接口的进行方法调用(具体过程看spring源码之生命周期)
@Override
@Nullable
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
if (!(bean instanceof EnvironmentAware || bean instanceof EmbeddedValueResolverAware ||
bean instanceof ResourceLoaderAware || bean instanceof ApplicationEventPublisherAware ||
bean instanceof MessageSourceAware || bean instanceof ApplicationContextAware)){
return bean;
}
...
...
else {
invokeAwareInterfaces(bean);
}
return bean;
}
可以看到上图中是在上下文中获取的Environment,并给PropertySourcesPlaceholderConfigurer中的Environment变量赋值了。那上下文中的Environment又是怎么来的?
上下文在启动的时候,会调用setConfigLocation()方法,如图:
在上下文AbstractApplicationContext中创建environment
在new StandardEnvironment()的时候,会调用customizePropertySources()方法,如图:
将PropertySourcesPlaceholderConfigurer实例化、调用了setEnvironment方法之后,会调用其postProcessBeanFactory方法,这时候environment就不为null了
@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
if (this.propertySources == null) {
this.propertySources = new MutablePropertySources();
if (this.environment != null) {
//把environment对象封装成的PropertySource对象加入到
this.propertySources.addLast(
//把environment对象封装成PropertySource对象MutablePropertySources中的list中
new PropertySource<Environment>(ENVIRONMENT_PROPERTIES_PROPERTY_SOURCE_NAME, this.environment) {
@Override
@Nullable
//source就是environment对象
public String getProperty(String key) {
return this.source.getProperty(key);
}
}
);
}
try {
//加载本地配置文件中的属性值包装成properties对象后,最终包装成PropertySource对象
PropertySource<?> localPropertySource =
new PropertiesPropertySource(LOCAL_PROPERTIES_PROPERTY_SOURCE_NAME, mergeProperties());
//加入到MutablePropertySources中的list中
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;
}
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
final ConfigurablePropertyResolver propertyResolver) throws BeansException {
//设置占位符的前缀后缀 ${}
propertyResolver.setPlaceholderPrefix(this.placeholderPrefix);
propertyResolver.setPlaceholderSuffix(this.placeholderSuffix);
//设分割符 :
propertyResolver.setValueSeparator(this.valueSeparator);
//这里是一个函数式接口
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);
};
//核心流程。把占位符${xxx}替换成真正的值
doProcessProperties(beanFactoryToProcess, valueResolver);
}
protected void doProcessProperties(ConfigurableListableBeanFactory beanFactoryToProcess,
StringValueResolver valueResolver) {
BeanDefinitionVisitor visitor = new BeanDefinitionVisitor(valueResolver);
...
这里会调到BeanDefinitionVistor的resolveStringValue方法,如下图所示
...
...
//把内嵌的Value解析器设置到BeanFactory中.为@Value的依赖注入做准备
beanFactoryToProcess.addEmbeddedValueResolver(valueResolver);
}
然后跳转到
doResolvePlaceholders方法会调到parseStringValue方法
protected String parseStringValue(
String value, PlaceholderResolver placeholderResolver, @Nullable Set<String> visitedPlaceholders) {
...
...
StringBuilder result = new StringBuilder(value);
while (startIndex != -1) {
int endIndex = findPlaceholderEndIndex(result, startIndex);
if (endIndex != -1) {
...
...
//递归解析 ${${}}
// Recursive invocation, parsing placeholders contained in the placeholder key.
placeholder = parseStringValue(placeholder, placeholderResolver, visitedPlaceholders);
// Now obtain the value for the fully resolved key...
//这里会调用到 this::getPropertyAsRawString 也是函数式接口
String propVal = placeholderResolver.resolvePlaceholder(placeholder);
if (propVal == null && this.valueSeparator != null) {
int separatorIndex = placeholder.indexOf(this.valueSeparator);
if (separatorIndex != -1) {
String actualPlaceholder = placeholder.substring(0, separatorIndex);
//获取 ":"号后面的默认值
String defaultValue = placeholder.substring(separatorIndex + this.valueSeparator.length());
//":"前面的参数解析
propVal = placeholderResolver.resolvePlaceholder(actualPlaceholder);
//如果解析不到,则用默认值
if (propVal == null) {
propVal = defaultValue;
}
}
}
...
...
}
函数式接口
@Value中占位符的解析
原理和标签中的解析差不多,AutowiredAnnotationBeanPostProcessor完成对@Value注解的收集,会对有@Value注解的属性依赖注入
这样就走到和标签占位符解析相同的代码。
大致流程总结:
1、spring在启动的时候会在上下文中创建Environment,并将系统变量放入environment对象中。
2、通过aware接口调用,将environment对象传给PropertySourcesPlaceholderConfigurer。
3、将environment对象放入MutablePropertySources中,并重写getProperty方法。
4、收集本地配置文件中的属性值包装成properties对象后,最终包装成PropertySource对象。
5、遍历所有的BeanDefinition,修改BeanDefinition中的PropertyValues中的每一个属性值,把属性值有${XXX}修改成真正的参数值
6、如果是@Value注解,则先收集注解,再进行属性的依赖注入,解析过程和标签的类似
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/13826.html