Spring-使用外部属性文件01

简介: Spring-使用外部属性文件01

导读


Spring-使用外部属性文件01

Spring-使用加密的属性文件02

Spring-属性文件自身的引用03


概述


在进行数据源或者邮件服务器等资源配置时,用户可以直接在Spring配置文件中配置用户名、密码、连接信息等,但是有一种更好的方法是将这些配置信息独立到一个外部属性文件中,并在Spring配置文件中通过形如${user}、${password}的占位符引用属性文件中的属性项。


通过这种方式配置拥有两个明显的好处


减少维护的工作量

部署更加简单

Spring提供了一个PropertyPlaceholderConfigurer,它能够使Bean在配置时引用外部属性文件。


PropertyPlaceholderConfigurer实现了BeanFactoryPostProcessorBean接口,因而也是一个Bean工厂后处理器.


PropertyPlaceholderConfigurer属性文件


代码已托管到Github—> https://github.com/yangshangwei/SpringMaster


实例


maven项目


20170807045841538.jpg


一种不太好的写法

<bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource" destroy-method="close">
        <property name="driverClassName" value="oracle.jdbc.driver.OracleDriver"/>
        <property name="url" value="jdbc:oracle:thin:@172.25.246.11:1521:xgj"/>
        <property name="username" value="cctb"/>
        <property name="password" value="xgj2017"/>
    </bean>



从上面的配置文件中,我们可以看到驱动器类名、JDBC的URL以及数据库的用户名和密码都写在了XML中。部署的时候,如果需要改动数据库的配置信息,需要先找到这个xml,然后修改,不是特别方便。


根据实际应用的最佳实践,我们可以将这些信息抽取到一个配置文件中,取名为 jdbc.priperties (随意取名)

我们先看下jdbc.priperties的配置文件

jdbc.properties

jdbc.driverClassName=oracle.jdbc.driver.OracleDriver
jdbc.url=jdbc:oracle:thin:@172.25.246.11:1521:xgj
jdbc.username=cctb
jdbc.password=xgj2017


属性文件可以定义多个属性,每个属性都有一个属性名和属性值组成,二者用“=”隔开。

使用PropertyPlaceholderConfigurer属性文件


下面通过PropertyPlaceholderConfigurer引入jdbc.properties属性文件,调整数据源Bean的配置,为了测试,我们引入了JdbcTemplate

beans.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
    xmlns:p="http://www.springframework.org/schema/p"
    xmlns:context="http://www.springframework.org/schema/context"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans.xsd
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context.xsd">
    <!-- 扫描类包,将标注Spring注解的类自动转化Bean,同时完成Bean的注入 -->
    <context:component-scan base- package="com.xgj.ioc.propertyplacehoder"/>
    <!-- 引入JDBC属性文件 -->
    <bean  class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
        p:location="classpath:spring/jdbc.properties"
        p:fileEncoding="utf-8"/>
    <!-- 通过属性名引用属性值 -->     
    <bean id="dataSource" class="org.apache.commons.dbcp.BasicDataSource"
        destroy-method="close"
        p:driverClassName="${jdbc.driverClassName}"
        p:url="${jdbc.url}"
        p:username="${jdbc.username}"
        p:password="${jdbc.password}"/>
    <!-- 配置Jdbc模板  -->
    <bean id="jdbcTemplate" class="org.springframework.jdbc.core.JdbcTemplate"
        p:dataSource-ref="dataSource" />
</beans>

通过PropertyPlaceholderConfigurer的location属性引入属性文件,这样在Bean定义的时候就可以引用属性文件中的属性了。


然后通过${jdbc.driverClassName}等占位符来引用jdbc.properties中的属性,这样部署人员仅需要关注jdbc.properties这个配置文件即可,无需关心Spring的配置文件。


测试:


我们数据库中temp_user表有一条记录,我们来模拟登录查询



20170807051317524.jpg

PropertyPlaceHoderTest.java

package com.xgj.ioc.propertyplacehoder;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Component;
@Component
public class PropertyPlaceHoderTest {
    private final static String MATCH_COUNT_SQL = " SELECT count(*) FROM temp_user  "
            + " WHERE user_name =? and password=? ";
    private JdbcTemplate jdbcTemplate;
    @Autowired
    public void setJdbcTemplate(JdbcTemplate jdbcTemplate) {
        this.jdbcTemplate = jdbcTemplate;
    }
    /**
     * 
     * 
     * @Title: getMatchCount
     * 
     * @Description: 根据用户名和密码判断用户是否存在
     * 
     * @param username
     * @param password
     * 
     * @return: int
     */
    public int getMatchCount(String username, String password) {
        return jdbcTemplate.queryForObject(MATCH_COUNT_SQL, new Object[] {
                username, password }, Integer.class);
    }
    /**
     * 
     * 
     * @Title: main
     * 
     * @Description: 测试
     * 
     * @param args
     * 
     * @return: void
     */
    public static void main(String[] args) {
        // 加载Spring配置文件
        ApplicationContext ctx = new ClassPathXmlApplicationContext(
                "classpath:com/xgj/ioc/propertyplacehoder/beans.xml");
        // 获取通过注解标注的Bean
        PropertyPlaceHoderTest propertyPlaceHoderTest = ctx.getBean(
                "propertyPlaceHoderTest", PropertyPlaceHoderTest.class);
        // 调用方法
        int count = propertyPlaceHoderTest.getMatchCount("xgj", "123456");
        System.out.println("匹配的用户数量:" + count);
    }
}


