spring系列之自定义扩展PropertyPlaceHolderConfigurer

12 篇文章 1 订阅

spring系列之自定义扩展PropertyPlaceHolderConfigurer

目录

spring系列之自定义扩展PropertyPlaceHolderConfigurer


 

 

 

本文章向大家介绍spring系列之自定义扩展PropertyPlaceHolderConfigurer,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

 

 

 

一、PropertyPlaceHolderConfigurer介绍

主要用于将一些配置信息移出xml文件,移到至properties文件

二、拓展使用

1、将porperties内容设置成java全局可读

思路:spring启动的时候会将properties加载至其指定缓存机制,可利用其加载机制,在读取properties时,保存一份至变量,对外提供访问接口。

1.1 创建PropertyPlaceHolderConfigurer的子类,代码如下CustomPropertyPlaceConfigurer

        private Map<String, String> ctxPropertiesMap;
        @Override
        protected void processProperties(
                ConfigurableListableBeanFactory beanFactoryToProcess,
                Properties props) throws BeansException {
            super.processProperties(beanFactoryToProcess, props);
            ctxPropertiesMap = new HashMap<String, String>();
            for(Object key: props.keySet()) {
                String keyStr = key.toString();
                String valueStr = props.getProperty(keyStr);
                ctxPropertiesMap.put(keyStr, valueStr);
            }
        }
        public String getContextProperty(String key) {
            return ctxPropertiesMap.get(key);
        }

重写processProperties方法,将properties内容存放至ctxPropertiesMap中。

1.2 spring相关配置

        <bean id="customPropertyPlaceConfigurer" class="com.baggio.common.spring.ext.CustomPropertyPlaceConfigurer">
           <property name="locations">
         <list>
        <value>classpath:test.properties</value>
         </list>
       </property>
    </bean>
      其中test.properties内容
      username=root
      password=123456789

2、 将properties文件的路径设置成运行时拼接

起因:公司要将tomcat jdk mysql等都做到一个安装包,实现一键安装,但又要求相关配置文件(如数据库连接的properties文件)不能放在项目中,

以免项目升级时容易将开发环境配置覆盖生产环境配置。而配置文件必须在安装包选择的安装目录中。描述这么多,就为了说明,配置文件是动态可变的,项目启动时才能知道。

 

2.1 修改spring相关配置,将默认locations属性修改为自定义属性,将资源文件路径改成用变量替代

        <bean id="customPropertyPlaceConfigurer" class="com.baggio.common.spring.ext.CustomPropertyPlaceConfigurer">
        <property name="customPropertyFiles">
            <list>
                <value>[filepath]test.properties</value>
            </list>
        </property>
        </bean>

其中,customPropertyFiles为新增属性,通过setter注入,[filepath]是动态值,在加载时替换

2.2 在类CustomPropertyPlaceConfigurer中,添加customPropertyFiles的setter注入方法。

//根据环境变量读取资源文件(实现动态读取资源文件)

    public void setCustomPropertyFiles(List<String> customPropertyFiles) {
        String fileSeparator = System.getProperty("file.separator");
        String javaHome = System.getenv("JAVA_HOME");
        Properties properties = new Properties();
        for(String customPropertyFile: customPropertyFiles) {
            customPropertyFile = customPropertyFile.replace("[filepath]", javaHome + fileSeparator);
            Properties pro = PropertyUtils.getProperties(customPropertyFile);
            properties.putAll(pro);
        }

//关键方法,调用父类中的方法,通过这个方法将自定义在家的properties文件加入spring

this.setProperties(properties);

}

读取环境变量JAVA_HOME,替换[filepath],即需要将test.properties文件存放至JAVA_HOME下(如果一键安装包,将选择的安装路径添加至环境变量,则就可以攻击环境变量找到

相关资源文件了)。

3、设置数据库密码成加密状态

起因:生产环境数据库的密码需要对外保密,知道的人越少越安全,故在配置中,将数据库密码加密可以防止被开发人员获得。

3.1 将加密的密码存放至test.properties中

username=root

password=4858037DC477F327BB60D4D2CD3259668EA7A2EEEF68AFF4BB2925039E8DD22CFF2D31F2578ED11B90B1324E6A8F9E112F9E4AD1B36534686D08C7177F775B03C5739B48130A37DA70AA4EFE929EBC1E768A81E31BFF2394580FAD95C9394142623F6E87589742E7822B27B9DAC78D9D9F61BC38B4A7E592DF2C5BBFE2EEBE2C

