注入属性文件的值

按照以往的方式,我们总是直接把具体的字面量值填入代码进行字面量值的注入。如下所示:

 1 @Component
 2 public class Music {
 3     private String musicName = null;
 4     private Date publishTime = null;
 5 
 6     @Value("Dream")
 7     public void setMusicName(String musicName) {
 8         this.musicName = musicName;
 9     }
10 
11     @Value("2022/12/22")
12     public void setPublishTime(Date publishTime) {
13         this.publishTime = publishTime;
14     }
15 
16     // 省略getter方法
17 }

可以看到Music定义了三个属性。其值直接填在代码里,由@Value注解注入。直觉告诉我们,这种硬编码的注入方式并不友好。假如Bean的属性值由于某种原因需做变动,我们还得修改代码。非常麻烦,非常不好维护!要是能从某个属性文件里读取属性值之后作为字面量值进行注入,该有多好!这样,我们无需修改代码,只需修改属性文件就能达到修改Bean的属性值的目的。非常灵活,非常简单,非常方便,非常易于修改,非常易于维护!幸运的是,Spring确实提供了这样的支持。而这支持,得从Environment接口谈起。

Environment是Spring定义的一个接口,代表着当前正在运行的应用程序的环境,主要提供两种功能:一种是设置Profile;一种是能让开发人员比较方便地访问属性文件。至于何为Profile,我们将在其它章节另行介绍。现在重点关注的是与属性文件访问相关的内容。

具体而言,Spring实现了两种Environment接口:一种是StandardEnvironment,用于与Web无关的应用程序;一种是StandardServletEnvironment,用于与Web相关的应用程序。创建Spring容器时,Spring容器将会创建Environment,而Environment则会加载属性文件。因此,我们需要提供配置信息告诉Environment属性文件在哪。而这,可以通过Spring提供的@PropertySource注解指定。如下所示:

1 @Configuration
2 @ComponentScan("com.dream.controller")
3 @PropertySource("classpath:com/dream/app.properties")
4 public class ServletConfig {
5 }

我们指定的属性文件是 classpath:com/dream/app.properties ,位于com.dream包里。属性文件的内容如下:

music.name=Dream
music.publishtime=2022/12/22

于是,Environment能从@PropertySource注解指定的位置加载属性文件。之后,我们需把Environment注入我们的Bean里,以使我们的Bean能用Environment获取属性文件的属性值,按照属性名的匹配情况把相应的属性值注入我们的Bean里,如下所示:

 1 @Component
 2 public class Music {
 3     private Environment environment = null;
 4     private String musicName = null;
 5     private Date publishTime = null;
 6 
 7     @Autowired
 8     public Music(Environment environment) {
 9         this.environment = environment;
10         this.musicName = environment.getProperty("music.name");
11         this.publishTime = environment.getProperty("music.publishtime", Date.class);
12     }
13 
14     // 省略getter方法
15 }

通过@Autowired注解,我们告诉Spring容器把Environment注入Music的构造函数里。构造函数拿到Environment之后,以属性文件的属性名作为参数调用getProperty()方法获取属性值,把属性值赋给Bean的属性,完成字面量值的注入。getProperty()方法具有四种重载,签名如下:
1.String getProperty(String key)
2.String getProperty(String key, String defaultValue)
3.T getProperty(String key, Class<T> type)
4.T getProperty(String key, Class<T> type, T defaultValue)

大家一看便知怎么调用,无需详叙。重点在于,我们还可通过往@Value注解里填入属性占位符的方式注入属性文件的值。而这,需要我们创建PropertySourcesPlaceholderConfigurer类型的Bean,让它帮助我们针对这种方式进行处理。如下所示:

 1 @Configuration
 2 @ComponentScan("com.dream.controller")
 3 public class ServletConfig {
 4     @Bean
 5     public PropertySourcesPlaceholderConfigurer placeholderConfigurer(
 6             ResourceLoader resourceLoader) {
 7         var resourceUrl = "classpath:com/dream/app.properties";
 8         var resource = resourceLoader.getResource(resourceUrl);
 9         var configurer = new PropertySourcesPlaceholderConfigurer();
10         configurer.setLocations(resource);
11         return configurer;
12     }
13 }

