一、PropertyPlaceholderConfigurer
spring提供的PropertyPlaceholderConfigurer实现类能够使Bean在配置时引用外部属性文件。
PropertyPlaceholderConfigurer实现了BeanFactoryPostProcessorBean接口,因而也是一个Bean工厂后处理器。
二、PropertyPlaceholderConfigurer的使用
1、xml配置及注解方式:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.3.16.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.16.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>4.3.16.RELEASE</version> </dependency>
name=zhangsan
age=23
package test; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; public class Driver { public static void main(String[] args) { ApplicationContext context = new ClassPathXmlApplicationContext("my.xml"); User user = context.getBean("user", User.class); System.out.println(user.getName()); System.out.println(user.getAge()); } } class User { @Value("${name}") private String name; private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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 class="org.springframework.beans.factory.config.PropertyPlaceholderConfigurer" p:location="classpath:my.properties" p:fileEncoding="utf-8"/> <!-- 开启包扫描,因为User类使用到了@Value注解;包扫描不支持裸体类上的注解 --> <context:component-scan base-package="test"/> <!-- 使用外部属性文件值赋值 --> <bean id="user" class="test.User" p:age="${age}"/> </beans>
PropertyPlaceholderConfigurer的其他属性:
locations:可以引入多个属性文件
fileEncoding:属性文件的编码格式
order:指定多个属性的优先级
placeholderPrefix:默认值为“${”,可以修改
placeholderSuffix:默认为“}”
除了使用<bean>声明PropertyPlaceholderConfigurer引入属性文件这种方式外,还可以使用另一种简洁的方式;但如果希望自定义一些额外的高级功能,如属性加密、使用数据库表来保存配置信息时,就必须使用<bean>声明PropertyPlaceholderConfigurer的方式
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:p="http://www.springframework.org/schema/p" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 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 引入属性文件 --> <context:property-placeholder location="my.properties"/> <!-- 开启包扫描,因为User类使用到了@Value注解;包扫描不支持裸体类上的注解 --> <context:component-scan base-package="test"/> <!-- 使用外部属性文件值赋值 --> <bean id="user" class="test.User" p:age="${age}"/> </beans>
2、Java配置及注解方式:
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.3.16.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.16.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>4.3.16.RELEASE</version> </dependency>
name=zhangsan
age=23
package test; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class Driver { public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class); User user = context.getBean("user", User.class); System.out.println(user.getName()); System.out.println(user.getAge()); } } class User { @Value("${name}") private String name; @Value("${age}") private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
package test; import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; @Configuration @ComponentScan public class MyConfig { @Bean public User user() { return new User(); } @Bean public PropertyPlaceholderConfigurer PropertyPlaceholderConfigurer() { PropertyPlaceholderConfigurer configurer = new PropertyPlaceholderConfigurer(); configurer.setLocation(new ClassPathResource("my.properties")); return configurer; } }
三、自定义PropertyPlaceholderConfigurer——支持加密
加密
如果属性信息是数据库用户名、密码等敏感信息,应该使用密文方式。虽说Web应用的客户端用户看不到服务器端的属性文件,但内部人员却可以轻易查看属性文件的内容,这样容易造成数据库访问权限的泄露。
实现
PropertyPlaceholderConfigurer类继承了PlaceholderConfigurerSupport,PlaceholderConfigurerSupport有几个protected的空实现的属性转换方法,专门用于在使用属性之前对属性值进行转换;
所以我们需要自定义PlaceholderConfigurerSupport的实现类并覆盖相应的属性转换方法来代替spring提供的PropertyPlaceholderConfigurer。
信息的加密可分为对称和非对称两种方式,前者加密后可以解密还原,而后者不可以还原;MD5是非对称加密,DES是对称加密,这里使用DES方式。DES加密解密的关键是加密密钥。
demo
<dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.3.16.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.16.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>4.3.16.RELEASE</version> </dependency> <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.6</version> </dependency>
name=agyo9QHJ78MfJAfVsP+M2w==
age=ArphqsQ7cdE=
package test; import java.security.Key; import java.security.SecureRandom; import javax.crypto.Cipher; import javax.crypto.KeyGenerator; import sun.misc.BASE64Decoder; import sun.misc.BASE64Encoder; public class DESUtils { // 指定DES加密解密所用的密钥 private static Key key; private static String KEY_STR = "myKey"; static { try { KeyGenerator generator = KeyGenerator.getInstance("DES"); generator.init(new SecureRandom(KEY_STR.getBytes())); key = generator.generateKey(); generator = null; } catch (Exception e) { throw new RuntimeException(e); } } /** * 对字符串进行DES加密,返回Base64编码的加密字符串 */ public static String getEncryptString(String str) { BASE64Encoder base64en = new BASE64Encoder(); try { byte[] strBytes = str.getBytes("UTF8"); Cipher cipher = Cipher.getInstance("DES"); cipher.init(Cipher.ENCRYPT_MODE, key); byte[] encryptStrBytes = cipher.doFinal(strBytes); return base64en.encode(encryptStrBytes); } catch (Exception e) { throw new RuntimeException(e); } } /** * 对Base64编码的加密字符串进行DES解密,返回解密后的字符串 */ public static String getDecryptString(String str) { BASE64Decoder base64De = new BASE64Decoder(); try { byte[] strBytes = base64De.decodeBuffer(str); Cipher cipher = Cipher.getInstance("DES"); cipher.init(Cipher.DECRYPT_MODE, key); byte[] decryptStrBytes = cipher.doFinal(strBytes); return new String(decryptStrBytes, "UTF8"); } catch (Exception e) { throw new RuntimeException(e); } } /** * 对入参的字符串加密,并打印密文 */ public static void main(String[] args) throws Exception { args = new String[]{"zhangsan", "23"}; if (args == null || args.length < 1) { System.out.println("请输入要加密的字符,用空格分隔."); } else { for (String arg : args) { System.out.println(arg + ":" + getEncryptString(arg)); } } //System.out.println(getDecryptString("WnplV/ietfQ=")); //System.out.println(getDecryptString("gJQ9O+q34qk=")); } }
package test; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.ApplicationContext; import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class Driver { public static void main(String[] args) { ApplicationContext context = new AnnotationConfigApplicationContext(MyConfig.class); User user = context.getBean("user", User.class); System.out.println(user.getName()); System.out.println(user.getAge()); } } class User { @Value("${name}") private String name; @Value("${age}") private int age; public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }
package test; import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; public class MyPropertyPlaceholderConfigurer extends PropertyPlaceholderConfigurer { // 需要解密的属性 private String[] encryptPropNames ={"name","age"}; // 解密 @Override protected String convertProperty(String propertyName, String propertyValue) { if(isEncryptProp(propertyName)){ String decryptValue = DESUtils.getDecryptString(propertyValue); System.out.println(decryptValue); return decryptValue; }else{ return propertyValue; } } /** * 判断是否是加密的属性 */ private boolean isEncryptProp(String propertyName){ for(String encryptPropName:encryptPropNames){ if(encryptPropName.equals(propertyName)){ return true; } } return false; } }
package test; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; @Configuration @ComponentScan // 因为用到了@Value注解,所以需要开启扫描 public class MyConfig { @Bean public User user() { return new User(); } // @Bean //// public PropertyPlaceholderConfigurer PropertyPlaceholderConfigurer() { //// PropertyPlaceholderConfigurer configurer = new PropertyPlaceholderConfigurer(); //// configurer.setLocation(new ClassPathResource("my.properties")); //// return configurer; //// } @Bean public MyPropertyPlaceholderConfigurer encryptPropertyPlaceholderConfigurer() { MyPropertyPlaceholderConfigurer configurer = new MyPropertyPlaceholderConfigurer(); configurer.setLocation(new ClassPathResource("my.properties")); return configurer; } }
四、属性文件引用属性文件的属性值
Spring允许在xml、java类中使用${xxx}、@Value("${xxx}")引用属性值,也允许在属性文件中使用${xxx}引用其它属性文件的属性值
name=lisi
age=${age}
age=18
package test; import org.springframework.beans.factory.config.PropertyPlaceholderConfigurer; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.core.io.ClassPathResource; @Configuration public class MyConfig { @Bean public User user() { return new User(); } @Bean public PropertyPlaceholderConfigurer PropertyPlaceholderConfigurer() { PropertyPlaceholderConfigurer configurer = new PropertyPlaceholderConfigurer(); configurer.setLocations(new ClassPathResource[] {new ClassPathResource("my.properties"), new ClassPathResource("my2.properties")}); return configurer; } }