思考

  1. PropertyPlaceholderConfigurer和<context:property-placeholder/>有何区别?
  2. @Value在Controller层和Service层使用有何不同?
  3. @Value和${key}在web中配置注意事项。
  4. PropertyPlaceholderConfigurer在容器里能存在多个bean吗 ?

关于PropertyPlaceholderConfigurer

1. PropertyPlaceholderConfigurer是个bean工厂后置处理器的实现,也就是 BeanFactoryPostProcessor接口的一个实现。PropertyPlaceholderConfigurer可以将上下文(配置文件)中的属性值放在另一个单独的标准java Properties文件中去。在XML文件中用${key}替换指定的properties文件中的值。这样的话,只需要对properties文件进行修改,而不用对xml配置文件进行修改。

2.在Spring中,使用PropertyPlaceholderConfigurer可以在XML配置文件中加入外部属性文件,当然也可以指定外部文件的编码,如:

<bean id="propertyConfigurer"class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="location">
<value>conf/sqlmap/jdbc.properties</value>
</property>
<property name="fileEncoding">
<value>UTF-8</value>
</property>
</bean>

当然也可以引入多个属性文件,如:

<bean id="propertyConfigurer"class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>/WEB-INF/mail.properties</value>
<value>classpath: conf/sqlmap/jdbc.properties</value>//注意这两种value值的写法
</list>
</property>
</bean>

譬如,jdbc.properties的内容为:

jdbc.driverClassName=com.mysql.jdbc.Driver
jdbc.url=jdbc:mysql://localhost/mysqldb?useUnicode=true&amp;characterEncoding=UTF-8&amp;zeroDateTimeBehavior=round;
jdbc.username=root
jdbc.password=123456

那么在spring配置文件中,我们就可以这样写:

<bean id="propertyConfigurer"class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer">
<property name="locations">
<list>
<value>classpath: conf/sqlmap/jdbc.properties </value>
</list>
</property>
</bean>
<bean id="dataSource"class="org.apache.commons.dbcp.BasicDataSource"destroy-method="close">
<property name="driverClassName"value="${jdbc.driverClassName}" />
<property name="url" value="${jdbc.url}" />
<property name="username" value="${jdbc.username}"/>
<property name="password"value="${jdbc.password}" />
</bean>

这样,一个简单的数据源就设置完毕了。可以看出:PropertyPlaceholderConfigurer起的作用就是将占位符指向的数据库配置信息放在bean中定义的工具。

查看源代码,可以发现,locations属性定义在PropertyPlaceholderConfigurer的祖父类 PropertiesLoaderSupport中,而location只有 setter方法。类似于这样的配置,在spring的源程序中很常见的。

PropertyPlaceholderConfigurer内置的功能非常丰富,如果它未找到${xxx}中定义的xxx键,它还会去JVM系统属性(System.getProperty())和环境变量(System.getenv())中寻找。通过启用systemPropertiesMode和searchSystemEnvironment属性,开发者能够控制这一行为。

我们可以通过System.setProperty(key, value)或者java中通过-Dnamevalue来给Spring配置文件传递参数。

为简化PropertyPlaceholderConfigurer的使用,Spring提供了<context:property-placeholder/>元素。下面给出了配置示例,启用它后,开发者便不用配置PropertyPlaceholderConfigurer对象了。

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

注意:Spring容器采用反射扫描的发现机制,在探测到Spring容器中有一个org.springframework.beans.factory.config.PropertyPlaceholderConfigurer的Bean就会停止对剩余PropertyPlaceholderConfigurer的扫描。

     即spring容器中最多只能定义一个context:property-placeholder!(spring和springmvc不是同一容器,PropertyPlaceholderConfigurer可以同时存在于spring和springmvc中)。

@Value使用注意事项

项目中经常会用到配置文件,定义成properties的形式比较常见,为了方便使用一般在spring配置文件中做如下配置:

<context:property-placeholder ignore-unresolvable="true" location="classpath:application.properties" />

这样在程序代码中直接用@Value("${name}")就能直接取到properties文件中定义的变量值。

但是发现一个情况,在Controller中取不到这个值,直接输出了${name}字符串,并没有解析出值,而在service中却能取到。有点奇怪啊,明显在Controller中貌似并没有引入properties文件中的变量,而被当做普通的字符串处理了。突然想到这个项目有2个配置文件,1个在WEB-INF下的springmvc-servlet.xml,1个在classpath下的applicationContext.xml,其中applicationContext.xml中定义有placeholder。

