spring aop获取目标对象的方法对象(包括方法上的注解)
这两天在学习权限控制模块。以前看过传智播客黎活明老师的巴巴运动网视频教程,里面就讲到权限控制的解决方案,当时也只是看看视频,没有动手实践,虽说看过几遍,可是对于系统中的权限控制还是很迷茫,所以借着这次机会动手实践一下。
黎活明老师的巴巴运动网使用的框架是struts + spring + jpa,大致思路是使用自定义注解,在需要权限控制的方法前使用注解定义方法所需的权限,然后使用AOP拦截访问的方法,在执行目标对象前通过反射取得目标对象所需的权限,然后从当前session中取得登陆用户,遍历用户所拥有的权限,如果有权限则继续执行目标对象,如果没有权限则跳转到错误提示页面。巴巴运动网使用的struts + spring + jpa应用这种方案是有问题的,大致是spring aop无法拦截通过反射调用的方法,然后黎活明老师通过定制RequestProcessor 解决了这个问题。具体权限实现方案及对这种方案应用到巴巴运动网的缺陷的分析可以参看黎活明老师的巴巴运动网视频教程。
我在实践的过程中采用的是spring mvc + spring + hibernate 的框架,因此使用spring aop拦截是完全可以实现这种权限方案的。因此我在系统中定义了一个切面,申明了切入点及一个环绕通知。
以下是被拦截的方法申明,方法上有做权限控制的注解Permission
- @Permission(module="user",operation="select")
- @RequestMapping(value="/detail/{uid}",method=RequestMethod.GET)
- public String detail(@PathVariable int uid,Model model){
- ....
- }
以下是AOP定义
- @Pointcut("execution(java.lang.String com.jiangnan.cms.controller..*.*(..))")
- public void controller(){}
- @Around("controller()")
- public Object introcepter(ProceedingJoinPoint pjp) throws Throwable{
- System.out.println("拦截到了" + pjp.getSignature().getName() +"方法...");
- }
测试的时候日志中的确输出了被拦截的方法。可是pjp.getSignature().getName()只是方法的名称,做权限控制需要得到方法上的注解Permission,那么就需要获取目标对象上的Method对象,通过Method对象的getAnnotation(Permission.class)方法获取注解。可是怎么怎么直接获取Method对象而不是方法名称呢?通过pjp.getSignature()方法获取的Signature方法上好像没有直接getMethod()方法,于是去问度娘,得出如下的转换:
- Signature signature = pjp.getSignature();
- MethodSignature methodSignature = (MethodSignature)signature;
- Method targetMethod = methodSignature.getMethod();
这下终于满足需求了,于是迅速的写出了权限控制的代码:
- public Object introcepter(ProceedingJoinPoint pjp) throws Throwable{
- System.out.println("拦截到了" + pjp.getSignature().getName() +"方法...");
- Signature signature = pjp.getSignature();
- MethodSignature methodSignature = (MethodSignature)signature;
- Method targetMethod = methodSignature.getMethod();
- Class clazz = targetMethod.getClass();
- if(clazz.isAnnotationPresent(Permission.class)){
- //获取方法上注解中表明的权限
- Permission permission = (Permission)clazz.getAnnotation(Permission.class);
- String module = permission.module();
- String operation = permission.operation();
- Privilege privilege = new Privilege(new PrivilegePK(module, operation));
- //获取当前用户拥有的权限
- User user = (User)ContextUtils.getHttpSession().getAttribute("employer");
- if(null != user){
- System.out.println(user.getUsername());
- }
- Set<Role> roles = user.getRoles();
- for(Role role : roles){
- if(role.getPrivileges().contains(privilege)){
- //如果当前用户拥有的权限包含方法注解上的权限,则执行被拦截到的方法
- return pjp.proceed();
- }
- }
- //如果没有权限,抛出异常,由Spring框架捕获,跳转到错误页面
- throw new PermissionException();
- }
- return pjp.proceed();
- }
然后接着测试,可是在测试的过程中发现对于需要权限验证的detail方法,居然直接执行了,权限控制并没有起作用,于是debug调试,发现
- clazz.isAnnotationPresent(Permission.class)
返回的是false,检查下,被拦截的方法上有Permission注解啊,而且注解的定义是方法级别的,作用范围是运行期啊,这个没错啊,重启了Eclipse,重新发布了,结果还是这样,郁闷啊。。。接着把生成的.class文件反编译,看到类上有Permission注解的呀,真是百思不得其解啊。。。实在想不明白,关灯睡觉了。
第二天,心里老是想着这个问题,牵挂着他,很难受啊!突然灵光一闪,会不会这个获取到得Method对象不是目标对象上的Method对象,因为通过检查,目标类上的Method上是的确有那个注解的,除非拦截到的Method对象不是目标对象上的,是代理对象上的,而这个代理对象上的这个方法上没有Permission注解。然后去网上搜了下这方面的资料,原来spring aop使用cglib生成的代理是不会加上父类的方法上的注解的,也就是这边生成的代理类上的方法上没有Permission注解,然后也看到了一篇老外的文章,上面有所提到,但那时针对接口实现的代理,大意是通过接口生成的代理,通过
- Signature signature = pjp.getSignature();
- MethodSignature methodSignature = (MethodSignature)signature;
- Method targetMethod = methodSignature.getMethod();
这段代码获取的targetMethod对象是接口上的方法,他上面也是没有注解的(原文地址http://stackoverflow.com/questions/5714411/getting-the-java-lang-reflect-method-from-a-proceedingjoinpoint)。但是我这边不是通过接口生成代理的啊,是使用cglib通过继承目标对象生成代理的啊,难道这边获取的targetMethod对象是代理对象上的?于是就想证明。于是在代码中输出以下信息:
- Signature signature = pjp.getSignature();
- MethodSignature methodSignature = (MethodSignature)signature;
- Method targetMethod = methodSignature.getMethod();
- System.out.println("classname:" + targetMethod.getDeclaringClass().getName());
- System.out.println("superclass:" + targetMethod.getDeclaringClass().getSuperclass().getName());
- System.out.println("isinterface:" + targetMethod.getDeclaringClass().isInterface());
- System.out.println("target:" + pjp.getTarget().getClass().getName());
- System.out.println("proxy:" + pjp.getThis().getClass().getName());
- System.out.println("method:" + targetMethod.getName());
结果如下:
- classname:com.jiangnan.cms.controller.LogonController
- superclass:java.lang.Object
- isinterface:false
- target:com.jiangnan.cms.controller.LogonController
- proxy:com.jiangnan.cms.controller.LogonController
EnhancerByCGLIB
f6998fd8
- method:logon
其他的都可以理解,按理3来说classname输出的应该是代理对象,应该是com.jiangnan.cms.controller.LogonController
f6998fd8,而不是com.jiangnan.cms.controller.LogonController,因为通过methodSignature对象获取的Method上没有Permission注解,所以通过getDeclaringClass获取定义该method的对象应该是代理对象而不是目标对象啊,这个没法解释啊,实验结果不能令人满意啊,不知道如何进行,希望有大神知道,或者以后想明白了再来完善。。。
至此,通过MethodSignature也无法直接获取目标对象的被拦截Method对象,那就只能用最笨的办法了,通过反射获取,刚才提到的一篇文章(http://stackoverflow.com/questions/5714411/getting-the-java-lang-reflect-method-from-a-proceedingjoinpoint)有说明,代码如下:
- Class[] parameterTypes = new Class[pjp.getArgs().length];
- Object[] args = pjp.getArgs();
- for(int i=0; i<args.length; i++) {
- if(args[i] != null) {
- parameterTypes[i] = args[i].getClass();
- }else {
- parameterTypes[i] = null;
- }
- }
- String methodName = pjp.getSignature().getName();
- Method method = pjp.getSignature().getDeclaringType().getMethod(methodName, parameterTypes);
这种方式能实现,但是麻烦了点,简化了下,最终代码如下:
- Method realMethod = pjp.getTarget().getClass().getDeclaredMethod(signature.getName(), targetMethod.getParameterTypes());
此处获取到的realMethod就是目标对象上的,realMethod.isAnnotationPresent(Permission.class)返回的是true。 对于这些东西还是要亲身实践的,视频要看,尤其是好的视频,要看不止一遍,但是看过了一定要动手实践,看看视频是简单的,完全不知道自己动手会遇到这些问题,不过遇到问题是好事,解决这些问题自己才能成长!
spring aop获取目标对象的方法对象(包括方法上的注解)的更多相关文章
- Spring动态获取已注入的对象的方法
1.根据类获取对象 @Autowired ApplicationContext context; GenericMapper<T,String> dao=(GenericMapper< ...
- spring aop 获取request、response对象
在网上看到有不少人说如下方式获取: 1.在web.xml中添加监听 <listener> <listener-class> org. ...
- Spring AOP获取不了增强类(额外方法)或无法通过getBean()获取对象
Spring AOP获取不了增强类(额外方法)和无法通过getBean()获取对象 今天在学习AOP发现一个小问题 Spring AOP获取不了额外方法,左思右想发现是接口上出了问题 先上代码 获取不 ...
- Spring中获取被代理的对象
目录 Spring中获取被代理的对象 获取Spring被代理对象什么时候可能会用到? Spring中获取被代理的对象 Spring中获取被代理的对象 ### 获取Spring被代理对象的JAVA工具类 ...
- 获取$(this)子节点对象的方法
获取$(this)子节点对象的方法: 1.children()方法: children() 方法返回被选元素的所有直接子元素. 该方法只会向下一级对 DOM 树进行遍历. 2.find()方法: fi ...
- spring aop pointcut 切入点是类的公共方法(私有方法不行),还是接口的方法
spring aop pointcut 切入点是类的公共方法(私有方法不行),还是接口的方法 类的公共方法可以,但是私有方法不行 测试一下接口的方法是否能够捕捉到
- JQuery this和$(this)的区别及获取$(this)子元素对象的方法
1.JQuery this和$(this)的区别 相信很多刚接触JQuery的人,很多都会对$(this)和this的区别模糊不清,那么这两者有什么区别呢? 首先来看看JQuery中的 $() 这 ...
- Spring aop:decare-parent 为类增加新的方法
Spring aop:decare-parent 为类增加新的方法: 使用XML配置的方式: XML: <?xml version="1.0" encoding=" ...
- 转: JQuery this和$(this)的区别及获取$(this)子元素对象的方法
1.JQuery this和$(this)的区别 相信很多刚接触JQuery的人,很多都会对$(this)和this的区别模糊不清,那么这两者有什么区别呢? 首先来看看JQuery中的 $() 这 ...
随机推荐
- (转)一小时搞定DIV+CSS布局-固定页面开度布局
本文讲解使用DIV+CSS布局最基本的内容,读完本文你讲会使用DIV+CSS进行简单的页面布局. 转载请标明:http://www.kwstu.com/ArticleView/divcss_20139 ...
- hive函数总结-字符串函数
hive 查看函数: show functions; parse_url: parse_url(url, partToExtract[, key]) - extracts a part from a ...
- silverlight 生成图表 WCF 解析XML代码.svc.cs 文件
silverlight 调用wcf 文件代码 private ListItem AnalyzeXML(string XMLCode, string Reportdate, string ChartNa ...
- 慕课linux学习笔记(四)常用命令(1)
Root 表示当前登录用户 Localhost 主机名 ~ 当前所在位置(~表示/root) # 超级用户 $ 普通用户 命令 1.pwd 显示当前所在位置 2.ls 查询目录中的内容 -a 显示所有 ...
- setInterval()、clearInterval()、setTimeout()和clearTimeout()js计数器方法
原文地址:http://caibaojian.com/setinterval-settimeout.html window.setInterval()方法 介绍 周期性地调用一个函数(function ...
- Jquery 中 $('obj').attr('checked',true)失效的几种解决方案
转载自:搜狐博客 大拙无为 1.$('obj').prop('checked',true) 2. $(':checkbox').each(function(){ this.checked=true; ...
- pubwin 客户端会员无法自助结账的排查方法
客户端会员无法自助结账按以下方法排查:1,看客户端能不能打开web https 后台,打不开的话,在服务端打上2048证书补丁(按下面帖子操作)http://bbs.pubwin.com.cn/for ...
- NAS4Free 安装配置(五)配置SMB
配置SMB 现在我们有2块存储设备,一块做下载盘,一块做数据盘 为了便于管理和扩展,我们分别在两块盘上建文件夹和Dataset 对于download盘,因为是UFS,所以只能建文件夹 我们把整个盘共享 ...
- 什么是REST架构 - z
什么是REST架构 - z REST架构风格是全新的针对Web应用的开发风格,是当今世界最成功的互联网超媒体分布式系统架构,它使得人们真正理解了Http协议本来面貌.随着 REST架构成为主流技术 ...
- C# 序列化过程中的已知类型(Known Type)
WCF下的序列化与反序列化解决的是数据在两种状态之间的相互转化:托管类型对象和XML.由于类型定义了对象的数据结构,所以无论对于序列化还是反序列化,都必须事先确定对象的类型.如果被序列化对象或者被反序 ...