有一個工具類, 用於對支付參數進行簽名, 其中使用了 @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(); } }