说实话之前并没有注意过这个配置文件的区别,一直以为只是放置的位置不一样而已,借助这次机会吧,查询到了一些资料。先看一则在web.xml中引入spring的配置:

<!-- springmvc配置开始 -->
<servlet>
<servlet-name>springmvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<!-- 可以自定义servlet.xml配置文件的位置和名称,默认为WEB-INF目录下,名称为[<servlet-name>]-servlet.xml,如spring-servlet.xml
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring-servlet.xml</param-value>
</init-param>
-->
<load-on-startup>1</load-on-startup>
</servlet> <servlet-mapping>
<servlet-name>springmvc</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
<!-- springmvc配置结束 -->
<!-- Spring配置开始 -->
<listener>
<listenerclass>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- 指定Spring Bean的配置文件所在目录。默认配置在WEB-INF目录下 -->
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:config/applicationContext.xml</param-value>
</context-param>
<!-- Spring配置结束 -->

可以看到分为spring配置和springmvc配置2种。其中spring配置以监听器的形式引入,不指定xml配置文件地址则默认查找WEB-INF下的applicationContext.xml文件。springmvc则以 servlet形式引入,当没有指定引入的xml配置文件地址时,则会自动引入WEB-INF下的[servlet-name]-servlet.xml文件,本例中为springmvc-servlet.xml。引入顺序为先引入spring配置,再引入servlet形式的springmvc配置。

值得注意的几点是:springmvc的配置文件中可以直接用id引入spring配置文件中定义的bean,但是反过来不可以。每一个springmvc的配置文件xxx-servlet.xml对应一个web.xml中的servlet定义。当存在多个springmvc配置文件时候,他们之间是不能互相访问的。

在百度中别人的帖子中看到一段应该是官方的原文解释,我摘抄过来并粗糙的直译一下:

Spring lets you define multiple contexts in a parent-child hierarchy.
spring允许你定义多个上下文在父子继承关系中
The applicationContext.xml defines the beans for the "root webapp context", i.e. the context associated with the webapp.
applicationContext.xml文件是为了"根webapp应用上下文"定义bean,也就是说它的上下文是和webapp相关联的
The spring-servlet.xml (or whatever else you call it) defines the beans for one servlet's app context. There can be many of these in a webapp,
spring-servlet.xml文件(或是其他的你习惯的称呼)是为了一个servlet应用上下文定义bean.在一个webapp中可以有多个此配置文件,
one per Spring servlet (e.g. spring1-servlet.xml for servlet spring1, spring2-servlet.xml for servlet spring2).
每一个配置对应一个spring的servlelt(例如: 名为spring1的servlet拥有配置文件spring1-servlet.xml, 名为spring2的servlet拥有配置文件spring2-servlet.xml).
Beans in spring-servlet.xml can reference beans in applicationContext.xml, but not vice versa.
在spring-servlet.xml中定义的bean可以直接引用在applicationContext.xml中定义的bean, 但是反过来不可以.
All Spring MVC controllers must go in the spring-servlet.xml context.
所有springmvc的Controller必须在spring-servlet.xml对应的上下文中运行.
In most simple cases, the applicationContext.xml context is unnecessary. It is generally used to contain beans that are shared between all servlets
在大多数简单的情况下, applicationContext.xml对应的上下文并不必须.它通常用来包含那些bean用来在webapp中所有servlet之间共享.
in a webapp. If you only have one servlet, then there's not really much point, unless you have a specific use for it.
如果你只有一个servlet, 那么实际没有什么必要定义applicationContext.xml, 除非你有特别应用.

@Value在哪个容器中使用,就调用哪个容器中的配置,因为Controller是定义在子容器中的,故在Controller中使用@Value也是在子容器中操作,而项目中placeholder是定义在父容器中的,在子容器中实际上没有它的定义,也就是并没有引入properties文件中的值。虽然子容器能访问父容器,但是如果不引入的话,子容器中实际上是没有父容器中的properties值。所以只需要在子容器中的配置中加入<context:property-placeholder ignore-unresolvable="true" location="classpath*:/application.properties" />就一切正常了。

那么回到最开始的@Value取不到值的问题,现在的可以清楚由于Controller是定义在springmvc的servlet配置文件中的,所以在Controller中使用@Value注解会从springmvc的配置中查找,故只需要将placeholder重新在springmvc的配置中配置一遍,Controller中的@Value注解便能取到值了。

注意: 

1.即使给变量赋了初值也会以配置文件的值为准 
2.用@Value注解只能在spring管理的bean中使用,在非spring管理的工具类中,只能通过静态方法读取配置文件

参考资料

http://www.cnblogs.com/chyu/p/4779265.html

