您的位置:首页 > 编程语言 > Java开发

spring aop自动代理注解配置失效问题及原理机制整理总结

2016-02-05 16:13 831 查看

迭代中遇到的问题处理

问题:用自动代理注解配置拦截了PlanService类的方法testAopFace,方法testAopFace被PlanService类的方法query调用。http请求controller后调用了方法query,aop对方法testAopFace拦截失效。

 




问题原因:

aop配置拦截了方法testAopFace后,在项目运行启动时PlanService类会被CglibAopProxy生成代理对象planServiceEhancer。访问代理对象planServiceEhancer的每个方法(在例子中访问了代理对象的事务方法query)都会被拦截,进入到intercept方法处理:



根据被代理的目标类和调用的方法去查找拦截链:

List<Object> chain =this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);



说明注解配置的拦截处理并没有出现在拦截链里,因为当前的方法query并不是aop注解配置的拦截方法testAopFace。

经过一系列的拦截转发,在进入真正的query方法体时,会利用反射调用methodProxy的invoke来进入真正的query方法体,第一个参数实例是被代理的目标对象,而不是代理对象就造成了query方法体里执行testAopFace();调用了被代理的目标对象的方法而不是代理对象的方法导致了拦截失效。



 

参考自:http://lgbolgger.iteye.com/blog/2123895

spring aop自动代理注解实例

1.   在配置文件中添加<aop:aspectj-autoproxy/>配置
2.   创建一个Java文件,使用@Component 、@Aspect注解修饰该类
3.   创建一个方法,使用@Before、@After、@Around等进行修饰,在注解中写上切入点的表达式

1. import org.aspectj.lang.ProceedingJoinPoint;  
2. import org.aspectj.lang.annotation.After;  
3. import org.aspectj.lang.annotation.AfterThrowing;  
4. import org.aspectj.lang.annotation.Around;  
5. import org.aspectj.lang.annotation.Aspect;  
6. import org.aspectj.lang.annotation.Before;  
7. import org.springframework.stereotype.Component;  
8.   
9. /** 
10. * 基于注解的AOP日志示例 
11. * @author ZYWANG 2011-3-24 
12. */  
13.@Component  
14.@Aspect  
15.public class AopLog {  
16.      
17.    //方法执行前调用  
18.    @Before("execution (* com.zywang.services.impl.*.*(..))")  
19.    public void before() {  
20.        System.out.println("before");  
21.    }  
22.      
23.    //方法执行后调用  
24.    @After("execution (* com.zywang.services.impl.*.*(..))")  
25.    public void after() {  
26.        System.out.println("after");  
27.    }  
28.      
29.    //方法执行的前后调用  
30.    @Around("execution (* com.zywang.services.impl.*.*(..))")  
31.    public Object around(ProceedingJoinPoint point) throws Throwable{  
32.        System.out.println("begin around");  
33.        Object object = point.proceed();  
34.        System.out.println("end around");  
35.        return object;  
36.    }  
37.      
38.    //方法运行出现异常时调用  
39.    @AfterThrowing(pointcut = "execution (* com.zywang.services.impl.*.*(..))",throwing = "ex")  
40.    public void afterThrowing(Exception ex){  
41.        System.out.println("afterThrowing");  
42.        System.out.println(ex);  
43.    }  
44.}  

参考自:http://zywang.iteye.com/blog/974226

ProceedingJoinPoint 参考:http://it-dream-qq-com.iteye.com/blog/1395505

aop 及spring aop原理介绍

通俗简单地理解,aop的原理是:在代码编译或项目启动运行的时候生成代理对象,代理对象糅合了被拦截方法和切面逻辑方法的执行。

 

AOP 实现的关键就在于 AOP 框架自动创建的 AOP 代理,AOP 代理主要分为静态代理和动态代理两大类,静态代理为编译时增强,动态代理为运行时增强。

 

静态代理中AspectJ是一个面向切面的框架,它扩展了Java语言,定义了AOP语法所以它有一个专门的编译器用来生成遵守Java字节编码规范的Class文件。动态代理则不需要专门的编译器和扩展语法,而是在字节码级别进行运行时的增强生成代理对象,常见的有jdk和cglib动态代理。

 

spring aop的底层是使用jdk或cglib动态代理来实现运行时的增强。

可参考:http://blog.jobbole.com/28791/

 

spring aop源码--ProxyFactory分析

spring 提供的编程式aop实现,即通过 ProxyFactory类完成的。 另外AnnotationAwareAspectJAutoProxyCreator BeanNameAutoProxyCreator  DefaultAdvisorAutoProxyCreator 等自动aop代理创建器都是通过在其父类AbstractAutoProxyCreator中通过ProxyFactory 来实现aop逻辑的植入。所以理解ProxyFactory 的使用对理解spring aop 至关重要。

 

