解决Springboot使用@Autowired注解注入的静态变量在使用时报空指针问题

一、背景介绍

我创建了两个Springboot工程,A作为公共工程,打包成Jar包供其他工程使用;B使用A提供的Jar包。A工程中的SceneCaseDebugAPI类的代码如下:

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.jayway.jsonpath.JsonPath;
import com.umetrip.qa.model.*;
import com.umetrip.qa.service.GatewayCaseService;
import com.umetrip.qa.service.SceneCaseService;
import com.umetrip.qa.service.ScenePublicParameterService;
import com.umetrip.qa.service.SceneStepsService;
import com.umetrip.qa.tester.singlecase.SingleCaseDebugAPI;
import com.umetrip.qa.utils.JsonStringUtils;
import com.umetrip.qa.utils.OkhttpUtil;
import com.umetrip.qa.utils.PrettyTimeTool;
import com.umetrip.qa.utils.request.ApiRequestCommonUtil;
import com.umetrip.qa.utils.request.RequestHttp_error;
import org.jsoup.helper.StringUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.regex.Pattern;

/**
 * @description: 普通java类要使用 @Autowired注解 的话,需要使用@Component注解申明该类是spring组件
 * @time: 2021-02-23 11:12
 */
@Component
public class SceneCaseDebugAPI {
    @Autowired
    static SceneCaseService sceneCaseService;

    @Autowired
    static SceneStepsService sceneStepsService;

    @Autowired
    static ScenePublicParameterService scenePublicParameterService;

    @Autowired
    static GatewayCaseService gatewayCaseService;

    @Autowired
    static SingleCaseDebugAPI singleCaseDebugAPI;

    public static ApiResult sceneCaseCommonRequest(JSONObject sceneCaseJson){
        ApiResult apiResult = new ApiResult();
        //步骤1:从DB中查询出场景用例的详细信息
        System.out.println("sceneCase: " + JSONObject.toJSONString(sceneCaseJson));
        SceneCase sceneCase = JSONObject.parseObject(JSONObject.toJSONString(sceneCaseJson), SceneCase.class);
        String singleSceneCaseRedisKey = "SceneCase" + "_" + sceneCase.getId() + "_" + sceneCase.getSceneName() + "_" + "Result";
        List<SceneSteps> sceneSteps = sceneStepsService.findAllSceneStepsBySceneCaseId(sceneCase.getId());
        // 清空redis中场景用例的用例步骤信息
//        baseRedisDao.remove(singleSceneCaseRedisKey);
        //设置一个数组存储场景用例步骤的结果
        List<String> stepsResult = new ArrayList<>();
        try{
            for(SceneSteps step : sceneSteps){
                System.out.println("当前正在处理的场景用例步骤信息为:" + JSONObject.toJSONString(step));
                JSONObject stepJson = JSONObject.parseObject(JSONObject.toJSONString(step), JSONObject.class);
                String sceneStepType = step.getSceneStepType();
                if(sceneStepType.equals("HTTP用例")){
                    ResMsg resMsg = executeHttpCaseStep(sceneCase.getId(), step);
                    stepJson = JSONObject.parseObject(JSONObject.toJSONString(resMsg), JSONObject.class);
                    stepsResult.add(resMsg.getResult());
                }else if(sceneStepType.equals("等待")){
                    Thread.sleep(step.getSceneStepWaitingtime());
                }else if(sceneStepType.equals("GET请求")){
                    ResMsg resMsg = executeGetRequestStep(sceneCase.getId(), step);
                    stepJson = JSONObject.parseObject(JSONObject.toJSONString(resMsg), JSONObject.class);
                    stepsResult.add(resMsg.getResult());
                }else if(sceneStepType.equals("条件")){
                    stepJson.put("debugCaseStartTime", new Date());
                    boolean result = executeConditionStep(sceneCase.getId(), step);
                    if(result){
                        stepJson.put("sceneCaseStepResult", "T");
                        stepsResult.add("T");
                    }else {
                        stepJson.put("sceneCaseStepResult", "F");
                        stepsResult.add("F");
                    }
                }
                PrettyTimeTool.createNewInterfaceCaseDebugLog(stepJson.getDate("debugCaseStartTime"), "SingleInterfaceCase", sceneCase.getId(), stepJson);
            }
        }catch (Exception e){
            e.printStackTrace();
            apiResult.setCode(400);
            apiResult.setResult("场景用例执行失败");
        }
        // 还需要增加场景用例结果判断逻辑,只要有一个失败步骤失败,场景用例结果即为失败;否则为成功
        if(stepsResult.contains("F")){
            apiResult.setCode(400);
            apiResult.setResult("场景用例执行失败");
        }else {
            apiResult.setCode(200);
            apiResult.setResult("场景用例执行成功");
        }
        return apiResult;
    }