运行结果:


20170807051406071.jpg


PropertyPlacerholderConfigurer的其他属性

locations

如果只有一个属性文件,则直接使用location属性指定即可,如果有多个属性文件,则可以通过locations属性进行设置,可以像配置list一样配置locations属性。

list的配置参考 Spring-注入参数详解-[集合类型属性]


fileEncoding

属性文件的编码格式,Spring默认使用操作系统默认编码读取属性文件,如果属性文件使用了特殊编码,则需要通过该属性显式指定。

order

如果配置配置文件中定义了多个PropertyPlacehoderConfigurer,则通过该属性指定优先顺序。


placeholderPrefix

上面的案例,我们使用${jdbc.driverClassName}引用属性文件中的属性项, 其中, ${ 为默认的占位符前缀,可修改


placeholderSuffix

占位符后缀,默认为 }


使用context:property-placehoder引用属性文件

可以使用context命名空间定义属性文件,相比传统的PropertyPlaceholderConfigurer配置,这种方式更优雅

<!-- 引入JDBC属性文件
    <bean  class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer"
        p:location="classpath:spring/jdbc.properties"
        p:fileEncoding="utf-8"/>  -->
    <!-- 使用context命名空间,同上面的Bean等效。 -->
     <context:property-placeholder location="classpath:spring/jdbc.properties" />

但是有个缺点: 如果想自定义一些额外的高级功能,比如属性加密、使用数据库表保存配置信息等,则必须扩展PropertyPlaceholderConfigurer的类并使用Bean的配置方式。


基于注解及基于JAVA类的配置中引用属性


在基于XML的配置文件中,通过${propName}的形式引用属性值,类似的,基于注解的Bean可以通过@Value注解为Bean的成员变量或者方法入参自动注入容器已有的属性。同样的基于JAVA类注解@Configuration的类的引用属性的方式和基于注解配置的引用方式是完全一样的,不再赘述。


实例


20170807061946127.jpg

package com.xgj.ioc.propertyplacehoder.annotation;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
@Component
public class MyDataSource {
    private String driveClassName;
    private String url;
    private String userName;
    private String password;
    /**
     * 
     * 
     * @Title: setDriveClassName
     * 
     * @Description: 注入jdbc.driverClassName的值 (也可以直接在属性上注入)
     * 
     * @param driveClassName
     * 
     * @return: void
     */
    @Value("${jdbc.driverClassName}")
    public void setDriveClassName(String driveClassName) {
        this.driveClassName = driveClassName;
    }
    @Value("${jdbc.url}")
    public void setUrl(String url) {
        this.url = url;
    }
    @Value("${jdbc.username}")
    public void setUserName(String userName) {
        this.userName = userName;
    }
    @Value("${jdbc.password}")
    public void setPassword(String password) {
        this.password = password;
    }
    /**
     * 
     * 
     * @Title: getDriveClassName
     * 
     * @Description: 获取driveClassName
     * 
     * @return
     * 
     * @return: String
     */
    public String getDriveClassName() {
        System.out.println("getDriveClassName:" + driveClassName);
        return driveClassName;
    }
    public String getUrl() {
        System.out.println("getUrl:" + url);
        return url;
    }
    public String getUserName() {
        System.out.println("getUserName:" + userName);
        return userName;
    }
    public String getPassword() {
        System.out.println("getPassword:" + password);
        return password;
    }
}

测试类

package com.xgj.ioc.propertyplacehoder.annotation;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
public class AnnotationTest {
    public static void main(String[] args) {
        ApplicationContext ctx = new ClassPathXmlApplicationContext(
                "classpath:com/xgj/ioc/propertyplacehoder/beans.xml");
        MyDataSource myDataSource = ctx.getBean("myDataSource",
                MyDataSource.class);
        myDataSource.getDriveClassName();
        myDataSource.getUrl();
        myDataSource.getUserName();
        myDataSource.getPassword();
    }
}


运行结果

20170807062109614.jpg


注意事项


使用的过程中,一定要确保所引用的属性值在属性文件中存在且数值匹配,否则会造成Bean创建错误。

比如我们修改一下注入的属性@Value(“${jdbc.driverClassName1}”)


Error creating bean with name ‘myDataSource’: Injection of autowired dependencies failed; …..