分析ProxyFactory 可以从两条线来分析:

1、代理对象的创建

2、method的调用以及拦截器(Advice)的织入。

 

运行时选择动态代理的方式:

DefaultAopProxyFactory类型的createAopProxy 方法:

1.  /** 默认的代理工厂实现 
2.  当 optimize =true或proxyTargetClass=ture 或 no proxy interfaces 指定,使用CGLIB 来生成代理,否则jdk代理 
3.  */  
4.  public class DefaultAopProxyFactory implements AopProxyFactory, Serializable {  
5.    
6.      /** 检查CGLIB2 相关类是否存在 */  
7.      private static final boolean cglibAvailable =  
8.              ClassUtils.isPresent("net.sf.cglib.proxy.Enhancer", DefaultAopProxyFactory.class.getClassLoader());  
9.    
10.   
11.     public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {  
12.         //optimize =true或proxyTargetClass=ture 或 no proxy interfaces 指定,使用CGLIB 来生成代理  
13.         //optimize 做优化,早期jdk的代理的生成相当慢,不过现在基本差异不大  
14.         if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {  
15.             Class targetClass = config.getTargetClass();  
16.             if (targetClass == null) { //被代理的对象不能为空  
17.                 throw new AopConfigException("TargetSource cannot determine target class: " +  
18.                         "Either an interface or a target is required for proxy creation.");  
19.             }  
20.             if (targetClass.isInterface()) { //虽然符合以上要求,但是如果代理对象是借口,那么继续使用jdk  
21.                 return new JdkDynamicAopProxy(config);  
22.             }  
23.             if (!cglibAvailable) {  
24.                 throw new AopConfigException(  
25.                         "Cannot proxy target class because CGLIB2 is not available. " +  
26.                         "Add CGLIB to the class path or specify proxy interfaces.");  
27.             }  
28.             return CglibProxyFactory.createCglibProxy(config); // Cglib2 代理  
29.         }  
30.         else {  
31.             return new JdkDynamicAopProxy(config); //jdk 代理  
32.         }  
33.     }  
34.   
35.     /** 
36.      * 判断是否有接口 
37.      */  
38.     private boolean hasNoUserSuppliedProxyInterfaces(AdvisedSupport config) {  
39.         Class[] interfaces = config.getProxiedInterfaces();  
40.         return (interfaces.length == 0 || (interfaces.length == 1 && SpringProxy.class.equals(interfaces[0])));  
41.     }  
42.   
43.     /** 
44.      * 创建Cglib2AopProxy 
45.      */  
46.     private static class CglibProxyFactory {  
47.         public static AopProxy createCglibProxy(AdvisedSupport advisedSupport) {  
48.             return new Cglib2AopProxy(advisedSupport);  
49.         }  
50.     }  
51. }  

 

分析详情请参考自:http://wangxinchun.iteye.com/blog/2079024

 

 

spring aop源码--JDK动态代理分析