3.2 步骤1已经得到资源文件,在此解密,并存入资源文件

    @Override
    protected void processProperties(
            ConfigurableListableBeanFactory beanFactoryToProcess,
            Properties props) throws BeansException {
        String password = props.getProperty("password");
        password = decryptByPrivateKey(password);
        props.put("password", password);
        super.processProperties(beanFactoryToProcess, props);
        ctxPropertiesMap = new HashMap<String, String>();
        for(Object key: props.keySet()) {
            String keyStr = key.toString();
            String valueStr = props.getProperty(keyStr);
            ctxPropertiesMap.put(keyStr, valueStr);
        }
    }

4、实现配置文件不同场景下切换

针对步骤2中的起因,如果不将配置文件移出项目,可以用两个配置文件,或者一个配置文件中配置两套配置来解决。

此处用两个配置文件,这样,开发阶段,开发人员将不会再操作生产配置文件

4.1 在步骤2基础上,在JAVA_HOME目录下添加资源文件test_dev.properties

username=root_dev

password=4858037DC477F327BB60D4D2CD3259668EA7A2EEEF68AFF4BB2925039E8DD22CFF2D31F2578ED11B90B1324E6A8F9E112F9E4AD1B36534686D08C7177F775B03C5739B48130A37DA70AA4EFE929EBC1E768A81E31BFF2394580FAD95C9394142623F6E87589742E7822B27B9DAC78D9D9F61BC38B4A7E592DF2C5BBFE2EEBE2C

4.2 添加环境变量devMode

通过此变量实现生产环境和开发环境的切换,在生产环境不需配置此变量

4.3 修改实现类

private static final String propertiesName = "test";

//根据环境变量读取资源文件(实现动态读取资源文件)

    public void setCustomPropertyFiles(List<String> customPropertyFiles) {
        String fileSeparator = System.getProperty("file.separator");
        String javaHome = System.getenv("JAVA_HOME");
        String devMode = System.getenv("devMode");
        Properties properties = new Properties();
        for(String customPropertyFile: customPropertyFiles) {
            customPropertyFile = customPropertyFile.replace("[filepath]", javaHome + fileSeparator);
            if(customPropertyFile.indexOf(propertiesName) > -1) {
                String selectMode = devMode == null ? "":devMode;
                customPropertyFile = customPropertyFile.replace(propertiesName, propertiesName + selectMode);
            }
            Properties pro = PropertyUtils.getProperties(customPropertyFile);
            properties.putAll(pro);
        }

//关键方法,调用父类中的方法,通过这个方法将自定义在家的properties文件加入spring

this.setProperties(properties);

}

5、相关代码

    5.1 applicationContext-propertyHolderPlaceConfigurer.xml
<bean id="customPropertyPlaceConfigurer" class="com.baggio.common.spring.ext.CustomPropertyPlaceConfigurer">
 <property name="customPropertyFiles">
 <list>
 <value>[filepath]test.properties</value>
 </list>
 </property>
 </bean>
