不学无数——Spring注入后反射的空指针异常

简介: Spring注入后反射的空指针异常自动装配是在spring环境下当使用该类的实例时由spring容器完成了类的实例化过程,包括对依赖对象的实例化过程。

Spring注入后反射的空指针异常

自动装配是在spring环境下当使用该类的实例时由spring容器完成了类的实例化过程,包括对依赖对象的实例化过程。

而通过反射创建实例时,是根据你调用的构造函数完成的实例化过程,没有容器的自动化创建实例了,所以需要自己对依赖对象进行注入。

所以依赖spring容器实例化和自己用反射实例化是两种独立的方式,不能相互渗透的。

1. 异常说明

在单元测试类中,要单独测一个类中的private方法,所以想通过反射获得此方法,解除私有限定,然后进行调用。此类为Service其中注入了许多的Mapper,因此在通过调用此私有方法的时候报了空指针异常的错误,错误如下:

java.lang.reflect.InvocationTargetException
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at com.suixingpay.bap.service.impl.CheckAmsPmtAndPtsFlowServiceImplTest.testInvoke(CheckAmsPmtAndPtsFlowServiceImplTest.java:78)
    at com.suixingpay.bap.service.impl.CheckAmsPmtAndPtsFlowServiceImplTest.insertByList(CheckAmsPmtAndPtsFlowServiceImplTest.java:69)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.junit.runners.model.FrameworkMethod$1.runReflectiveCall(FrameworkMethod.java:50)
    at org.junit.internal.runners.model.ReflectiveCallable.run(ReflectiveCallable.java:12)
    at org.junit.runners.model.FrameworkMethod.invokeExplosively(FrameworkMethod.java:47)
    at org.junit.internal.runners.statements.InvokeMethod.evaluate(InvokeMethod.java:17)
    at org.junit.internal.runners.statements.RunBefores.evaluate(RunBefores.java:26)
    at org.springframework.test.context.junit4.statements.RunBeforeTestMethodCallbacks.evaluate(RunBeforeTestMethodCallbacks.java:75)
    at org.junit.internal.runners.statements.RunAfters.evaluate(RunAfters.java:27)
    at org.springframework.test.context.junit4.statements.RunAfterTestMethodCallbacks.evaluate(RunAfterTestMethodCallbacks.java:86)
    at org.springframework.test.context.junit4.statements.SpringRepeat.evaluate(SpringRepeat.java:84)
    at org.junit.runners.ParentRunner.runLeaf(ParentRunner.java:325)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:252)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.runChild(SpringJUnit4ClassRunner.java:94)
    at org.junit.runners.ParentRunner$3.run(ParentRunner.java:290)
    at org.junit.runners.ParentRunner$1.schedule(ParentRunner.java:71)
    at org.junit.runners.ParentRunner.runChildren(ParentRunner.java:288)
    at org.junit.runners.ParentRunner.access$000(ParentRunner.java:58)
    at org.junit.runners.ParentRunner$2.evaluate(ParentRunner.java:268)
    at org.springframework.test.context.junit4.statements.RunBeforeTestClassCallbacks.evaluate(RunBeforeTestClassCallbacks.java:61)
    at org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70)
    at org.junit.runners.ParentRunner.run(ParentRunner.java:363)
    at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:191)
    at org.junit.runner.JUnitCore.run(JUnitCore.java:137)
    at com.intellij.junit4.JUnit4IdeaTestRunner.startRunnerWithArgs(JUnit4IdeaTestRunner.java:68)
    at com.intellij.rt.execution.junit.IdeaTestRunner$Repeater.startRunnerWithArgs(IdeaTestRunner.java:47)
    at com.intellij.rt.execution.junit.JUnitStarter.prepareStreamsAndStart(JUnitStarter.java:242)
    at com.intellij.rt.execution.junit.JUnitStarter.main(JUnitStarter.java:70)
Caused by: java.lang.NullPointerException
    at com.suixingpay.bap.service.impl.CheckAmsPmtAndPtsFlowServiceImpl.succesExitReove(CheckAmsPmtAndPtsFlowServiceImpl.java:247)
    ... 36 more

注意最后面是由NullPointerException引起的。通过Debug进去以后发现是与数据库交互的Mapper没有注入进来。