可以看到先前添加的@PropertySource注解已被删除,却定义了一个具有ResourceLoader类型的参数的方法,用于创建PropertySourcesPlaceholderConfigurer类型的Bean。创建过程如下:
1.调用resourceLoader的getResource()方法获取属性文件这种资源。
2.创建PropertySourcesPlaceholderConfigurer类型的实例。
3.调用setLocations()方法,把属性文件这种资源交给PropertySourcesPlaceholderConfigurer加载。

注意:setLocations()方法的参数是 Resource... 类型的,可以指定多个代表属性文件的资源。

于是,Spring容器加载配置文件之后,PropertySourcesPlaceholderConfigurer类型的Bean就存在Spring容器里,帮助我们处理属性占位符。因此,我们还得修改Music如下:

 1 @Component
 2 public class Music {
 3     private String musicName = null;
 4     private Date publishTime = null;
 5 
 6     @Value("${music.name}")
 7     public void setMusicName(String musicName) {
 8         this.musicName = musicName;
 9     }
10 
11     @Value("${music.publishtime}")
12     public void setPublishTime(Date publishTime) {
13         this.publishTime = publishTime;
14     }
15 
16     // 省略getter方法
17 }

可以看到@Value注解注入的是属性占位符。属性占位符是这样构成的: ${属性文件里的属性名} ,也就是用 ${} 把属性文件里的属性名括起来。这样,实现了BeanFactoryPostProcessor接口的PropertySourcesPlaceholderConfigurer就能在Spring容器创建Bean之前做些事情:
1. 把由setLocations()方法指定的属性文件加载到Environment里。
2. 找出Bean的定义里的属性占位符,与Environment里的属性名进行匹配,并把匹配成功的属性占位符改成属性名对应的属性值。

于是,那些含有属性占位符的Bean的定义都被修改了,由属性占位符改成属性文件的值。之后,Spring容器根据修改之后的Bean的定义创建和装配Bean,注入的值自然是属性文件的值。

另外,如果配置文件是XML的话,可以这样配置:

1 <bean class="org.springframework.context.support.PropertySourcesPlaceholderConfigurer">
2     <property name="locations" value="classpath:com/dream/app.properties"/>
3 </bean>
4 <bean class="com.dream.controller.Music">
5     <property name="musicName" value="${music.name}" />
6     <property name="publishTime" value="${music.publishtime}" />
7 </bean>

为了方便配置,Spring还专门提供了<context:property-placeholder>元素,用于创建PropertySourcesPlaceholderConfigurer类型的Bean。因此,配置信息也能改成这样:

1 <context:property-placeholder locations ="classpath:com/dream/app.properties"/>
2 
3 <bean class="com.dream.controller.Music">
4     <property name="musicName" value="${music.name}" />
5     <property name="publishTime" value="${music.publishtime}" />
6 </bean>

还有,Environment除了能够加载属性文件,也能按照优先级从上到下加载这些参数:
1.Servlet初始化参数
2.Servlet上下文初始化参数
3.JNDI环境变量
4.JVM系统属性(也就是JVM的命令行参数)
5.操作系统环境变量

这意味着我们也能通过属性占位符注入这些参数的值。大家可以试试,这里不作过多介绍。

于是,关于怎样通过属性占位符注入属性文件的值,我们已经理清楚了。下章该谈谈Spring表达式语言,看看能用强大的Spring表达式语言注入怎样的字面量值。欢迎大家继续阅读,谢谢大家!

返回目录    下载代码

posted @ 2022-02-06 10:35  林雪波  阅读(102)  评论(0编辑  收藏  举报