有一个工具类, 用于对支付参数进行签名, 其中使用了 @ConfigurationProperties 配置类. 签名工具类以下:java
import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.DigestUtils; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.TreeMap; @Slf4j public class SignUtil { private static PayProperties payProperties; @Autowired public static void setPayProperties(PayProperties payProperties) { SignUtil.payProperties = payProperties; } public static String signParams(PayOrderRequest payOrderRequest, String apiKey) { //////////////////////////////////////////////////////////////////// // 这里 payProperties 的输出为 null, 访问它将会致使 NullPointerException //////////////////////////////////////////////////////////////////// log.info("============================: {}", payProperties); // 排序 Map<String, String> map = new TreeMap<>(); map.put("merchantOrderId", payOrderRequest.getMerchantOrderId()); map.put("createdAt", payOrderRequest.getCreatedAt()); ... ... ... List<String> params = new ArrayList<>(); for (String key : map.keySet()) { log.debug("参数: {}", String.format("%s=%s", key, map.get(key))); params.add(map.get(key)); } params.add(apiKey); // 拼接 String plainTextParams = String.join("|", params); log.info("支付签名参数拼接: {}", plainTextParams); // 签名并返回 return DigestUtils.md5DigestAsHex(plainTextParams.getBytes()).toUpperCase(); } }
这样是不行的,
PayProperties
是一个静态成员, Spring 容器在初始化过程当中若是看到这是一个静态的成员, 它会直接跳过这个成员字段, 处理下一个字段.
使用一个代理Bean类对静态成员进行初始化, 这个代理Bean能够叫作
StaticContextInitializer
(静态山下文初始化器)
首先, 这个代理类须要使用 @Component
进行注解
其次, 删除上述代码中静态方法 setPayProperties
的注解 @Autowired
,
而后, 建立这个代理类, 并使用PostConstruct
对 SignUtil
工具类的 PayProperties
成员进行手工注入:spring
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; @Component public class StaticContextInitializer { private PayProperties anyiProperties; @Autowired public void setAnyiProperties(PayProperties anyiProperties) { this.anyiProperties = anyiProperties; } @PostConstruct public void init() { SignUtil.setPayProperties(anyiProperties); } }
StaticContextInitializer.javaapi
import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import javax.annotation.PostConstruct; @Component public class StaticContextInitializer { private PayProperties anyiProperties; @Autowired public void setAnyiProperties(PayProperties anyiProperties) { this.anyiProperties = anyiProperties; } @PostConstruct public void init() { SignUtil.setPayProperties(anyiProperties); } }
SignUtil.java工具
import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.util.DigestUtils; import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.TreeMap; @Slf4j public class SignUtil { private static PayProperties payProperties; @Autowired public static void setPayProperties(PayProperties payProperties) { SignUtil.payProperties = payProperties; } public static String signParams(PayOrderRequest payOrderRequest, String apiKey) { //////////////////////////////////////////////////////////////////// // payProperties 字段已经通 StaticContextInitializer.init() 进行注入 // 不会再出现 NPE 问题 //////////////////////////////////////////////////////////////////// log.info("============================: {}", payProperties); // 排序 Map<String, String> map = new TreeMap<>(); map.put("merchantOrderId", payOrderRequest.getMerchantOrderId()); map.put("createdAt", payOrderRequest.getCreatedAt()); List<String> params = new ArrayList<>(); for (String key : map.keySet()) { log.debug("参数: {}", String.format("%s=%s", key, map.get(key))); params.add(map.get(key)); } params.add(apiKey); // 拼接 String plainTextParams = String.join("|", params); log.info("支付签名参数拼接: {}", plainTextParams); // 签名并返回 return DigestUtils.md5DigestAsHex(plainTextParams.getBytes()).toUpperCase(); } }