代码如下:
public class UserRealm extends Pac4jRealm(){ @Override public AuthorizationInfo doGetAuthorizationInfo(PrincipalCollection principals) { // TODO Auto-generated method stub System.out.println("Onece"); Set<String> roles=new HashSet()<>; roles.add("admin"); SimpleAuthorizationInfo authorizationInfo = new SimpleAuthorizationInfo(); authorizationInfo.setRoles(roles); return authorizationInfo ; }
在controller层有一个方法
@RequestMapping("/hello2") @ResponseBody public String hello2() { SecurityUtils.getSubject().checkRoles("user"); return "success"; }
当执行chekRoles 执行了doGetAuthorizationInfo() 方法 但是要打印两个"Onece" 请问这是什么原因呢?
原因如下:
这个问题其实换一个标题问题更清晰:即是 Spring Aop注解执行两次 方法体执行一次!!!
在我的业务场景中 使用CGLIB动态代理 通过DynamicAdvisedInterceptor来处理 intercept是aop处理入口:
包含四个参数 第一个是代理方法 第二个是目标方法 第三个是方法参数 第四个是方法代理信息
@Override public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable { Object oldProxy = null; boolean setProxyContext = false; Class<?> targetClass = null; Object target = null; try { if (this.advised.exposeProxy) { // Make invocation available if necessary. oldProxy = AopContext.setCurrentProxy(proxy); setProxyContext = true; } // May be null. Get as late as possible to minimize the time we // "own" the target, in case it comes from a pool... target = getTarget(); if (target != null) { targetClass = target.getClass(); } List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass); Object retVal; // Check whether we only have one InvokerInterceptor: that is, // no real advice, but just reflective invocation of the target. if (chain.isEmpty() && Modifier.isPublic(method.getModifiers())) { // We can skip creating a MethodInvocation: just invoke the target directly. // Note that the final invoker must be an InvokerInterceptor, so we know // it does nothing but a reflective operation on the target, and no hot // swapping or fancy proxying. Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args); retVal = methodProxy.invoke(target, argsToUse); } else { // We need to create a method invocation... retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed(); } retVal = processReturnType(proxy, target, method, retVal); return retVal; } finally { if (target != null) { releaseTarget(target); } if (setProxyContext) { // Restore old proxy. AopContext.setCurrentProxy(oldProxy); } } }
通过打断点发现了bug出现的原因:
第一次执行时 代理方法和目标方法 是同一个!!!
第二次执行时 目标方法才是真正的方法体!!!
解决方法就是去除掉
当前我的pub-shiro项目依赖了 pub-datasource模块 pub-datasource模块里面使用了
@Aspect @EnableAspectJAutoProxy(exposeProxy=true,proxyTargetClass=true) @Component
使用了cglib代理 而后在shiroConfig中 注入了DefaultAdvisorAutoProxyCreator代理器 存在了两个代理器 这两个代理器的区别和为什么出现该问题的原因 我还在研究中 得出答案会来更新
本问题即是Spring Aop注解执行两次 方法体执行一次!!! 代理1 代理 代理2 而 代理2 代理 真正的方法体!!! 这也就是为什么shiro中 权限校验方法执行两次 而真正的方法体执行一次的原因。