http://www.cnblogs.com/huqianliang/p/5673701.html

https://stackoverflow.com/questions/11890544/spring-value-annotation-in-controller-class-not-evaluating-to-value-inside-pro

PropertyPlaceholderConfigurer使用及@Value使用注意事项的更多相关文章

  1. PropertyPlaceholderConfigurer的注意事项

    1.基本的使用方法是<bean id="propertyConfigurerForWZ" class="org.springframework.beans.fact ...

  2. Spring 后置处理器 PropertyPlaceholderConfigurer 类(引用外部文件)

    一.PropertyPlaceholderConfigurer类的作用 PropertyPlaceholderConfigurer 是 BeanFactory 后置处理器的实现,也是 BeanFact ...

  3. activemq消息队列的使用及应用docker部署常见问题及注意事项

    activemq消息队列的使用及应用docker部署常见问题及注意事项 docker用https://hub.docker.com/r/rmohr/activemq/配置在/data/docker/a ...

  4. Spring PropertyPlaceholderConfigurer 自定义扩展

    原文地址:https://blog.csdn.net/feiyu8607/article/details/8282893 Spring中PropertyPlaceholderConfigurer这个类 ...

  5. jQuery UI resizable使用注意事项、实时等比例拉伸及你不知道的技巧

    这篇文章总结的是我在使用resizable插件的过程中,遇到的问题及变通应用的奇思妙想. 一.resizable使用注意事项 以下是我在jsfiddle上写的测试demo:http://jsfiddl ...

  6. Windows Server 2012 NIC Teaming介绍及注意事项

    Windows Server 2012 NIC Teaming介绍及注意事项 转载自:http://www.it165.net/os/html/201303/4799.html Windows Ser ...

  7. TODO:Golang指针使用注意事项

    TODO:Golang指针使用注意事项 先来看简单的例子1: 输出: 1 1 例子2: 输出: 1 3 例子1是使用值传递,Add方法不会做任何改变:例子2是使用指针传递,会改变地址,从而改变地址. ...

  8. app开发外包注意事项,2017最新资讯

    我们见过很多创业者,栽在这app外包上.很多创业者对于app外包这件事情不是特别重视,以为将事情交给app外包公司就完事了,实际上不是的.无论是从选择app外包公司还是签订合同.售后维护等各方面都有许 ...

  9. favicon.ioc使用以及注意事项

    1.效果 2.使用引入方法 2.1 注意事项:(把图标命名为favicon.ico,并且放在根目录下,同时使用Link标签,多重保险) 浏览器默认使用根目录下的favicon.ico 图标(如果你并没 ...

随机推荐

  1. matlab读写图片,读取图像序列,读取AVI视频

    介绍使用matlab读写图片,读取图像序列,读取AVI视频的方法: 一.读写图像 使用matlab读一幅图像,并另存 % Filename: ImageReadWrite clc; clear; i ...

  2. Redis: Redis支持五种数据类型

    ylbtech-Redis: Redis支持五种数据类型 Redis支持五种数据类型:string(字符串) ,hash(哈希),list(列表),set(集合)及zset(sorted set:有序 ...

  3. java,js 解unicode

    import java.util.regex.Matcher; import java.util.regex.Pattern; public class UnicodeDecodeDataConver ...

  4. Linux系统性能检测

    转自:http://www.cnblogs.com/itech/archive/2011/06/08/2075145.html 一 .uptime uptime命令用于查看服务器运行了多长时间以及有多 ...

  5. C++ 单链表操作总结

    第一.单链表的定义和操作 #include <iostream> using namespace std; template <typename T> struct Node ...

  6. 快速安装laravel和依赖

    http://pkg.phpcomposer.com CMD敲命令: composer config -g repositories.packagist composer http://packagi ...

  7. javascript json数据的处理

    1.前端页面default.html <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" " ...

  8. Python小知识点(3)--装饰器

    (1)装饰器含参数,被装饰函数不含(含)参数 实例代码如下: import time # 装饰器函数 def wrapper(func): def done(*args,**kwargs): star ...

  9. linux 进程通信 :流套接字

    消息队列是可以实现没有共同关系的进程之间的通信.Socket则可以实现不同计算机的不同进程之间的通信. //地址的结构体 struct sockaddr_in{ short int sin_famil ...

  10. swagger 接口文档,控制器 和 object类型的参数与返回值 的 注释不显示问题

    一.控制器的注释不显示:是因为配置swagger的时候没有将includeControllerXmlComments参数配置为true,因为其默认值为false 二.object 类型的参数和返回值 ...