[bug]spring项目通过反射测试私有方法时,注入对象异常
背景
遇到问题:在进行Spring单元测试编写时,发现被测方法是一个私有方法,无法直接通过注入对象调用
解决思路:首先想到通过反射获取该私有方法的访问权限,并传入注入对象,最终调用对象的私有方法。
出现的异常
运行时抛出空指针异常

定位问题
- 点击异常代码行打上断点,debug调试

- 通过查看变量值发现roleMapper为空,从而导致空指针
- 而roleMapper是传入this对象的属性,因此,问题来自传入的对象
分析问题
- 通过分析this对象,可以发现它是一个被Cglib代理后的实例,由此可知,该类方法上必定有@Transactional事务注解或AOP注解修饰,从而被SpringCglib代理

- 查看cglib原理:
动态生成一个要代理类的子类,子类重写要代理的类的所有不是final的方法。在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。它比使用java反射的JDK动态代理要快。
其中重要的一点,代理类是被代理类的子类,回想关于Java中的继承,有一条很重要的特性就是:
- 子类拥有父类非 private 的属性、方法。
- 此时,尝试修改私有方法变成public,发现this对象恢复正常,由此锁定代理类和私有方法出现问题

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


- 由此可知,此处注入的cglib代理对象中不包含private方法!
- 那为啥同样传入的代理对象,调用public方法就成功,而调用private方法就失败呢?
- 如果是私有方法,那么在代理类中,不会包含这个方法。此时通过Method.invoke()来调用目标方法,传入的实例对象是userController的代理类,而这个代理类中的userService为NULL,所以,执行的时候,才会看到userService没有注入,导致空指针异常。
- 如果是公共方法,在代理类中,就有它的子类实现,则会先调用到代理类的拦截器MethodInterceptor。拦截器负责链式调用AOP方法和目标方法。在拦截器执行过程中,又调用了方法。但不同的是,此时传入的实例对象并不是代理类,而是代理类的目标对象。
结论:可以发现代理类正常情况下,执行到原方法时是通过代理的目标对象(即原始对象)来执行,而当代理类发现没有代理对应的private方法时,则直接通过代理对象(即上文的this)执行目标方法。
解决方法
既然我们需要的是只原始对象执行私有方法,只要通过代理类获取原始的目标对象即可。
// 由于cglib类是通过继承代理,无法代理私有方法,因此无法通过原始对象执行方法
if (AopUtils.isCglibProxy(menuService)) {
// 如果是cglib代理对象,则转为原始对象
menuService = (MenuServiceImpl)AopProxyUtils.getSingletonTarget(menuService);
}
此时得到的对象即为原始对象,bug成功消灭!

参考文章:
[bug]spring项目通过反射测试私有方法时,注入对象异常的更多相关文章
- JUnit 3.8 通过反射测试私有方法
测试私有(private)的方法有两种: 1)把目标类的私有方法(修饰符:private)修改为(public),不推荐,因为修改了源程序不佳 2)通过反射 (推荐) 代码演示: 目标程序 Priva ...
- 无所不能的PowerMock,mock私有方法,静态方法,测试私有方法,final类
1.为什么要用mock 我的一本书的解释: (1)创建所需的DB数据可能需要很长时间,如:调用别的接口,模拟很多数据 (2)调用第三方API接口,测试很慢, (3)编写满足所有外部依赖的测试可能很复杂 ...
- Android Studio 重写方法时参数命名异常
Android Studio 重写方法时参数命名异常 Android Studio 重写方法时参数名称乱掉可以通过下载相应源码解决
- 使用PowerMockito和Mockito进行模拟测试,包括静态方法测试,私有方法测试等,以及方法执行的坑或者模拟不成功解决
依赖:这个很重要,不同版本用法也有点区别: <dependency> <groupId>org.mockito</groupId> <artifactId&g ...
- java中用反射访问私有方法和私有成员[转]
转自: http://zhouyangchenrui.iteye.com/blog/470521 java的反射可以绕过访问权限,访问到类的私有方法和成员.可能这点会引起安全性的讨论.反射的使用帮助解 ...
- Java 反射获取私有方法
通常我们创建一个类时,它的私有方法在类外是不可见的,但是可以通过反射机制来获取调用.具体的反射机制的介绍大家自己百度. 所以反射可能会破坏我们的单例模式,当然解决方案也是有的,就是做个标记记录次数,第 ...
- c# 通过反射获取私有方法
class Program { static void Main(string[] args) { //通过反射来调私有的成员 Type type = typeof(Person); //Bindin ...
- Java开发笔记(八十)利用反射技术操作私有方法
前面介绍了如何利用反射技术读写私有属性,不单是私有属性,就连私有方法也能通过反射技术来调用.为了演示反射的逆天功能,首先给Chicken鸡类增加下列几个私有方法,简单起见弄来了set***/get** ...
- java反射调用私有方法和修改私有属性
//调用私有方法package com.java.test; public class PrivateMethod { private String sayHello(String name) { r ...
随机推荐
- OVERLAPPED 结构
typedef struct _OVERLAPPED { ULONG_PTR Internal; ULONG_PTR InternalHigh; union { struct { DWORD Offs ...
- 解决iOS上网页滑动不流畅问题
body { overflow:auto; /* 用于 android4+,或其他设备 */ -webkit-overflow-scrolling:touch; /* 用于 ios5+ */ }说明: ...
- 学会这十招,轻松搜索github优质项目
大家好,我是青空. 今天我想给大家分享一下使用 GitHub 的一些心得体会.之前我是在分享 GitHub上的一些开源项目,通过这段时间的收集工作,我积累了一些相关的经验在这里分享给大家. 我做了一个 ...
- charles抓取https设置
第一步:打开charles,查看电脑ip,手机设置代理(需要手机和电脑在同一网络) 手机下载证书不要用自带的下,会失败 1.查看电脑ip 2.手机设置代理,修改网络,保存 3.手机访问"看图 ...
- WanJetpack项目:用Jetpack实现玩Android,追求最官方的实现方式
项目简介 玩Android demo.用Jetpack MVVM开发架构.单Activity多Fragment项目设计,项目结构清晰,代码简洁优雅,追求最官方的实现方式.用到以下知识点: LiveDa ...
- CVE-2018-12613总结
1.漏洞基础介绍 1.1漏洞背景 phpMyAdmin 是一个以PHP为基础,以Web-Base方式架构在网站主机上的MySQL的数据库管理工具,让管理者可用Web接口管理MySQL数据库.借由此We ...
- 两年Android开发三面上岸腾讯,这些核心知识点建议收藏
概述 感觉毕业后时间过得真快啊,从 19 年 7 月本科毕业入职后,到现在快两年了,前段时间金三银四期间想着找一个新的工作,前前后后花了一个多月的时间复习以及面试,面试好几家大厂,最后选择了腾讯.也祝 ...
- Flutter 中的动画
Flutter 中动画的创建有很多种, 需要根据具体的需求选择不同的动画.如果只是简单的布局等的动画直接使用最简单的隐式动画就可以了,因为隐式动画是由框架控制的,所以仅仅只需要更改变需要变化属性就可以 ...
- MVVM窗体show的弹窗事件
RestMatCutWin restMatCutWindow;//定义一个窗体的全局变量 private void RestMatCutWinExecute() { if (restMatCutWin ...
- JavaSE-方法
何谓方法 比如之前用到的 System.out.println(); System为一个类:out为这个类的一个输出对象:println()为这个对象的方法 调用System类中out输出对象的pri ...