[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 ...
随机推荐
- Asp.net DropDownList 自定义样式(想怎么改就怎么改!)
最近在做一个asp.net的项目,需要对默认的dropdownlist样式进行美化,固有的dropdownlist的小箭头实在让人无法接受,于是开始在百度,google 上下求索,天不负有心人,终于找 ...
- Nginx使用的php-fpm的两种进程管理方式及优化(转)
php-fpm目前主要又两个分支,分别对应于php-5.2.x的版本和php-5.3.x的版本.在5.2.x的版本中,php-fpm.conf使用的是xml格式,而在新的5.3.x版本中,则是和php ...
- Java 异常 —— java.io.InvalidClassException: javax.xml.namespace.QName; local class incompatible
项目中有个 WebService 接口,调试时使用 Main 方法运行,别人的机器上都能运行,就笔者的机器出问题.他们说是RP的问题…… 异常信息: java.io.InvalidClassExcep ...
- Filtering Specific Columns with cut
Filtering Specific Columns with cut When working with text files, it can be useful to filter out s ...
- JavaScript事件属性绑定带参数的函数
JavaScript中在对事件进行绑定的时候,往往是element.onclick=event;这种形式,这样使用的话则会出现无法传参数.因此我们可以使用function(){}匿名函数将事件包含其中 ...
- java正则表达式去除html标签
当我们用ckeditor或其他一些在线文本编辑器的时候 内容里会有很多的标签 如下片段: <p><img alt="" src="/img/upload ...
- Vim 命令图解-Gvim使用笔记
Vim 命令图解-Gvim使用笔记... 参考的网址:http://blog.vgod.tw/wp-content/uploads/2014/08/vgod-vim-cheat-sheet-full. ...
- HTML知识点总结之div、section标签
div元素 div是块级元素,相当于一个容器,在语义上不代表任何特定类型的内容.主要用作大的框架布局,也就是说网页的骨架主要通过div来架设的,而网页的血肉则是有span.p或者ul等元素完成. se ...
- 深入了解GOT,PLT和动态链接
之前几篇介绍exploit的文章, 有提到return-to-plt的技术. 当时只简单介绍了 GOT和PLT表的基本作用和他们之间的关系, 所以今天就来详细分析下其具体的工作过程. 本文所用的依然是 ...
- 什么是Java Bean
刚才看java中的注解,老是说注解引入的是个java Bean,那我就要问了,什么是Java Bean? 知乎引用:https://www.zhihu.com/question/19773379下杨博 ...