通过@Value注解获取properties的值

通过@Value注解获取properties的值

目的

为了让代码能获取到指定配置文件里的特定配置。

解决方案:

接入使用:

  1. 在需要注入参数的变量上加上@Value(“${xxx:default}”)
    xxx是properties中的配置名,default是默认值,若是没有默认值,也没有设置ignoreUnresolvablePlaceholders,那么就会报错IllegalArgumentException。

  2. 先在applicationContext.xml中声明

    <bean id= "placeHolder" class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" >  
    <property name="location" >
    <value>classpath*:*.properties</value >
    </property>
    </bean>

或者

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

可以看到事实上是注册了一个PropertyPlaceholderConfigurer,那么就奇怪了,声明这么一个bean怎么就能注入配置了呢?

PropertyPlaceholderConfigurer原理

看一下PropertyPlaceholderConfigurer的类图:

2019-01-10-22-44-35-2019110

注意这个BeanFactoryPostProcessor接口,Spring提供了的一种叫做BeanFactoryPostProcessor的容器扩展机制。这里PrepertyPlaceholderConfigurer实现了BeanFactoryPostProcesser接口,并实现了postProcessBeanFactory()方法即当Spring容器的BeanFactory被构造成功之后会调用这个方法。

public interface BeanFactoryPostProcessor {
void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException;
}

那我们来看看父类的PropertyResourceConfigurer方法postProcessBeanFactory。因为这个类继承了Spring的BeanFactoryPostProcesser接口,所以这个方法一定是操作BeanFactory的。

@Override
public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
try {
       //1.获取当前容器配置的所有Properties文件,可能由多个文件merge而来
Properties mergedProps = mergeProperties();

//2.如果需要的话,将Properties文件的内容进行转化,因为默认的Preperties都是String的key-value形式。
//Spring提供的默认方式是不转化,保持String,String的key-value
convertProperties(mergedProps);

//3.由子类继承,对容器与Properties进行操作,即value注入。
processProperties(beanFactory, mergedProps);
}
catch (IOException ex) {
throw new BeanInitializationException("Could not load properties", ex);
}
}

第一二步就是对location里定义的配置文件进行处理,然后得到一个拥有所有配置信息的Properties对象。

第三步就是将beanFactory,和Properties交给子类的processProperties方法实现处理;

那么直接看PropertyPlaceholderConfigurer中processProperties实现:

@Override
protected void processProperties(ConfigurableListableBeanFactory beanFactoryToProcess, Properties props)
throws BeansException {
     //1.声明一个支持value为String类型的Resolver
StringValueResolver valueResolver = new PlaceholderResolvingStringValueResolver(props);
//2.将key-value注入到BeanFactory的某些bean中
doProcessProperties(beanFactoryToProcess, valueResolver);
}

跟进去看doProcessProperties(beanFactoryToProcess, valueResolver);

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.
// 只有同一个容器内的才可以进行value注入,同时应该避免掉操作本身,避免进入循环递归
if (!(curName.equals(this.beanName) && beanFactoryToProcess.equals(this.beanFactory))) {
BeanDefinition bd = beanFactoryToProcess.getBeanDefinition(curName);
try {
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);
}

可以看到,我们已经获取到了容器内所有的BeanDefinition,然后通过visitor.visitBeanDefinition(bd);完成了参数的注入。

2019-01-10-23-15-43-2019110

解析的过程中可以发现,我们注有@Value注解的参数,对应的BeanDefinition中的properties中对应参数的value其实已经注入了我们在@Value(#{xxx})中注入的通配符”#{xxx}”,这与我们直接在xml配置文件中为bean注入值的理解是一致的。

后面的代码就是通过解析通配符,然后再从之前已经准备好的Properties中找到对应的配置值进行替换。

流程总结:

2019-01-10-23-07-01-2019110

Spirng在生命周期里关于Bean的处理大概可以分为下面几步:

  1. 加载Bean定义(从xml或者从@Import等)
  2. 处理BeanFactoryPostProcessor
  3. 实例化Bean
  4. 处理Bean的property注入
  5. 处理BeanPostProcessor

所以我们@Value其实是在第一步就将通配符注入到bean的定义中,然后在第二步时完成值的替换,到我们实例化bean的时候其实BeanDefinition中的值已经是我们所期望看到的配置了。

这也是为什么,如果@Value(“#{xxx}”)注解,如果没有给默认值,又找不到对应的配置,在spring容器启动的时候就报错了。

参考

http://www.cnblogs.com/kingszelda/p/7261156.html

https://blog.csdn.net/ljc1026774829/article/details/80492996

Your browser is out-of-date!

Update your browser to view this website correctly. Update my browser now

×