    public static ResMsg executeGetRequestStep(Integer sceneCaseId, SceneSteps step){
        System.out.println("执行Get请求步骤");
        ResMsg resMsg = new ResMsg();
        ApiRequestCommonUtil apiRequestCommonUtil = new ApiRequestCommonUtil();
        String newUrl = step.getSceneStepGetUrl();
        JSONArray params = JSONArray.parseArray(step.getSceneStepGetParams());
        JSONArray header = JSONArray.parseArray(step.getSceneStepGetHeader());
        System.out.println("请求参数的数据:" + params.size() + "   请求头的数据:" + header.size());
        for(int i = 0; i < params.size(); i++){
            JSONObject paramsTableOneRow = params.getJSONObject(i);
            String key = paramsTableOneRow.getString("key");
            String val = paramsTableOneRow.getString("value");

            if(val.contains("${")){
                String dbParam = val.substring(2, val.length() - 1);
                System.out.println("1 dbParam: " + dbParam);
                // 从场景内公共参数查找变量,当查找不到时,需要去全局公共参数中查找(目前尚未实现)
                ScenePublicParameter scenePublicParameter = apiRequestCommonUtil.findPublicParamValue(sceneCaseId, dbParam);
                val = scenePublicParameter.getSceneParameterVal();
            }
            if (i == 0) {
                System.out.println("2 newUrl: " + newUrl + " key:" + key + " val:" + val);
                newUrl = newUrl + "?" + key + "=" + val;
            } else {
                System.out.println("3 newUrl: " + newUrl + " key:" + key + " val:" + val);
                newUrl = newUrl + "&" + key + "=" + val;
            }
        }
        System.out.println("处理后的url:" + newUrl);
        // 处理header
        if(header.size() != 0){
            for(int j = 0; j < header.size(); j++){
                JSONObject headerTableOneRow= header.getJSONObject(j);
                String key = headerTableOneRow.getString("key");
                String val = headerTableOneRow.getString("value");
                if(val.contains("${")){
                    String dbParam = val.substring(2, val.length() - 1);
                    System.out.println("1 dbParam: " + dbParam);
                    ScenePublicParameter scenePublicParameter = apiRequestCommonUtil.findPublicParamValue(sceneCaseId, dbParam);
                    val = scenePublicParameter.getSceneParameterVal();
                }
                headerTableOneRow.put("value", val);
            }
        }
        System.out.println("header str: " + JSONArray.toJSONString(header));
        long startTime = System.currentTimeMillis();
        Date deBugCaseStartTime = new Date();
        // 请求url和header已经准备完毕,真正发送GET请求
        String getResult = OkhttpUtil.dynamicGet(newUrl, header);
        long endTime = System.currentTimeMillis();
        long consumetime = endTime - startTime;

        // 判断是否需要将响应体中数据存储至场景内的公共参数表
        if(!StringUtil.isBlank(step.getSceneStepExtractJsonPath())){
            String[] jsonPathList = step.getSceneStepExtractJsonPath().split("\r|\n");
            for(int i = 0; i<jsonPathList.length; i++){
                getResponseBodyJsonPathVal(sceneCaseId, getResult, jsonPathList[i]);
            }
        }
        //需要加断言处理
        System.out.println("Get请求的返回结果:" + getResult);
        RequestHttp_error requestHttp = new RequestHttp_error();
        requestHttp.assertResponse(getResult, step.getSceneStepGetExpectResponse(), resMsg);
        resMsg.setConsumetime(consumetime);
        resMsg.setInterfaceType("SceneCase");
        resMsg.setRequestBodyParameters(JSONObject.toJSONString(params));
        resMsg.setResponsebody(getResult);
        resMsg.setUrl(newUrl);
        resMsg.setHttpHeaders(JSONObject.toJSONString(header));
        resMsg.setTestpoint(step.getSceneStepName());
        resMsg.setDebugCaseStartTime(deBugCaseStartTime);
        return resMsg;
    }