5.2 CustomPropertyPlaceConfigurer.java
public class CustomPropertyPlaceConfigurer extends
 PropertyPlaceholderConfigurer {
 private Map<String, String> ctxPropertiesMap;
 private static final String modulus = "92441782767737168436954696909693024093858863793961614402344444228605172372365323551758683343929072119833664753887689264694957015832845985460688129219184238713260973364694630820191644812136716548792835729282422798366717212011721279467766827116978941847031775579004384556944199142453248284103715410910396365107";
 private static final String privateKey = "32679115967513537671046377130533408337374645717358174504074279931768689954116908251020723800481388267273545402109800340028575676988805953753654005794437645857893594581228615872260459962755278225682193031026820777624756496861897491016205372128604377845082143258059385192300542462832051528405770845154897431553";
 //4858037DC477F327BB60D4D2CD3259668EA7A2EEEF68AFF4BB2925039E8DD22CFF2D31F2578ED11B90B1324E6A8F9E112F9E4AD1B36534686D08C7177F775B03C5739B48130A37DA70AA4EFE929EBC1E768A81E31BFF2394580FAD95C9394142623F6E87589742E7822B27B9DAC78D9D9F61BC38B4A7E592DF2C5BBFE2EEBE2C
 private static final String propertiesName = "test";

//根据环境变量读取资源文件(实现动态读取资源文件)

 public void setCustomPropertyFiles(List<String> customPropertyFiles) {
 String fileSeparator = System.getProperty("file.separator");
 String javaHome = System.getenv("JAVA_HOME");
 String devMode = System.getenv("devMode");
 Properties properties = new Properties();
 for(String customPropertyFile: customPropertyFiles) {
 customPropertyFile = customPropertyFile.replace("[filepath]", javaHome + fileSeparator);
 if(customPropertyFile.indexOf(propertiesName) > -1) {
 String selectMode = devMode == null ? "":devMode;
 customPropertyFile = customPropertyFile.replace(propertiesName, propertiesName + selectMode);
 }
 Properties pro = PropertyUtils.getProperties(customPropertyFile);
 properties.putAll(pro);
 }

//关键方法,调用父类中的方法,通过这个方法将自定义在家的properties文件加入spring

this.setProperties(properties);

}

@Override

 protected void processProperties(
 ConfigurableListableBeanFactory beanFactoryToProcess,
 Properties props) throws BeansException {
 String password = props.getProperty("password");
 password = decryptByPrivateKey(password);
 props.put("password", password);
 super.processProperties(beanFactoryToProcess, props);
 ctxPropertiesMap = new HashMap<String, String>();
 for(Object key: props.keySet()) {
 String keyStr = key.toString();
 String valueStr = props.getProperty(keyStr);
 ctxPropertiesMap.put(keyStr, valueStr);
 }
 }
 public Map<String, String> getCtxPropertiesMap() {
 return ctxPropertiesMap;
 }
 public String getContextProperty(String key) {
 return ctxPropertiesMap.get(key);
 }
 private String decryptByPrivateKey(String password){
 String mingwen = null;
 try {
 RSAPrivateKey priKey = RSAUtils
 .getPrivateKey(modulus, privateKey);
 // 解密后的明文
 mingwen = RSAUtils.decryptByPrivateKey(password, priKey);
 } catch(Exception e) {
 e.printStackTrace();
 }
 return mingwen;
 }
 public static void main(String[] args) {
 ApplicationContext context =  new ClassPathXmlApplicationContext("applicationContext-propertyHolderPlaceConfigurer.xml");
 CustomPropertyPlaceConfigurer cu = (CustomPropertyPlaceConfigurer) context.getBean("customPropertyPlaceConfigurer");
 Map<String, String> map = cu.getCtxPropertiesMap();
 Iterator<String> it = map.keySet().iterator();
 while(it.hasNext()) {
 String key = (String)it.next();
 String value = map.get(key);
 System.out.println(key + ":" + value);
 }
 }
}
  5.3 PropertyUtils.java