Ø  /** 
Ø   * 基于JDK动态代理 的Aop代理实现 
Ø     通过JDK代理的方法调用 只对接口中的方法做拦截 
Ø     即使真实对象不是线程安全的,通过JdkDynamicAopProxy 产生的对象依然是线程安全的。 
Ø   */  
Ø  final class JdkDynamicAopProxy implements AopProxy, InvocationHandler, Serializable {  
Ø    
Ø      private static final long serialVersionUID = 5531744639992436476L;  
Ø    
Ø      /** We use a static Log to avoid serialization issues */  
Ø      private static Log logger = LogFactory.getLog(JdkDynamicAopProxy.class);  
Ø    
Ø      /** aop的配置信息(增强逻辑,真是对象,切点信息) */  
Ø      private final AdvisedSupport advised;  
Ø    
Ø      /** 代理接口是否有equals方法 
Ø       */  
Ø      private boolean equalsDefined;  
Ø    
Ø      /** 代理借口是否有hashCode方法 
Ø       */  
Ø      private boolean hashCodeDefined;  
Ø    
Ø    
Ø      public JdkDynamicAopProxy(AdvisedSupport config) throws AopConfigException {  
Ø          Assert.notNull(config, "AdvisedSupport must not be null");  
Ø          if (config.getAdvisors().length == 0 && config.getTargetSource() == AdvisedSupport.EMPTY_TARGET_SOURCE) {  
Ø              throw new AopConfigException("No advisors and no TargetSource specified");  
Ø          }  
Ø          this.advised = config;  
Ø      }  
Ø    
Ø    /** 获取代理*/  
Ø      public Object getProxy() {  
Ø          return getProxy(ClassUtils.getDefaultClassLoader());  
Ø      }  
Ø    
Ø  /**  至此方法结束,通过jdk生成代理对象已经完成*/  
Ø      public Object getProxy(ClassLoader classLoader) {  
Ø          if (logger.isDebugEnabled()) {  
Ø              logger.debug("Creating JDK dynamic proxy: target source is " + this.advised.getTargetSource());  
Ø          }  
Ø          Class[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised); //找到所有借口  
Ø          findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);  
Ø          return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this); //生成代理  
Ø      }  
Ø    
Ø      /** 
Ø       * 检查是否定义了hashCode  equals 方法  
Ø       */  
Ø      private void findDefinedEqualsAndHashCodeMethods(Class[] proxiedInterfaces) {  
Ø          for (Class proxiedInterface : proxiedInterfaces) {  
Ø              Method[] methods = proxiedInterface.getDeclaredMethods();  
Ø              for (Method method : methods) {  
Ø                  if (AopUtils.isEqualsMethod(method)) {  
Ø                      this.equalsDefined = true;  
Ø                  }  
Ø                  if (AopUtils.isHashCodeMethod(method)) {  
Ø                      this.hashCodeDefined = true;  
Ø                  }  
Ø                  if (this.equalsDefined && this.hashCodeDefined) {  
Ø                      return;  
Ø                  }  
Ø              }  
Ø          }  
Ø      }  
Ø    
Ø      /** 
Ø       * 这里是代理对象调用,也是jdk代理 实现的核心逻辑 
Ø       */  
Ø      public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  
Ø          MethodInvocation invocation;  
Ø          Object oldProxy = null;  
Ø          boolean setProxyContext = false;  
Ø    
Ø          TargetSource targetSource = this.advised.targetSource;  
Ø          Class targetClass = null;  
Ø          Object target = null;  
Ø    
Ø          try {  
Ø              if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) { //如果代理的是equlas 方法  
Ø                  return equals(args[0]);  
Ø              }  
Ø              if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) { //如果代理的是hashCode方法  
Ø                  // The target does not implement the hashCode() method itself.  
Ø                  return hashCode();  
Ø              }  
Ø              if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&  
Ø                      method.getDeclaringClass().isAssignableFrom(Advised.class)) {  
Ø                  // Service invocations on ProxyConfig with the proxy config...  
Ø                  return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);  
Ø              }  
Ø    
Ø              Object retVal;  
Ø    
Ø              if (this.advised.exposeProxy) { // 是否允许把当前代理放到AopContext中,这样可以保存在ThreadLocal中,在代码里使用AopContext.currentProxy() 获取当前代理  
Ø                  // Make invocation available if necessary.  
Ø                  oldProxy = AopContext.setCurrentProxy(proxy);  
Ø                  setProxyContext = true;  
Ø              }  
Ø    
Ø              // 获取目标对象以及Class类型  
Ø              target = targetSource.getTarget();  
Ø              if (target != null) {  
Ø                  targetClass = target.getClass();  
Ø              }  
Ø    
Ø              // 根据Method 和 targetClass 获取对应的拦截器(Advice增强封装) 这里是获取拦截逻辑的地方。(MethodMatcher 和 ClassFilter 在此处做匹配)  
Ø              List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);  
Ø    
Ø              // 如果没有配置,或者没有匹配到任何方法,那么直接调用当前实例的方法即可  
Ø              if (chain.isEmpty()) {  
Ø                  retVal = AopUtils.invokeJoinpointUsingReflection(target, method, args);  
Ø              }  
Ø              else {  
Ø                  // ReflectiveMethodInvocation 类封装了 增强和实例方法的调用  
Ø                  invocation = new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);  
Ø                  //aop的增强的植入 精华全在于此  
Ø                  retVal = invocation.proceed();  
Ø              }  
Ø    
Ø              // Massage return value if necessary.  
Ø              if (retVal != null && retVal == target && method.getReturnType().isInstance(proxy) &&  
Ø                      !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {  
Ø                  retVal = proxy;  
Ø              }  
Ø              return retVal;  
Ø          }  
Ø          finally {  
Ø              if (target != null && !targetSource.isStatic()) {  
Ø                  targetSource.releaseTarget(target);  
Ø              }  
Ø              if (setProxyContext) {  
Ø                  AopContext.setCurrentProxy(oldProxy); //恢复currentProxy  
Ø              }  
Ø          }  
Ø      }  
Ø    
Ø  }  

这是spring aop 中jdk的代理服务类,详细的分析流程请参考:http://rejoy.iteye.com/blog/1627405

 

 

 

spring aop源码--cglib动态代理分析

分析详情查看:http://lgbolgger.iteye.com/blog/2119379

JDK动态代理和CGLIB字节码生成的区别?
 * JDK动态代理只能对实现了接口的类生成代理,而不能针对类
 * CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法
   因为是继承,所以该类或方法最好不要声明成final 
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  spring aop