单元测试代码如下:


    @Autowired
    private CheckAmsPmtAndPtsFlowServiceImpl checkAmsPmtAndPtsFlowServiceI;
    
    @Test
    public void insertByList()
            throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException,
            NoSuchFieldException {
        List<TBapCheckResultSave>  list = new ArrayList<>();
        Class cls = Class.forName("service.impl.CheckAmsPmtAndPtsFlowServiceImpl");
        Class[] cArg = new Class[5];
        cArg[0] = List.class;
        cArg[1] = boolean.class;
        cArg[2] = String.class;
        cArg[3] = String.class;
        cArg[4] = String.class;
        Method succesExitReove = cls.getDeclaredMethod("succesExitReove",cArg);
        succesExitReove.setAccessible(true);
        succesExitReove.invoke(checkAmsPmtAndPtsFlowServiceI,list,true,"1","1","2");
    }

此处有个知识点:在调用getDeclaredMethod()获得指定的方法时,第二个参数是方法的传参类型,包装类和其基本类型是不一样,例如下面方法为boolean,如果此时在上面 cArg[1] = Boolean.class;写为其包装类型的话,那么就会报错找不到此方法的错误

被反射的类中方法succesExitReove ()如下

    @Autowired
    private TMemAcPmtDtlPoMapper tMemAcPmtDtlPoMapper;
    
    private void succesExitReove(List<TBapCheckResultSave> result, boolean isFen, String sourceType, String opId, String batchId) {
        Iterator<TBapCheckResultSave> iteDownPtsNoCodeSuccces = result.iterator();
        while (iteDownPtsNoCodeSuccces.hasNext()) {
            int count;
            if (isFen) {
                count = tMemAcPmtDtlPoMapper.queryCheckFlow(bapCheckResult.getOutTransId());
            } else {
                count = tMemAcPmtDtlPoMapper.queryCheckFlow(bapCheckResult.getTransId());
            }
            bapCheckResult.setPmtCount(count);
            if (count > 0) {
                iteDownPtsNoCodeSuccces.remove();
            }
        }
    }

此处在Debug时发现tMemAcPmtDtlPoMapper为空。

2. 解决办法

在单元测试类中@Autowired要用的Mapper,然后通过反射获得上面方法要用到Mapper的变量,为其赋值即可

单元测试代码如下


    @Autowired
    private CheckAmsPmtAndPtsFlowServiceImpl checkAmsPmtAndPtsFlowServiceI;
    --在单元测试类中进行依赖注入Mapper
    @Autowired
    private TMemAcPmtDtlPoMapper tMemAcPmtDtlPoMapper;
    
    @Test
    public void insertByList()
            throws ClassNotFoundException, NoSuchMethodException, InvocationTargetException, IllegalAccessException,
            NoSuchFieldException {
        List<TBapCheckResultSave>  list = new ArrayList<>();
        Class cls = Class.forName("service.impl.CheckAmsPmtAndPtsFlowServiceImpl");
        Class[] cArg = new Class[5];
        cArg[0] = List.class;
        cArg[1] = boolean.class;
        cArg[2] = String.class;
        cArg[3] = String.class;
        cArg[4] = String.class;
        Field a1 = cls.getDeclaredField("tMemAcPmtDtlPoMapper");
        Method succesExitReove = cls.getDeclaredMethod("succesExitReove",cArg);
        succesExitReove.setAccessible(true);
        --在此为反射的类进行赋值Mapper
        Field a1 = cls.getDeclaredField("tMemAcPmtDtlPoMapper");
        a1.setAccessible(true);
        a1.set(checkAmsPmtAndPtsFlowServiceI,tMemAcPmtDtlPoMapper);
        succesExitReove.invoke(checkAmsPmtAndPtsFlowServiceI,list,true,"1","1","2");
    }