public class PropertyUtils {
 public static Properties getProperties(String filename) {
 FileInputStream fis = null;
 Properties p = new Properties();
 try {
 fis = new FileInputStream(filename);
 p.load(fis);
 }catch (Exception e) {
 e.printStackTrace();
 }finally {
 if(fis != null) {
 try {
 fis.close();
 }catch (Exception e) {}
 }
 }
 return p;
 }
}
5.4 RSAUtils.java
public class RSAUtils {
 /**
  * 生成公钥和私钥
  * 
  * @throws NoSuchAlgorithmException
  * 
  */
 public static HashMap<String, Object> getKeys()
 throws NoSuchAlgorithmException {
 HashMap<String, Object> map = new HashMap<String, Object>();
 KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
 keyPairGen.initialize(1024);
 KeyPair keyPair = keyPairGen.generateKeyPair();
 RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
 RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
 map.put("public", publicKey);
 map.put("private", privateKey);
 return map;
 }
 /**
  * 使用模和指数生成RSA公钥
  * 注意:【此代码用了默认补位方式,为RSA/None/PKCS1Padding,不同JDK默认的补位方式可能不同,如Android默认是RSA
  * /None/NoPadding】
  * 
  * @param modulus
  *            模
  * @param exponent
  *            指数
  * @return
  */
 public static RSAPublicKey getPublicKey(String modulus, String exponent) {
 try {
 BigInteger b1 = new BigInteger(modulus);
 BigInteger b2 = new BigInteger(exponent);
 KeyFactory keyFactory = KeyFactory.getInstance("RSA");
 RSAPublicKeySpec keySpec = new RSAPublicKeySpec(b1, b2);
 return (RSAPublicKey) keyFactory.generatePublic(keySpec);
 } catch (Exception e) {
 e.printStackTrace();
 return null;
 }
 }
 /**
  * 使用模和指数生成RSA私钥
  * 注意:【此代码用了默认补位方式,为RSA/None/PKCS1Padding,不同JDK默认的补位方式可能不同,如Android默认是RSA
  * /None/NoPadding】
  * 
  * @param modulus
  *            模
  * @param exponent
  *            指数
  * @return
  */
 public static RSAPrivateKey getPrivateKey(String modulus, String exponent) {
 try {
 BigInteger b1 = new BigInteger(modulus);
 BigInteger b2 = new BigInteger(exponent);
 KeyFactory keyFactory = KeyFactory.getInstance("RSA");
 RSAPrivateKeySpec keySpec = new RSAPrivateKeySpec(b1, b2);
 return (RSAPrivateKey) keyFactory.generatePrivate(keySpec);
 } catch (Exception e) {
 e.printStackTrace();
 return null;
 }
 }
 /**
  * 公钥加密
  * 
  * @param data
  * @param publicKey
  * @return
  * @throws Exception
  */
 public static String encryptByPublicKey(String data, RSAPublicKey publicKey)
 throws Exception {
 Cipher cipher = Cipher.getInstance("RSA");
 cipher.init(Cipher.ENCRYPT_MODE, publicKey);
 // 模长
 int key_len = publicKey.getModulus().bitLength() / 8;
 // 加密数据长度 <= 模长-11
 String[] datas = splitString(data, key_len - 11);
 String mi = "";
 // 如果明文长度大于模长-11则要分组加密
 for (String s : datas) {
 mi += bcd2Str(cipher.doFinal(s.getBytes()));
 }
 return mi;
 }
 /**
  * 私钥解密
  * 
  * @param data
  * @param privateKey
  * @return
  * @throws Exception
  */
 public static String decryptByPrivateKey(String data,
 RSAPrivateKey privateKey) throws Exception {
 Cipher cipher = Cipher.getInstance("RSA");
 cipher.init(Cipher.DECRYPT_MODE, privateKey);
 // 模长
 int key_len = privateKey.getModulus().bitLength() / 8;
 byte[] bytes = data.getBytes();
 byte[] bcd = ASCII_To_BCD(bytes, bytes.length);
 //System.err.println(bcd.length);
 // 如果密文长度大于模长则要分组解密
 String ming = "";
 byte[][] arrays = splitArray(bcd, key_len);
 for (byte[] arr : arrays) {
 ming += new String(cipher.doFinal(arr));
 }
 return ming;
 }
 /**
  * ASCII码转BCD码
  * 
  */
 public static byte[] ASCII_To_BCD(byte[] ascii, int asc_len) {
 byte[] bcd = new byte[asc_len / 2];
 int j = 0;
 for (int i = 0; i < (asc_len + 1) / 2; i++) {
 bcd[i] = asc_to_bcd(ascii[j++]);
 bcd[i] = (byte) (((j >= asc_len) ? 0x00 : asc_to_bcd(ascii[j++])) + (bcd[i] << 4));
 }
 return bcd;
 }
 public static byte asc_to_bcd(byte asc) {
 byte bcd;
 if ((asc >= '0') && (asc <= '9'))
 bcd = (byte) (asc - '0');
 else if ((asc >= 'A') && (asc <= 'F'))
 bcd = (byte) (asc - 'A' + 10);
 else if ((asc >= 'a') && (asc <= 'f'))
 bcd = (byte) (asc - 'a' + 10);
 else
 bcd = (byte) (asc - 48);
 return bcd;
 }
 /**
  * BCD转字符串
  */
 public static String bcd2Str(byte[] bytes) {
 char temp[] = new char[bytes.length * 2], val;
 for (int i = 0; i < bytes.length; i++) {
 val = (char) (((bytes[i] & 0xf0) >> 4) & 0x0f);
 temp[i * 2] = (char) (val > 9 ? val + 'A' - 10 : val + '0');
 val = (char) (bytes[i] & 0x0f);
 temp[i * 2 + 1] = (char) (val > 9 ? val + 'A' - 10 : val + '0');
 }
 return new String(temp);
 }
 /**
  * 拆分字符串
  */
 public static String[] splitString(String string, int len) {
 int x = string.length() / len;
 int y = string.length() % len;
 int z = 0;
 if (y != 0) {
 z = 1;
 }
 String[] strings = new String[x + z];
 String str = "";
 for (int i = 0; i < x + z; i++) {
 if (i == x + z - 1 && y != 0) {
 str = string.substring(i * len, i * len + y);
 } else {
 str = string.substring(i * len, i * len + len);
 }
 strings[i] = str;
 }
 return strings;
 }
 /**
  * 拆分数组
  */
 public static byte[][] splitArray(byte[] data, int len) {
 int x = data.length / len;
 int y = data.length % len;
 int z = 0;
 if (y != 0) {
 z = 1;
 }
 byte[][] arrays = new byte[x + z][];
 byte[] arr;
 for (int i = 0; i < x + z; i++) {
 arr = new byte[len];
 if (i == x + z - 1 && y != 0) {
 System.arraycopy(data, i * len, arr, 0, y);
 } else {
 System.arraycopy(data, i * len, arr, 0, len);
 }
 arrays[i] = arr;
 }
 return arrays;
 }
 public static void main(String[] args) throws Exception {
 // TODO Auto-generated method stub
 HashMap<String, Object> map = RSAUtils.getKeys();
 // 生成公钥和私钥
 RSAPublicKey publicKey = (RSAPublicKey) map.get("public");
 RSAPrivateKey privateKey = (RSAPrivateKey) map.get("private");
 // 模
 String modulus = publicKey.getModulus().toString();
 // 公钥指数
 String public_exponent = publicKey.getPublicExponent().toString();
 // 私钥指数
 String private_exponent = privateKey.getPrivateExponent().toString();
 // 明文
 String ming = "123456789";
 // 使用模和指数生成公钥和私钥
 RSAPublicKey pubKey = RSAUtils.getPublicKey(modulus, public_exponent);
 System.out.println("模:" + modulus);
 System.out.println("私钥:" + private_exponent);
 RSAPrivateKey priKey = RSAUtils
 .getPrivateKey(modulus, private_exponent);
 // 加密后的密文
 String mi = RSAUtils.encryptByPublicKey(ming, pubKey);
 System.out.println("加密后:"  + mi);
 // 解密后的明文
 ming = RSAUtils.decryptByPrivateKey(mi, priKey);
 System.out.println("解密后:" + ming);
 }
}

 

 

  • 11
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 12
    评论
