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中的 $() 这 ...
随机推荐
- shell读取文件参数
环境 csh 说明 通常我们需要使用使用shell脚本处理一些事务,每次调用shell都需要添加参数. 如果重复调用多次这个shell脚本,我们可以将参数存入指定文件,循环得到参数. shell脚本( ...
- C#扩展方法的理解
“扩展方法使您能够向现有类型“添加”方法,而无需创建新的派生类型.重新编译或以其他方式修改原始类型.” 这是msdn上说的,也就是你可以对String,Int,DataRow,DataTable等这些 ...
- Android入门2:从GridView控件使用到自定义Adapter
在日常手机app的使用中,出现频率最高的便是ListView和GridView.ListView的例子是微信主界面,而GridView的例子则是支付宝的主界面,不明白的小伙伴打开手机便一目了然.然而这 ...
- 《第一行代码》学习笔记2-Android开发特色
1.四大组件:活动(Activity),服务(Service),广播接收器(Broadcast Receiver),内容提供器(Content Provider). Activity:应用中看得到的东 ...
- cxf的使用及安全校验-01创建简单的服务端接口
最近因为项目的需要,研究了一下webservice的使用: 这里以cxf2.7.0为例,大致介绍一下,也用于备份啦(张立胜) 大致介绍一下项目的情况:项目有maven管理,webservice调用的方 ...
- hdu 1263 水果
Problem Description 夏天来了~~好开心啊,呵呵,好多好多水果~~ Joe经营着一个不大的水果店.他认为生存之道就是经营最受顾客欢迎的水果.现在他想要一份水果销售情况的明细表,这样J ...
- 编译XSIP过程中环境配置
昨天在编译XSip的过程中,有很多问题首先是出现了很多的error C1083. 然后到XSIP自己的文件夹中,也找不到对应的.h文件. 上网查阅后发现应该是缺少了对应的头文件的路径. 于是到可以 ...
- windows新的数据类型
1.简单重定义的 如LPCSTR只字符串,只是名字不同 2.句柄类型 H开头的句柄 3.结构体类型 如对话框 4.重新更名一方面为了32位->64位升级时带来的麻烦 typedef unsign ...
- jQuery读取json文件,实现省市区/县(国标)三级联动
最近做一个微信项目,需要用户填写所在的省市区/县,决定使用jQuery读取json文件来实现省市区/县的联动. 其实很简单,jQuery文档也有详细解释: 代码如下: html <table w ...
- mac下升级ruby环境版本
在ios开发中会经常使用到cocoapods来管理第三方框架,在安装cocoapods的时候会涉及到ruby环境,有时候会因为版本过低会导致安装失败,本文主要讲一下如何升级ruby环境 安装rvm,r ...