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中的 $() 这 ...
随机推荐
- iphone开发 IOS 组织架构图
转载自 :http://blog.csdn.net/mashi321323/article/details/18267719 登录|注册 mashi321323的专栏 目录视图 ...
- CodeSmith使用总结--创建一个基础模板
问:为什么要用CodeSmith? 答曰:因为我懒的写. Codesmith是一款非常不错的懒人工具,我也经常会用到,因为它在“重复代码”方面能够节省我们很多时间,并且解除了我们重复繁琐并且乏味的“码 ...
- Geodatabase - 删除要素
//删除要素类. //例如:workspacePath=@"G:\doc\gis\1.400\data\pdb.mdb", featureClassPath="res2_ ...
- trailingZeroes
Given an integer n, return the number of trailing zeroes in n!. 给一个数字n,返回它n!数字后面有多少个0. public class ...
- VC获取当前程序运行路径
/***************************************************/ /* 函数: 获取当前程序运行的路径 /* 返回: 当前程序运行路径 C:\AAA\BBB\ ...
- web 安全 初探 (正在更新)
1.web应用程序所采用的防卫机制的几个核心构成:1.处理用户对应用程序的数据和功能的访问,以防止用户未经授权访问.2.处理用户的输入,以防止恶意的输入导致未预期的行为.3.处理攻击,以确保应用程序在 ...
- jquery 1.9 之后toggle不能用的问题
今天用到toggle这个方法,发现不是自己想要的效果,之前有用过好多次,一直都没有问题. 网上查了原因,才知道是版本的问题,jquery1.9之后toggle取消了.那么如果想要继续用toggle的这 ...
- 【1】Laravel5.1 安装
1.安装composer http://www.phpcomposer.com/ 这个是中文网址里边有教程,但是由于被墙的缘故,可以通过下边这个链接下载Windows安装包 http://docs.p ...
- Python-----格式化字符
摘要: Python中 %s . %r Python中也有类似于C中的 printf()格式输出,使用 % 运算符,格式: 格式标记字符串 % 要输出的值组 右边的”值组“若有两个及以上的值则需要用小 ...
- R语言画正弦曲线
正弦曲线一个周期是2π,我们要先生成x的取值范围. 可以用seq函数生成一个等差序列,步进为0.01 x=seq( 0, 2*pi, 0.01 ) pi表示π y=sin(x) plot(x, ...