    public static boolean executeConditionStep(Integer sceneCaseId, SceneSteps step){
        System.out.println("执行条件步骤");
        boolean res = false;
        ApiRequestCommonUtil apiRequestCommonUtil = new ApiRequestCommonUtil();
        String firstExpression = step.getSceneFirstExpression();
        String secondExpression = step.getSceneSecondExpression();
        String judgeCondition = step.getSceneExpressionJudgeCondition();
        if(secondExpression.contains("${")){
            String dbParam = secondExpression.substring(2, secondExpression.length() - 1);
            System.out.println("1 dbParam: " + dbParam);
            ScenePublicParameter scenePublicParameter = apiRequestCommonUtil.findPublicParamValue(sceneCaseId, dbParam);
            secondExpression = scenePublicParameter.getSceneParameterVal();
        }

        switch (judgeCondition){
            case "等于":
                res = firstExpression.equals(secondExpression);
                break;
            case "不等于":
                res = !firstExpression.equals(secondExpression);
                break;
            case "包含":
                res = firstExpression.contains(secondExpression);
                break;
            case "不包含":
                res = !firstExpression.contains(secondExpression);
                break;
            case "大于":
                res = Integer.parseInt(firstExpression) > Integer.parseInt(secondExpression);
                break;
            case "小于":
                res = Integer.parseInt(firstExpression) < Integer.parseInt(secondExpression);
                break;
        }
        System.out.println("条件步骤的运行结果:" + res);
        return res;
    }

    public static ResMsg executeHttpCaseStep(Integer sceneCaseId, SceneSteps step){
        System.out.println("执行Http用例步骤");
        Integer gatewaySingleCaseId = step.getSceneGatewaySingleCaseId();
        GatewayCase gatewayCase = gatewayCaseService.findByPrimaryKey(gatewaySingleCaseId);
        System.out.println("gateway case rparams before:" + gatewayCase.getRparams());
        JSONObject jsonObjectDB = JSONObject.parseObject(JsonStringUtils.StringInDBtoJSONString(gatewayCase.getRparams()));
        ApiRequestCommonUtil.processRequestParameterization(sceneCaseId, jsonObjectDB);
        System.out.println("gateway case rparams after:" + JsonStringUtils.JSONStringtoStringInDB(JSONObject.toJSONString(jsonObjectDB)));
        gatewayCase.setRparams(JsonStringUtils.JSONStringtoStringInDB(JSONObject.toJSONString(jsonObjectDB)));

        JSONObject jsonObject = JSONObject.parseObject(JSONObject.toJSONString(gatewayCase), JSONObject.class);
        jsonObject.put("env", "gray");
        jsonObject.put("interfaceType", "GateWay");

        Date deBugCaseStartTime = new Date();
        ResMsg resMsg = singleCaseDebugAPI.singleCaseDebug(jsonObject);
        resMsg.setDebugCaseStartTime(deBugCaseStartTime);
        // jsonpath
        if(!StringUtil.isBlank(gatewayCase.getExtractJsonPath())){
            String[] jsonPathList = gatewayCase.getExtractJsonPath().split("\r|\n");
            System.out.println("executeHttpCaseStep jsonPathList length:" + jsonPathList.length);
            for(int i = 0; i<jsonPathList.length; i++){
                getResponseBodyJsonPathVal(sceneCaseId, resMsg.getResponsebody(), jsonPathList[i]);
            }
        }
        System.out.println("Http用例执行的结果:" + JSONObject.toJSONString(resMsg));
        return resMsg;
    }

    public static void getResponseBodyJsonPathVal(Integer sceneCaseId, String responseBody, String jsonPathKey){
        System.out.println("jsonPathKey: " + jsonPathKey);
        try{
            Object actualValue = JsonPath.read(responseBody, jsonPathKey);
            System.out.println("actualValue: " + actualValue.toString());

            // 存入数据库的字段,以jsonpath分隔出的最后一个为准
            String[] paramList = jsonPathKey.split("\\.");
            System.out.println("paramList length: " + paramList.length);
            String param = paramList[paramList.length - 1];
            System.out.println("param: " + param);
            boolean isNumber = isNumber(actualValue.toString());
            boolean isBoolean = isBoolean(actualValue.toString());

            ScenePublicParameter scenePublicParameterDB = scenePublicParameterService.findPublicParamValueBySceneCaseIdAndName(sceneCaseId, param);
            if(scenePublicParameterDB == null){
                System.out.println("新增~~");
                ScenePublicParameter scenePublicParameter = new ScenePublicParameter();
                scenePublicParameter.setSceneCaseId(sceneCaseId);
                scenePublicParameter.setSceneParameterName(param);

                if(isNumber){
                    // 这种类型怎么办
                    scenePublicParameter.setSceneParameterType("整数");
                    scenePublicParameter.setSceneParameterVal(String.valueOf(actualValue));
                } else if(isBoolean){
                    scenePublicParameter.setSceneParameterType("布尔");
                    scenePublicParameter.setSceneParameterVal(String.valueOf(actualValue));
                } else {
                    scenePublicParameter.setSceneParameterType("字符串");
                    scenePublicParameter.setSceneParameterVal((String)actualValue);
                }
                scenePublicParameterService.insert(scenePublicParameter);
            }else {
                System.out.println("更新~~");
                if(isNumber){
                    scenePublicParameterDB.setSceneParameterVal(String.valueOf(actualValue));
                } else if(isBoolean){
                    scenePublicParameterDB.setSceneParameterVal(String.valueOf(actualValue));
                } else {
                    scenePublicParameterDB.setSceneParameterVal((String)actualValue);
                }
                scenePublicParameterService.update(scenePublicParameterDB);
            }
        }catch (Exception e){
            e.printStackTrace();
        }
    }

