springaop获取⽬标对象的⽅法对象(包括⽅法上的注解)
这两天在学习权限控制模块。以前看过传智播客黎活明⽼师的巴巴运动⽹视频教程,⾥⾯就讲到权限控制的解决⽅案,当时也只是看看视频,没有动⼿实践,虽说看过⼏遍,可是对于系统中的权限控制还是很迷茫,所以借着这次机会动⼿实践⼀下。
黎活明⽼师的巴巴运动⽹使⽤的框架是struts + spring + jpa,⼤致思路是使⽤⾃定义注解,在需要权限控制的⽅法前使⽤注解定义⽅法所需的权限,然后使⽤AOP拦截访问的⽅法,在执⾏⽬标对象前通过反射取得⽬标对象所需的权限,然后从当前session中取得登陆⽤户,遍历⽤户所拥有的权限,如果有权限则继续执⾏⽬标对象,如果没有权限则跳转到错误提⽰页⾯。巴巴运动⽹使⽤的struts + spring + jpa应⽤这种⽅案是有问题的,⼤致是spring aop⽆法拦截通过反射调⽤的⽅法,然后黎活明⽼师通过定制RequestProcessor 解决了这个问题。具体权限实现⽅案及对这种⽅案应⽤到巴巴运动⽹的缺陷的分析可以参看黎活明⽼师的巴巴运动⽹视频教程。
我在实践的过程中采⽤的是spring mvc + spring + hibernate 的框架,因此使⽤spring aop拦截是完全可以实现这种权限⽅案的。因此我在系统中定义了⼀个切⾯,申明了切⼊点及⼀个环绕通知。
以下是被拦截的⽅法申明,⽅法上有做权限控制的注解Permission
[java]
1. @Permission(module="user",operation="select")
2. @RequestMapping(value="/detail/{uid}",method=RequestMethod.GET)
3.    public String detail(@PathVariable int uid,Model model){
4.    ....
5. }
以下是AOP定义
[java]
1. @Pointcut("execution(java.lang.String s.controller..*.*(..))")
2. public void controller(){}
3. @Around("controller()")
4. public Object introcepter(ProceedingJoinPoint pjp) throws Throwable{
5.    System.out.println("拦截到了" + Signature().getName() +"⽅法...");
6. }
测试的时候⽇志中的确输出了被拦截的⽅法。可是Signature().getName()只是⽅法的名称,做权限控制需要得到⽅法上的注解Permission,那么就需要获取⽬标对象上的Method对象,通过Method对象的getAnnotation(Permission.class)⽅法获取注解。可是怎么怎么直接获取Method对象⽽不是⽅法名称呢?通过Signature()⽅法获取的Signature⽅法上好像没有直接getMethod()⽅法,于是去问度娘,得出如下的转换:
[java]
1. Signature signature = Signature();
2. MethodSignature methodSignature = (MethodSignature)signature;
3. Method targetMethod = Method();
这下终于满⾜需求了,于是迅速的写出了权限控制的代码:
[java]
1. public Object introcepter(ProceedingJoinPoint pjp) throws Throwable{
2.    System.out.println("拦截到了" + Signature().getName() +"⽅法...");
3.    Signature signature = Signature();
4.    MethodSignature methodSignature = (MethodSignature)signature;
5.    Method targetMethod = Method();
6.
7.    Class clazz = Class();
8.    if(clazz.isAnnotationPresent(Permission.class)){
9.        //获取⽅法上注解中表明的权限
10.        Permission permission = (Annotation(Permission.class);
11.        String module = dule();
12.        String operation = permission.operation();
13.        Privilege privilege = new Privilege(new PrivilegePK(module, operation));
14.        //获取当前⽤户拥有的权限
15.        User user = (HttpSession().getAttribute("employer");
16.        if(null != user){
17.            System.out.Username());
18.        }
19.        Set<Role> roles = Roles();
20.        for(Role role : roles){
21.            Privileges().contains(privilege)){
22.                //如果当前⽤户拥有的权限包含⽅法注解上的权限,则执⾏被拦截到的⽅法
23.                return pjp.proceed();
24.            }
25.        }
26.        //如果没有权限,抛出异常,由Spring框架捕获,跳转到错误页⾯
27.            throw new PermissionException();
28.        }
29.        return pjp.proceed();
30.    }
然后接着测试,可是在测试的过程中发现对于需要权限验证的detail⽅法,居然直接执⾏了,权限控制并没有起作⽤,于是debug调试,发现
[java]
1. clazz.isAnnotationPresent(Permission.class)
返回的是false,检查下,被拦截的⽅法上有Permission注解啊,⽽且注解的定义是⽅法级别的,作⽤范围是运⾏期啊,这个没错啊,重启了Eclipse,重新发布了,结果还是这样,郁闷啊。。。接着把⽣成的.class⽂件反编译,看到类上有Permission注解的呀,真是百思不得其解啊。。。实在想不明⽩,关灯睡觉了。
第⼆天,⼼⾥⽼是想着这个问题,牵挂着他,很难受啊!突然灵光⼀闪,会不会这个获取到得Method对象不是⽬标对象上的Method 对象,因为通过检查,⽬标类上的Method上是的确有那个注解的,除⾮拦截到的Method对象不是⽬标对象上的,是代理对象上的,⽽这个代理对象上的这个⽅法上没有Permission注解。然后去⽹上搜了下这⽅⾯的资料,原来spring aop使⽤cglib⽣成的代理是不会加上⽗类的⽅法上的注解的,也就是这边⽣成的代理类上的⽅法上没有Permission注解,然后也看到了⼀篇⽼外的⽂章,上⾯有所提到,但那时针对接⼝实现的代理,⼤意是通过接⼝⽣成的代理,通过
[java]
1. Signature signature = Signature();
2. MethodSignature methodSignature = (MethodSignature)signature;
3. Method targetMethod = Method();
这段代码获取的targetMethod对象是接⼝上的⽅法,他上⾯也是没有注解的(原⽂地址stackoverflow/questions/5714411/getting-the-java-lang-reflect-method-from-a-proceedingjoinpoint)。但是我这边不是通过接⼝⽣成代理的啊,是使⽤cglib通过继承⽬标对象⽣成代理的啊,难道这边获取的targetMethod对象是代理对象上的?于是就想证明。于是在代码中输出以下信息:
[java]
1. Signature signature = Signature();
2. MethodSignature methodSignature = (MethodSignature)signature;
3. Method targetMethod = Method();
4. System.out.println("classname:" + DeclaringClass().getName());
5. System.out.println("superclass:" + DeclaringClass().getSuperclass().getName());
6. System.out.println("isinterface:" + DeclaringClass().isInterface());
spring教学视频7. System.out.println("target:" + Target().getClass().getName());
8. System.out.println("proxy:" + This().getClass().getName());
9. System.out.println("method:" + Name());
结果如下:
[java]
1. classname:s.controller.LogonController
2. superclass:java.lang.Object
3. isinterface:false
4. target:s.controller.LogonController
5. proxy:s.controller.LogonController
EnhancerByCGLIB
f6998fd8
6. method:logon
其他的都可以理解,按理3来说classname输出的应该是代理对象,应该是s.controller.LogonController EnhancerByCGLIB
f6998fd8,⽽不是s.controller.LogonController,因为通过methodSignature对象获取的Method上没有Permission注解,所以通过getDeclaringClass获取定义该method的对象应该是代理对象⽽不是⽬标对象啊,这个没法解释啊,实验结果不能令⼈满意啊,不知道如何进⾏,希望有⼤神知道,或者以后想明⽩了再来完善。。。
⾄此,通过MethodSignature也⽆法直接获取⽬标对象的被拦截Method对象,那就只能⽤最笨的办法了,通过反射获取,刚才提到的⼀篇⽂章(stackoverflow/questions/5714411/getting-the-java-lang-reflect-method-from-a-proceedingjoinpoint)有说明,代码如下:[java]
1. Class[] parameterTypes = new Args().length];
2. Object[] args = Args();
3. for(int i=0; i<args.length; i++) {
4.    if(args[i] != null) {
5.        parameterTypes[i] = args[i].getClass();
6.    }else {
7.        parameterTypes[i] = null;
8.    }
9. }
10. String methodName = Signature().getName();
11. Method method = Signature().getDeclaringType().getMethod(methodName, parameterTypes);
这种⽅式能实现,但是⿇烦了点,简化了下,最终代码如下:
[java]
1. Method realMethod = Target().getClass().Name(), ParameterTypes());
此处获取到的realMethod就是⽬标对象上的,realMethod.isAnnotationPresent(Permission.class)返回的是true。对于这些东西还是要亲⾝实践的,视频要看,尤其是好的视频,要看不⽌⼀遍,但是看过了⼀定要动⼿实践,看看视频是简单的,完全不知道⾃⼰动⼿会遇到这些问题,不过遇到问题是好事,解决这些问题⾃⼰才能成长!

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。