背景

遇到问题:在进行Spring单元测试编写时,发现被测方法是一个私有方法,无法直接通过注入对象调用

解决思路:首先想到通过反射获取该私有方法的访问权限,并传入注入对象,最终调用对象的私有方法。

出现的异常

运行时抛出空指针异常

定位问题

  1. 点击异常代码行打上断点,debug调试

  2. 通过查看变量值发现roleMapper为空,从而导致空指针
  3. 而roleMapper是传入this对象的属性,因此,问题来自传入的对象

分析问题

  1. 通过分析this对象,可以发现它是一个被Cglib代理后的实例,由此可知,该类方法上必定有@Transactional事务注解或AOP注解修饰,从而被SpringCglib代理

  2. 查看cglib原理:

动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。

其中重要的一点,代理类是被代理类的子类,回想关于Java中的继承,有一条很重要的特性就是:

  • 子类拥有父类非 private 的属性、方法。
  1. 此时,尝试修改私有方法变成public,发现this对象恢复正常,由此锁定代理类和私有方法出现问题

  2. 通过搜索cglib代理类私有方法发现原因:



  3. 由此可知,此处注入的cglib代理对象中不包含private方法!
  4. 那为啥同样传入的代理对象,调用public方法就成功,而调用private方法就失败呢?
  1. 如果是私有方法,那么在代理类中,不会包含这个方法。此时通过Method.invoke()来调用目标方法,传入的实例对象是userController的代理类,而这个代理类中的userService为NULL,所以,执行的时候,才会看到userService没有注入,导致空指针异常。
  2. 如果是公共方法,在代理类中,就有它的子类实现,则会先调用到代理类的拦截器MethodInterceptor。拦截器负责链式调用AOP方法和目标方法。在拦截器执行过程中,又调用了方法。但不同的是,此时传入的实例对象并不是代理类,而是代理类的目标对象。

结论:可以发现代理类正常情况下,执行到原方法时是通过代理的目标对象(即原始对象)来执行,而当代理类发现没有代理对应的private方法时,则直接通过代理对象(即上文的this)执行目标方法。

解决方法

既然我们需要的是只原始对象执行私有方法,只要通过代理类获取原始的目标对象即可。

// 由于cglib类是通过继承代理,无法代理私有方法,因此无法通过原始对象执行方法
if (AopUtils.isCglibProxy(menuService)) {
// 如果是cglib代理对象,则转为原始对象
menuService = (MenuServiceImpl)AopProxyUtils.getSingletonTarget(menuService);
}

此时得到的对象即为原始对象,bug成功消灭!

参考文章:

[bug]spring项目通过反射测试私有方法时,注入对象异常的更多相关文章

  1. JUnit 3.8 通过反射测试私有方法

    测试私有(private)的方法有两种: 1)把目标类的私有方法(修饰符:private)修改为(public),不推荐,因为修改了源程序不佳 2)通过反射 (推荐) 代码演示: 目标程序 Priva ...

  2. 无所不能的PowerMock,mock私有方法,静态方法,测试私有方法,final类

    1.为什么要用mock 我的一本书的解释: (1)创建所需的DB数据可能需要很长时间,如:调用别的接口,模拟很多数据 (2)调用第三方API接口,测试很慢, (3)编写满足所有外部依赖的测试可能很复杂 ...

  3. Android Studio 重写方法时参数命名异常

    Android Studio 重写方法时参数命名异常 Android Studio 重写方法时参数名称乱掉可以通过下载相应源码解决

  4. 使用PowerMockito和Mockito进行模拟测试,包括静态方法测试,私有方法测试等,以及方法执行的坑或者模拟不成功解决

    依赖:这个很重要,不同版本用法也有点区别: <dependency> <groupId>org.mockito</groupId> <artifactId&g ...

  5. java中用反射访问私有方法和私有成员[转]

    转自: http://zhouyangchenrui.iteye.com/blog/470521 java的反射可以绕过访问权限,访问到类的私有方法和成员.可能这点会引起安全性的讨论.反射的使用帮助解 ...

  6. Java 反射获取私有方法

    通常我们创建一个类时,它的私有方法在类外是不可见的,但是可以通过反射机制来获取调用.具体的反射机制的介绍大家自己百度. 所以反射可能会破坏我们的单例模式,当然解决方案也是有的,就是做个标记记录次数,第 ...

  7. c# 通过反射获取私有方法

    class Program { static void Main(string[] args) { //通过反射来调私有的成员 Type type = typeof(Person); //Bindin ...

  8. Java开发笔记(八十)利用反射技术操作私有方法

    前面介绍了如何利用反射技术读写私有属性,不单是私有属性,就连私有方法也能通过反射技术来调用.为了演示反射的逆天功能,首先给Chicken鸡类增加下列几个私有方法,简单起见弄来了set***/get** ...

  9. java反射调用私有方法和修改私有属性

    //调用私有方法package com.java.test; public class PrivateMethod { private String sayHello(String name) { r ...

随机推荐

  1. Python中调用Java程序包

    <原创不易,转载请标明出处:https://www.cnblogs.com/bandaobudaoweng/p/10785766.html> 开发Python程序,需求中需要用到Java代 ...

  2. [WUSTCTF2020]朴实无华

    [WUSTCTF2020]朴实无华 考点:1.MD5碰撞 2.命令执行绕过 打开题发现编码有些问题,修改编码为utf-8 看了源码和请求包没发现什么东西,看到title想到了robots.txt文件, ...

  3. Linux下-LNMP环境搭建博客网站(全过程)

    通常我们所说的LNMP是指一个网站基本的组织框架,即Linux系统支持,Nginx静态服务,Mysql数据库支持以及PHP动态编程语言支持.目前Mysql数据库被Oracle数据库分析公司收购,其创始 ...

  4. ERROR: database "db" is being accessed by other users

    执行DROP DATABASE testdb;的时候提示: ERROR: database "testdb" is being accessed by other users DE ...

  5. Upload-labs 文件上传靶场通关攻略(上)

    Upload-labs 文件上传靶场通关攻略(上) 文件上传是Web网页中常见的功能之一,通常情况下恶意的文件上传,会形成漏洞. 逻辑是这样的:用户通过上传点上传了恶意文件,通过服务器的校验后保存到指 ...

  6. 01 CTF从0到。。。。

    无意间在前段时间接触到了CTF,感觉很有意思,就参加了个单位的短期培训,并且参加了比赛,也是无意混进了决赛.感觉自己不会的还很多!SO,开始写博客开始刷题,自己很菜,不会C,不会Python,不会汇编 ...

  7. Spring学习04(使用注解开发)

    7.使用注解开发 说明:在spring4之后,想要使用注解形式,必须得要引入aop的包. 在配置文件当中,还得要引入一个context约束 <?xml version="1.0&quo ...

  8. 为什么有些容器在docker run的时候需要接 -it ,有些不需要?

    这是我们的Dockerfile文件 FROM busybox ENV sg WANG CMD ["/bin/sh", "-c", "echo wang ...

  9. nohup 启动命令

    start.sh #!/bin/bash nohup $PWD/node_exporter > /dev/null 2>&1 &

  10. Linux文件系统只读 解决方案:

    Linux系统Read-only file system,文件系统只读排查解决方案:文件系统只读机制:当文件系统自身的校验机制发现文件系统存在问题时,为避免文件系统受到进一步的损坏,系统会把文件系统设 ...