相关文章
|
Java Spring
在使用Spring的`@Value`注解注入属性值时,有一些特殊字符需要注意
【10月更文挑战第9天】在使用Spring的`@Value`注解注入属性值时,需注意一些特殊字符的正确处理方法,包括空格、引号、反斜杠、新行、制表符、逗号、大括号、$、百分号及其他特殊字符。通过适当包裹或转义,确保这些字符能被正确解析和注入。
754 3
|
Java 测试技术 程序员
为什么Spring不推荐@Autowired用于字段注入?
作为Java程序员,Spring框架在日常开发中使用频繁,其依赖注入机制带来了极大的便利。然而,尽管@Autowired注解简化了依赖注入,Spring官方却不推荐在字段上使用它。本文将探讨字段注入的现状及其存在的问题,如难以进行单元测试、违反单一职责原则及易引发NPE等,并介绍为何Spring推荐构造器注入,包括增强代码可读性和维护性、方便单元测试以及避免NPE等问题。通过示例代码展示如何将字段注入重构为构造器注入,提高代码质量。
395 1
|
9月前
|
JSON Java 数据格式
微服务——SpringBoot使用归纳——Spring Boot中的全局异常处理——处理系统异常
本文介绍了在Spring Boot项目中如何通过创建`GlobalExceptionHandler`类来全局处理系统异常。通过使用`@ControllerAdvice`注解,可以拦截项目中的各种异常,并结合`@ExceptionHandler`注解针对特定异常(如参数缺失、空指针等)进行定制化处理。文中详细展示了处理参数缺失异常和空指针异常的示例代码,并说明了通过拦截`Exception`父类实现统一异常处理的方法。虽然拦截`Exception`可一劳永逸,但为便于问题排查,建议优先处理常见异常,最后再兜底处理未知异常,确保返回给调用方的信息友好且明确。
1188 0
微服务——SpringBoot使用归纳——Spring Boot中的全局异常处理——处理系统异常
|
9月前
|
JSON Java 数据格式
微服务——SpringBoot使用归纳——Spring Boot中的全局异常处理——拦截自定义异常
本文介绍了在实际项目中如何拦截自定义异常。首先,通过定义异常信息枚举类 `BusinessMsgEnum`,统一管理业务异常的代码和消息。接着,创建自定义业务异常类 `BusinessErrorException`,并在其构造方法中传入枚举类以实现异常信息的封装。最后,利用 `GlobalExceptionHandler` 拦截并处理自定义异常,返回标准的 JSON 响应格式。文章还提供了示例代码和测试方法,展示了全局异常处理在 Spring Boot 项目中的应用价值。
419 0
|
12月前
|
Java Spring
一键注入 Spring 成员变量,顺序编程
介绍了一款针对Spring框架开发的插件,旨在解决开发中频繁滚动查找成员变量注入位置的问题。通过一键操作(如Ctrl+1),该插件可自动在类顶部添加`@Autowired`注解及其成员变量声明,同时保持光标位置不变,有效提升开发效率和代码编写流畅度。适用于IntelliJ IDEA 2023及以上版本。
167 2
一键注入 Spring 成员变量,顺序编程
|
Dubbo Java 应用服务中间件
深入探讨了“dubbo+nacos+springboot3的native打包成功后运行出现异常”的原因及解决方案
本文深入探讨了“dubbo+nacos+springboot3的native打包成功后运行出现异常”的原因及解决方案。通过检查GraalVM版本兼容性、配置反射列表、使用代理类、检查配置文件、禁用不支持的功能、查看日志文件、使用GraalVM诊断工具和调整GraalVM配置等步骤,帮助开发者快速定位并解决问题,确保服务的正常运行。
457 1
|
Java 关系型数据库 数据库连接
SpringBoot项目使用yml文件链接数据库异常
【10月更文挑战第3天】Spring Boot项目中数据库连接问题可能源于配置错误或依赖缺失。YAML配置文件的格式不正确,如缩进错误,会导致解析失败;而数据库驱动不匹配、连接字符串或认证信息错误同样引发连接异常。解决方法包括检查并修正YAML格式,确认配置属性无误,以及添加正确的数据库驱动依赖。利用日志记录和异常信息分析可辅助问题排查。
1206 11
|
前端开发 小程序 Java
【规范】SpringBoot接口返回结果及异常统一处理,这样封装才优雅
本文详细介绍了如何在SpringBoot项目中统一处理接口返回结果及全局异常。首先,通过封装`ResponseResult`类,实现了接口返回结果的规范化,包括状态码、状态信息、返回信息和数据等字段,提供了多种成功和失败的返回方法。其次,利用`@RestControllerAdvice`和`@ExceptionHandler`注解配置全局异常处理,捕获并友好地处理各种异常信息。
6659 1
【规范】SpringBoot接口返回结果及异常统一处理,这样封装才优雅
|
Java API Spring
springBoot:注解&封装类&异常类&登录实现类 (八)
本文介绍了Spring Boot项目中的一些关键代码片段,包括使用`@PathVariable`绑定路径参数、创建封装类Result和异常处理类GlobalException、定义常量接口Constants、自定义异常ServiceException以及实现用户登录功能。通过这些代码,展示了如何构建RESTful API,处理请求参数,统一返回结果格式,以及全局异常处理等核心功能。
153 1
|
Java 关系型数据库 MySQL
SpringBoot项目使用yml文件链接数据库异常
【10月更文挑战第4天】本文分析了Spring Boot应用在连接数据库时可能遇到的问题及其解决方案。主要从四个方面探讨:配置文件格式错误、依赖缺失或版本不兼容、数据库服务问题、配置属性未正确注入。针对这些问题,提供了详细的检查方法和调试技巧,如检查YAML格式、验证依赖版本、确认数据库服务状态及用户权限,并通过日志和断点调试定位问题。
1320 6