资讯专栏INFORMATION COLUMN

Spring Boot静态成员注入导致的NullPointerException(NPE) 问题

XUI / 1493人阅读

摘要:问题场景有一个工具类用于对支付参数进行签名其中使用了配置类签名工具类如下这里的输出为访问它将会导致排序参数拼接支付签名参数拼接签名并返回这样是不行的是一个静态成员容器在初始化过程中如果看到这是一个静态的成员它会直接跳过这个成员字段处理下一个

问题场景

有一个工具类, 用于对支付参数进行签名, 其中使用了 @ConfigurationProperties 配置类. 签名工具类如下:

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 map = new TreeMap<>();
        map.put("merchantOrderId", payOrderRequest.getMerchantOrderId());
        map.put("createdAt", payOrderRequest.getCreatedAt());
        ...
        ...
        ...
        
        List 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,
然后, 创建这个代理类, 并使用PostConstructSignUtil 工具类的 PayProperties 成员进行手工注入:

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.java

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 map = new TreeMap<>();
        map.put("merchantOrderId", payOrderRequest.getMerchantOrderId());
        map.put("createdAt", payOrderRequest.getCreatedAt());
        
        List 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();
    }
}

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/72318.html

相关文章

  • Spring Boot 2.x(六):优雅统一返回值

    摘要:下面我们来测试一下,访问我们经过修改后的编写的接口这里我将返回值统一为,以便数据存入,实际类型应是接口的返回类型。如果没有返回值的话,那就可以一个对象直接通过构造方法赋值即可。 为什么要统一返回值 在我们做后端应用的时候,前后端分离的情况下,我们经常会定义一个数据格式,通常会包含code,message,data这三个必不可少的信息来方便我们的交流,下面我们直接来看代码 ReturnV...

    shaonbean 评论0 收藏0
  • Spring Boot 2.x(七):全局处理异常

    摘要:全局异常处理类用于全局返回,如需返回请使用继承了,对于一些类似于请求方式异常的异常进行捕获重写,自定义处理过程这里将异常直接传给方法进行处理,返回值为保证友好的返回,而不是出现错误码。 前言 异常的处理在我们的日常开发中是一个绕不过去的坎,在Spring Boot 项目中如何优雅的去处理异常,正是我们这一节课需要研究的方向。 异常的分类 在一个Spring Boot项目中,我们可以把异...

    ivyzhang 评论0 收藏0
  • Lombok安装及Spring Boot集成Lombok

    摘要:注意,其是在编译源码过程中,帮你自动生成的。就是说,将极大减少你的代码总量。注解和类似,区别在于它会把所有成员变量默认定义为修饰,并且不会生成方法。不同的日志注解总结如下上面是注解,下面是编译后的代码参考资料下的安装以及使用简介注解介绍 Lombok有什么用 在我们实体Bean中有大量的Getter/Setter方法以及toString, hashCode等可能不会用到,但是某些时候仍...

    dkzwm 评论0 收藏0
  • Spring Boot 单元测试二三事

    摘要:但是,一个好的单元测试应该是毫秒级的,否则这会影响的工作方式,这也就是测试驱动开发的思想。在单元测试中,我们可以像这样来构建一个实例。所以,我们在写单元测试的时候,应该以一种更简单的方式去构建。 本文翻译自:https://reflectoring.io/unit-...原文作者:Tom Hombergs 译文原地址:https://weyunx.com/2019/02/04... ...

    xbynet 评论0 收藏0
  • Javag工程师成神之路(2019正式版)

    摘要:结构型模式适配器模式桥接模式装饰模式组合模式外观模式享元模式代理模式。行为型模式模版方法模式命令模式迭代器模式观察者模式中介者模式备忘录模式解释器模式模式状态模式策略模式职责链模式责任链模式访问者模式。 主要版本 更新时间 备注 v1.0 2015-08-01 首次发布 v1.1 2018-03-12 增加新技术知识、完善知识体系 v2.0 2019-02-19 结构...

    Olivia 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<