Spring Boot中,可以通过自定义一个PropertySource来实现自定义PropertyPlaceholderConfigurer。 首先,创建一个类,继承自PropertySource,实现getProperties方法,该方法返回一个Properties对象,其中包含了我们要替换的属性值,例如: ```java public class MyPropertySource extends PropertySource { public MyPropertySource(String name) { super(name); } @Override public Object getProperty(String name) { // 返回我们要替换的属性值 if (name.equals("my.property")) { return "my custom property value"; } return null; } @Override public boolean containsProperty(String name) { // 判断是否包含我们要替换的属性 return name.equals("my.property"); } @Override public Enumeration<?> getPropertyNames() { // 返回所有属性名 return Collections.enumeration(Arrays.asList("my.property")); } @Override public String toString() { return "MyPropertySource"; } } ``` 然后,在Spring Boot启动类中,将该自定义PropertySource添加到Environment中: ```java @SpringBootApplication public class MyApp { public static void main(String[] args) { SpringApplication app = new SpringApplication(MyApp.class); app.setAdditionalProfiles("my-profile"); app.setEnvironment(new StandardEnvironment() { @Override protected void customizePropertySources(MutablePropertySources propertySources) { super.customizePropertySources(propertySources); propertySources.addFirst(new MyPropertySource("my-property-source")); } }); app.run(args); } } ``` 在上述代码中,我们通过setEnvironment方法设置了一个新的Environment,并在customizePropertySources方法中,将自定义PropertySource添加到了最前面,以确保优先使用该自定义PropertySource替换属性值。 最后,在application.yml或application.properties中,可以使用${my.property}来引用我们自定义的属性值,例如: ```yaml my.custom.property: ${my.property} ``` 这样,当应用启动时,就会将${my.property}替换成我们自定义的属性值"my custom property value"。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 12
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

杨得江-君临天下wyj

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值