2017-08-06 18:23:17,692  INFO [main] (AbstractApplicationContext.java:583) - Refreshing org.springframework.context.support.ClassPathXmlApplicationContext@7fae1081: startup date [Sun Aug 06 18:23:17 BOT 2017]; root of context hierarchy
2017-08-06 18:23:17,767  INFO [main] (XmlBeanDefinitionReader.java:317) - Loading XML bean definitions from class path resource [com/xgj/ioc/propertyplacehoder/beans.xml]
2017-08-06 18:23:18,398  WARN [main] (AbstractApplicationContext.java:551) - Exception encountered during context initialization - cancelling refresh attempt: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'myDataSource': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'jdbc.driverClassName1' in value "${jdbc.driverClassName1}"
Exception in thread "main" org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'myDataSource': Injection of autowired dependencies failed; nested exception is java.lang.IllegalArgumentException: Could not resolve placeholder 'jdbc.driverClassName1' in value "${jdbc.driverClassName1}"
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:372)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1264)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:553)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:483)
    at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:761)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:867)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:543)
    at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:139)
    at org.springframework.context.support.ClassPathXmlApplicationContext.<init>(ClassPathXmlApplicationContext.java:83)
    at com.xgj.ioc.propertyplacehoder.annotation.AnnotationTest.main(AnnotationTest.java:9)
Caused by: java.lang.IllegalArgumentException: Could not resolve placeholder 'jdbc.driverClassName1' in value "${jdbc.driverClassName1}"
    at org.springframework.util.PropertyPlaceholderHelper.parseStringValue(PropertyPlaceholderHelper.java:174)
    at org.springframework.util.PropertyPlaceholderHelper.replacePlaceholders(PropertyPlaceholderHelper.java:126)
    at org.springframework.core.env.AbstractPropertyResolver.doResolvePlaceholders(AbstractPropertyResolver.java:236)
    at org.springframework.core.env.AbstractPropertyResolver.resolveRequiredPlaceholders(AbstractPropertyResolver.java:210)
    at org.springframework.context.support.PropertySourcesPlaceholderConfigurer$2.resolveStringValue(PropertySourcesPlaceholderConfigurer.java:172)
    at org.springframework.beans.factory.support.AbstractBeanFactory.resolveEmbeddedValue(AbstractBeanFactory.java:831)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1086)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1066)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredMethodElement.inject(AutowiredAnnotationBeanPostProcessor.java:659)
    at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:366)
    ... 13 more


相关文章
|
5天前
|
存储 Java 数据安全/隐私保护
|
5天前
|
设计模式 前端开发 Java
了解 Spring MVC 架构、Dispatcher Servlet 和 JSP 文件的关键作用
Spring MVC 是 Spring 框架的一部分,是一个 Web 应用程序框架。它旨在使用 Model-View-Controller(MVC) 设计模式轻松构建Web应用程序。
65 0
|
5天前
|
JavaScript Java API
【JavaEE】Spring Boot - 日志文件
【JavaEE】Spring Boot - 日志文件
6 0
|
5天前
|
Java Spring
Spring文件配置以及获取
Spring文件配置以及获取
13 0
|
5天前
|
XML Java 数据格式
Spring 属性注入方式
Spring 属性注入方式
14 2
|
5天前
|
Java 数据库连接 数据库
Spring事务简介,事务角色,事务属性
Spring事务简介,事务角色,事务属性
20 2
|
5天前
|
Java Apache Spring
Spring BeanUtils与Apache BeanUtils提供基本属性复制,适用于简单需求
【5月更文挑战第4天】Spring BeanUtils与Apache BeanUtils提供基本属性复制,适用于简单需求;Cglib BeanCopier用于转换为Cglib代理对象;Apache PropertyUtils处理属性操作;Dozer支持复杂对象映射。选择工具取决于具体需求,如需精细控制或对象映射,推荐Dozer或Apache PropertyUtils。Apache BeanUtils可能因潜在的封装性破坏被禁用。
24 3
|
5天前
|
Java 开发者 Spring
Spring Boot中的资源文件属性配置
【4月更文挑战第28天】在Spring Boot应用程序中,配置文件是管理应用程序行为的重要组成部分。资源文件属性配置允许开发者在不重新编译代码的情况下,对应用程序进行灵活地配置和调整。本篇博客将介绍Spring Boot中资源文件属性配置的基本概念,并通过实际示例展示如何利用这一功能。
27 1
|
5天前
|
JSON Java 数据库连接
属性注入掌握:Spring Boot配置属性的高级技巧与最佳实践
属性注入掌握:Spring Boot配置属性的高级技巧与最佳实践
28 1
|
5天前
|
XML Java 关系型数据库
注解驱动事务:Spring中基于注解的事务属性配置详解
注解驱动事务:Spring中基于注解的事务属性配置详解
43 0
注解驱动事务:Spring中基于注解的事务属性配置详解