    public static boolean isNumber(String str) {
        //采用正则表达式的方式来判断一个字符串是否为数字,这种方式判断面比较全
        //可以判断正负、整数小数

        boolean isInt = Pattern.compile("^-?[1-9]\\d*$").matcher(str).find();
        boolean isDouble = Pattern.compile("^-?([1-9]\\d*\\.\\d*|0\\.\\d*[1-9]\\d*|0?\\.0+|0)$").matcher(str).find();
        return isInt || isDouble;
    }

    public static boolean isBoolean(String str){
        return str.equals("true") || str.equals("false");
    }
}

其中的sceneCaseService、sceneStepsService…等五个对象是使用@Autowired从Spring容器中获取的,并都声明成了静态变量。但是在使用上述变量时发现,上述五个变量会报空指针错误

二、问题分析

我们都知道静态变量,静态方法都是属于类的;普通变量、普通方法都是属于实例化对象的(即new处理的对象);Spring依赖注入是在容器中实例化对象;

当我们使用静态变量或静态方法时,不需要new出来一个类的实例化对象,所以使用@Autowired修饰一个静态变量时,该静态变量并没有真正实例化成一个对象,因此该静态变量为null

三、解决方法

3.1 将@Autowired加到构造方法上

@Component
public class RuleFunctions {
  @Autowired
  private static RuleEntityItemInfoBiz ruleEntityItemInfoBiz;
  
  @Autowired
  public RuleFunctions(RuleEntityItemInfoBiz ruleEntityItemInfoBiz) {
RuleFunctions.ruleEntityItemInfoBiz = ruleEntityItemInfoBiz;
}
  public static double calculateCurrentMonthIncomeTax(String fileId, String salaryMonth, String taxPlanId){
    //此处省略方法实现逻辑
  }
}

3.2 使用set方法

@Component
public class RuleFunctions {
  @Autowired
  private static RuleEntityItemInfoBiz ruleEntityItemInfoBiz;
  
  @Autowired
  public void setRuleEntityItemInfoBiz(RuleEntityItemInfoBiz ruleEntityItemInfoBiz) {
RuleFunctions.ruleEntityItemInfoBiz = ruleEntityItemInfoBiz;
}
  public static double calculateCurrentMonthIncomeTax(String fileId, String salaryMonth, String taxPlanId){
    //此处省略方法实现逻辑
  }
}

3.3 使用@PostConstruct注解

3.3.1 @PostConstruct注解

@PostConstruct注解位于javax.annotation包下,可以用来修饰一个非静态的void返回的方法。

被@PostConstruct注解修饰的方法的执行时机是:在服务器加载Servlet的时候运行,并且只会被服务器加载一次;在构造函数之后,init()方法之前执行

被@PostConstruct注解修饰的方法参数规范:除了拦截器这种特殊情况以外,其他情况都不允许有参数,否则Spring框架会报IllegalStateException;而且返回值要是void,但实际也可以有返回值,至少不会报错,只会忽略

Spring框架中使用到@PostConstruct注解,该注解修饰的方法在整个Bean初始化过程中的执行顺序如下:

Constructor(构造方法) -> @Autowired(依赖注入) -> @PostConstruct(注释的方法)

3.3.2 实际例子
@Component
public class RuleFunctions {
	@Autowired
	private RuleEntityItemInfoBiz ruleEntityItemInfoBiz;

	private static RuleEntityItemInfoBiz staticRuleEntityItemInfoBiz;  

	/**
	*注释用于在完成依赖注入以后执行任何初始化之后需要执行的方法。必须在类投入使用之前调用此方法。
	*/
	@PostConstruct
	public void beforeInit() {
		staticRuleEntityItemInfoBiz = ruleEntityItemInfoBiz;
	}
	
  public static double calculateCurrentMonthIncomeTax(String fileId, String salaryMonth, String taxPlanId){
    //此处省略方法实现逻辑
  }
}

参考博文:spingboot使用@Resource注入静态变量报空指针的问题解决

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值