zoukankan      html  css  js  c++  java
  • Spring AOP 详解

    AOP使用场景

    AOP用来封装横切关注点,具体可以在下面的场景中使用:

    Authentication 权限

    Caching 缓存

    Context passing 内容传递

    Error handling 错误处理

    Lazy loading 懒加载

    Debugging  调试

    logging, tracing, profiling and monitoring 记录跟踪 优化 校准

    Performance optimization 性能优化

    Persistence  持久化

    Resource pooling 资源池

    Synchronization 同步

    Transactions 事务

    Spring提供了两种方式来生成代理对象: JDKProxy和Cglib

    JDK动态代理(实现接口)

    关于JDK的动态代理,最为人熟知要可能要数Spring AOP的实现,默认情况下,Spring AOP的实现对于接口来说就是使用的JDK的动态代理来实现的,而对于类的代理使用CGLIB来实现

    JDK的动态代理,就是在程序运行的过程中,根据被代理的接口来动态生成代理类的class文件,并加载运行的过程。

    首先来看一下如何使用JDK动态代理。JDK提供了java.lang.reflect.Proxy类来实现动态代理的,可通过它的newProxyInstance来获得代理实现类。同时对于代理的接口的实际处理,是一个java.lang.reflect.InvocationHandler,它提供了一个invoke方法供实现者提供相应的代理逻辑的实现。可以对实际的实现进行一些特殊的处理,像Spring AOP中的各种advice。

    下面来看看如何使用。

    public interface HelloWorld {
    
        void sayHello(String name);
    
    }
    
    
    public class HelloWorldImpl implements HelloWorld {
        @Override
        public void sayHello(String name) {
            System.out.println("Hello " + name);
        }
    }
    
    public class CustomInvocationHandler implements InvocationHandler {
        private Object target;
    
        public CustomInvocationHandler(Object target) {
            this.target = target;
        }
    
        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("Before invocation");
            Object retVal = method.invoke(target, args);
            System.out.println("After invocation");
            return retVal;
        }
    }
    
    
    public class ProxyTest {
    
        public static void main(String[] args) throws Exception {
            System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
    
            CustomInvocationHandler handler = new CustomInvocationHandler(new HelloWorldImpl());
            HelloWorld proxy = (HelloWorld) Proxy.newProxyInstance(
                    ProxyTest.class.getClassLoader(),
                    new Class[]{HelloWorld.class},
                    handler);
            proxy.sayHello("yeah");
        }
    
    }

    它是如何生成代理类?(JDK源码)

        public static Object newProxyInstance(ClassLoader loader,
                                              Class<?>[] interfaces,
                                              InvocationHandler h)
            throws IllegalArgumentException {
            if (h == null) {
                throw new NullPointerException();
            }
    
            final Class<?>[] intfs = interfaces.clone();
            final SecurityManager sm = System.getSecurityManager();
            if (sm != null) {
                checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
            }
            // 这里是生成class的地方
            Class<?> cl = getProxyClass0(loader, intfs);
            // 使用我们实现的InvocationHandler作为参数调用构造方法来获得代理类的实例
            try {
                final Constructor<?> cons = cl.getConstructor(constructorParams);
                final InvocationHandler ih = h;
                if (sm != null && ProxyAccessHelper.needsNewInstanceCheck(cl)) {
                    return AccessController.doPrivileged(new PrivilegedAction<Object>() {
                        public Object run() {
                            return newInstance(cons, ih);
                        }
                    });
                } else {
                    return newInstance(cons, ih);
                }
            } catch (NoSuchMethodException e) {
                throw new InternalError(e.toString());
            }
        }

    其中newInstance只是调用Constructor.newInstance来构造相应的代理类实例,这里重点是看getProxyClass0这个方法的实现:

        private static Class<?> getProxyClass0(ClassLoader loader,
                                               Class<?>... interfaces) {
            // 代理的接口数量不能超过65535(没有这种变态吧)
            if (interfaces.length > 65535) {
                throw new IllegalArgumentException("interface limit exceeded");
            }
            // JDK对代理进行了缓存,如果已经存在相应的代理类,则直接返回,否则才会通过ProxyClassFactory来创建代理
            return proxyClassCache.get(loader, interfaces);
        }

    其中代理缓存是使用WeakCache实现的,如下

        private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
            proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

    ProxyClassFactory是Proxy的一个静态内部类,实现了WeakCache的内部接口BiFunction的apply方法:

        private static final class ProxyClassFactory
            implements BiFunction<ClassLoader, Class<?>[], Class<?>> {
            // 所有代理类名字的前缀
            private static final String proxyClassNamePrefix = "$Proxy";
    
            // 用于生成代理类名字的计数器
            private static final AtomicLong nextUniqueNumber = new AtomicLong();
    
            @Override
            public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
                // 省略验证代理接口的代码……
    
                String proxyPkg = null;     // 生成的代理类的包名
                // 对于非公共接口,代理类的包名与接口的相同
                for (Class<?> intf : interfaces) {
                    int flags = intf.getModifiers();
                    if (!Modifier.isPublic(flags)) {
                        String name = intf.getName();
                        int n = name.lastIndexOf('.');
                        String pkg = ((n == -1) ? "" : name.substring(0, n + 1));
                        if (proxyPkg == null) {
                            proxyPkg = pkg;
                        } else if (!pkg.equals(proxyPkg)) {
                            throw new IllegalArgumentException(
                                "non-public interfaces from different packages");
                        }
                    }
                }
    
                // 对于公共接口的包名,默认为com.sun.proxy
                if (proxyPkg == null) {
                    proxyPkg = ReflectUtil.PROXY_PACKAGE + ".";
                }
    
                // 获取计数
                long num = nextUniqueNumber.getAndIncrement();
                // 默认情况下,代理类的完全限定名为:com.sun.proxy.$Proxy0,com.sun.proxy.$Proxy1……依次递增
                String proxyName = proxyPkg + proxyClassNamePrefix + num;
    
                // 这里才是真正的生成代理类的字节码的地方
                byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                    proxyName, interfaces);
                try {
                    // 根据二进制字节码返回相应的Class实例
                    return defineClass0(loader, proxyName,
                                        proxyClassFile, 0, proxyClassFile.length);
                } catch (ClassFormatError e) {
                    throw new IllegalArgumentException(e.toString());
                }
            }
        }

    ProxyGenerator是sun.misc包中的类,它没有开源,但是可以反编译来一探究竟:

        public static byte[] generateProxyClass(final String var0, Class[] var1) {
            ProxyGenerator var2 = new ProxyGenerator(var0, var1);
            final byte[] var3 = var2.generateClassFile();
            // 这里根据参数配置,决定是否把生成的字节码(.class文件)保存到本地磁盘,我们可以通过把相应的class文件保存到本地,再反编译来看看具体的实现,这样更直观
            if(saveGeneratedFiles) {
                AccessController.doPrivileged(new PrivilegedAction() {
                    public Void run() {
                        try {
                            FileOutputStream var1 = new FileOutputStream(ProxyGenerator.dotToSlash(var0) + ".class");
                            var1.write(var3);
                            var1.close();
                            return null;
                        } catch (IOException var2) {
                            throw new InternalError("I/O exception saving generated file: " + var2);
                        }
                    }
                });
            }
            return var3;
        }

    saveGeneratedFiles这个属性的值从哪里来呢:

        private static final boolean saveGeneratedFiles = ((Boolean)AccessController.doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"))).booleanValue();

    GetBooleanAction实际上是调用Boolean.getBoolean(propName)来获得的,而Boolean.getBoolean(propName)调用了System.getProperty(name),所以我们可以设置sun.misc.ProxyGenerator.saveGeneratedFiles这个系统属性为true来把生成的class保存到本地文件来查看。

    这里要注意,当把这个属性设置为true时,生成的class文件及其所在的路径都需要提前创建,否则会抛出FileNotFoundException异常。

    即我们要在运行当前main方法的路径下创建com/sun/proxy目录,并创建一个$Proxy0.class文件,才能够正常运行并保存class文件内容。

    反编译$Proxy0.class文件,如下所示:

    package com.sun.proxy;
    
    import com.mikan.proxy.HelloWorld;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    
    public final class $Proxy0 extends Proxy implements HelloWorld {
      private static Method m1;
      private static Method m3;
      private static Method m0;
      private static Method m2;
    
      public $Proxy0(InvocationHandler paramInvocationHandler) {
        super(paramInvocationHandler);
      }
    
      public final boolean equals(Object paramObject) {
        try {
          return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
        }
        catch (Error|RuntimeException localError) {
          throw localError;
        }
        catch (Throwable localThrowable) {
          throw new UndeclaredThrowableException(localThrowable);
        }
      }
    
      public final void sayHello(String paramString) {
        try {
          this.h.invoke(this, m3, new Object[] { paramString });
          return;
        }
        catch (Error|RuntimeException localError) {
          throw localError;
        }
        catch (Throwable localThrowable) {
          throw new UndeclaredThrowableException(localThrowable);
        }
      }
    
      public final int hashCode() {
        try {
          return ((Integer)this.h.invoke(this, m0, null)).intValue();
        }
        catch (Error|RuntimeException localError) {
          throw localError;
        }
        catch (Throwable localThrowable) {
          throw new UndeclaredThrowableException(localThrowable);
        }
      }
    
      public final String toString() {
        try {
          return (String)this.h.invoke(this, m2, null);
        }
        catch (Error|RuntimeException localError) {
          throw localError;
        }
        catch (Throwable localThrowable) {
          throw new UndeclaredThrowableException(localThrowable);
        }
      }
    
      static {
        try {
          m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") });
          m3 = Class.forName("com.mikan.proxy.HelloWorld").getMethod("sayHello", new Class[] { Class.forName("java.lang.String") });
          m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
          m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
          return;
        }
        catch (NoSuchMethodException localNoSuchMethodException) {
          throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
        }
        catch (ClassNotFoundException localClassNotFoundException) {
          throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
        }
      }
    }

    可以看到,动态生成的代理类有如下特性: 

    1. 继承了Proxy类,实现了代理的接口,由于java不能多继承,这里已经继承了Proxy类了,不能再继承其他的类,所以JDK的动态代理不支持对实现类的代理,只支持接口的代理。
    2. 提供了一个使用InvocationHandler作为参数的构造方法。
    3. 生成静态代码块来初始化接口中方法的Method对象,以及Object类的equals、hashCode、toString方法。
    4. 重写了Object类的equals、hashCode、toString,它们都只是简单的调用了InvocationHandler的invoke方法,即可以对其进行特殊的操作,也就是说JDK的动态代理还可以代理上述三个方法。
    5. 代理类实现代理接口的sayHello方法中,只是简单的调用了InvocationHandler的invoke方法,我们可以在invoke方法中进行一些特殊操作,甚至不调用实现的方法,直接返回。

    CGLIB动态代理(创建子类)

    JDK实现动态代理需要实现类通过接口定义业务方法,对于没有接口的类,如何实现动态代理呢,这就需要CGLib了。CGLib采用了非常底层的字节码技术,其原理是通过字节码技术为一个类创建子类,并在子类中采用方法拦截的技术拦截所有父类方法的调用,顺势织入横切逻辑。JDK动态代理与CGLib动态代理均是实现Spring AOP的基础。

    这是一个需要被代理的类,也就是父类,通过字节码技术创建这个类的子类,实现动态代理。

    public class SayHello {
     public void say(){
      System.out.println("hello everyone");
     }
    }

    该类实现了创建子类的方法与代理的方法。getProxy(SuperClass.class)方法通过入参即父类的字节码,通过扩展父类的class来创建代理对象。intercept()方法拦截所有目标类方法的调用,obj表示目标类的实例,method为目标类方法的反射对象,args为方法的动态入参,proxy为代理类实例。proxy.invokeSuper(obj, args)通过代理类调用父类中的方法。

    public class CglibProxy implements MethodInterceptor{
     private Enhancer enhancer = new Enhancer();
     public Object getProxy(Class clazz){
      //设置需要创建子类的类
      enhancer.setSuperclass(clazz);
      enhancer.setCallback(this);
      //通过字节码技术动态创建子类实例
      return enhancer.create();
     }
     //实现MethodInterceptor接口方法
     public Object intercept(Object obj, Method method, Object[] args,
       MethodProxy proxy) throws Throwable {
      System.out.println("前置代理");
      //通过代理类调用父类中的方法
      Object result = proxy.invokeSuper(obj, args);
      System.out.println("后置代理");
      return result;
     }
    }

    具体实现类:

    public class DoCGLib {
     public static void main(String[] args) {
      CglibProxy proxy = new CglibProxy();
      //通过生成子类的方式创建代理类
      SayHello proxyImp = (SayHello)proxy.getProxy(SayHello.class);
      proxyImp.say();
     }
    }

    CGLib创建的动态代理对象性能比JDK创建的动态代理对象的性能高不少,但是CGLib在创建代理对象时所花费的时间却比JDK多得多,所以对于单例的对象,因为无需频繁创建对象,用CGLib合适,反之,使用JDK方式要更为合适一些。同时,由于CGLib由于是采用动态创建子类的方法,对于final方法,无法进行代理。

    Spring使用JDK动态代理分析

    研究一下Spring如何使用JDK来生成代理对象,具体的生成代码放在JdkDynamicAopProxy这个类中,直接上相关代码:

    /** 
        * <ol> 
        * <li>获取代理类要实现的接口,除了Advised对象中配置的,还会加上SpringProxy, Advised(opaque=false) 
        * <li>检查上面得到的接口中有没有定义 equals或者hashcode的接口 
        * <li>调用Proxy.newProxyInstance创建代理对象 
        * </ol> 
        */  
       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);  
    }  

    我们知道InvocationHandler是JDK动态代理的核心,生成的代理对象的方法调用都会委托到InvocationHandler.invoke()方法。而通过JdkDynamicAopProxy的签名我们可以看到这个类其实也实现了InvocationHandler,下面我们就通过分析这个类中实现的invoke()方法来具体看下Spring AOP是如何织入切面的。

    publicObject invoke(Object proxy, Method method, Object[] args) throwsThrowable {
           MethodInvocation invocation = null;
           Object oldProxy = null;
           boolean setProxyContext = false;
     
           TargetSource targetSource = this.advised.targetSource;
           Class targetClass = null;
           Object target = null;
     
           try {
               //eqauls()方法,具目标对象未实现此方法
               if (!this.equalsDefined && AopUtils.isEqualsMethod(method)){
                    return (equals(args[0])? Boolean.TRUE : Boolean.FALSE);
               }
     
               //hashCode()方法,具目标对象未实现此方法
               if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)){
                    return newInteger(hashCode());
               }
     
               //Advised接口或者其父接口中定义的方法,直接反射调用,不应用通知
               if (!this.advised.opaque &&method.getDeclaringClass().isInterface()
                        &&method.getDeclaringClass().isAssignableFrom(Advised.class)) {
                    // Service invocations onProxyConfig with the proxy config...
                    return AopUtils.invokeJoinpointUsingReflection(this.advised,method, args);
               }
     
               Object retVal = null;
     
               if (this.advised.exposeProxy) {
                    // Make invocation available ifnecessary.
                    oldProxy = AopContext.setCurrentProxy(proxy);
                    setProxyContext = true;
               }
     
               //获得目标对象的类
               target = targetSource.getTarget();
               if (target != null) {
                    targetClass = target.getClass();
               }
     
               //获取可以应用到此方法上的Interceptor列表
               List chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method,targetClass);
     
               //如果没有可以应用到此方法的通知(Interceptor),此直接反射调用 method.invoke(target, args)
               if (chain.isEmpty()) {
                    retVal = AopUtils.invokeJoinpointUsingReflection(target,method, args);
               } else {
                    //创建MethodInvocation
                    invocation = newReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
                    retVal = invocation.proceed();
               }
     
               // Massage return value if necessary.
               if (retVal != null && retVal == target &&method.getReturnType().isInstance(proxy)
                        &&!RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
                    // Special case: it returned"this" and the return type of the method
                    // is type-compatible. Notethat we can't help if the target sets
                    // a reference to itself inanother returned object.
                    retVal = proxy;
               }
               return retVal;
           } finally {
               if (target != null && !targetSource.isStatic()) {
                    // Must have come fromTargetSource.
                   targetSource.releaseTarget(target);
               }
               if (setProxyContext) {
                    // Restore old proxy.
                    AopContext.setCurrentProxy(oldProxy);
               }
           }
        }

    主流程可以简述为:获取可以应用到此方法上的通知链(Interceptor Chain),如果有,则应用通知,并执行joinpoint; 如果没有,则直接反射执行joinpoint。而这里的关键是通知链是如何获取的以及它又是如何执行的,下面逐一分析下。

    首先,从上面的代码可以看到,通知链是通过Advised.getInterceptorsAndDynamicInterceptionAdvice()这个方法来获取的,我们来看下这个方法的实现:

    public List<Object>getInterceptorsAndDynamicInterceptionAdvice(Method method, Class targetClass) {
                       MethodCacheKeycacheKey = new MethodCacheKey(method);
                       List<Object>cached = this.methodCache.get(cacheKey);
                       if(cached == null) {
                                cached= this.advisorChainFactory.getInterceptorsAndDynamicInterceptionAdvice(
                                                   this,method, targetClass);
                                this.methodCache.put(cacheKey,cached);
                       }
                       returncached;
             }

    可以看到实际的获取工作其实是由AdvisorChainFactory. getInterceptorsAndDynamicInterceptionAdvice()这个方法来完成的,获取到的结果会被缓存。

    下面来分析下这个方法的实现:

    /**
        * 从提供的配置实例config中获取advisor列表,遍历处理这些advisor.如果是IntroductionAdvisor,
        * 则判断此Advisor能否应用到目标类targetClass上.如果是PointcutAdvisor,则判断
        * 此Advisor能否应用到目标方法method上.将满足条件的Advisor通过AdvisorAdaptor转化成Interceptor列表返回.
        */
        publicList getInterceptorsAndDynamicInterceptionAdvice(Advised config, Methodmethod, Class targetClass) {
           // This is somewhat tricky... we have to process introductions first,
           // but we need to preserve order in the ultimate list.
           List interceptorList = new ArrayList(config.getAdvisors().length);
     
           //查看是否包含IntroductionAdvisor
           boolean hasIntroductions = hasMatchingIntroductions(config,targetClass);
     
           //这里实际上注册一系列AdvisorAdapter,用于将Advisor转化成MethodInterceptor
           AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
     
           Advisor[] advisors = config.getAdvisors();
            for (int i = 0; i <advisors.length; i++) {
               Advisor advisor = advisors[i];
               if (advisor instanceof PointcutAdvisor) {
                    // Add it conditionally.
                    PointcutAdvisor pointcutAdvisor= (PointcutAdvisor) advisor;
                    if(config.isPreFiltered() ||pointcutAdvisor.getPointcut().getClassFilter().matches(targetClass)) {
                        //TODO: 这个地方这两个方法的位置可以互换下
                        //将Advisor转化成Interceptor
                        MethodInterceptor[]interceptors = registry.getInterceptors(advisor);
     
                        //检查当前advisor的pointcut是否可以匹配当前方法
                        MethodMatcher mm =pointcutAdvisor.getPointcut().getMethodMatcher();
     
                        if (MethodMatchers.matches(mm,method, targetClass, hasIntroductions)) {
                            if(mm.isRuntime()) {
                                // Creating a newobject instance in the getInterceptors() method
                                // isn't a problemas we normally cache created chains.
                                for (intj = 0; j < interceptors.length; j++) {
                                   interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptors[j],mm));
                                }
                            } else {
                                interceptorList.addAll(Arrays.asList(interceptors));
                            }
                        }
                    }
               } else if (advisor instanceof IntroductionAdvisor){
                    IntroductionAdvisor ia =(IntroductionAdvisor) advisor;
                    if(config.isPreFiltered() || ia.getClassFilter().matches(targetClass)) {
                        Interceptor[] interceptors= registry.getInterceptors(advisor);
                        interceptorList.addAll(Arrays.asList(interceptors));
                    }
               } else {
                    Interceptor[] interceptors =registry.getInterceptors(advisor);
                    interceptorList.addAll(Arrays.asList(interceptors));
               }
           }
           return interceptorList;
    }

    这个方法执行完成后,Advised中配置能够应用到连接点或者目标类的Advisor全部被转化成了MethodInterceptor. 

    接下来我们再看下得到的拦截器链是怎么起作用的。

    if (chain.isEmpty()) {
                    retVal = AopUtils.invokeJoinpointUsingReflection(target,method, args);
                } else {
                    //创建MethodInvocation
                    invocation = newReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
                    retVal = invocation.proceed();
                }

    从这段代码可以看出,如果得到的拦截器链为空,则直接反射调用目标方法,否则创建MethodInvocation,调用其proceed方法,触发拦截器链的执行,来看下具体代码

    public Object proceed() throws Throwable {
           //  We start with an index of -1and increment early.
           if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size()- 1) {
               //如果Interceptor执行完了,则执行joinPoint
               return invokeJoinpoint();
           }
     
           Object interceptorOrInterceptionAdvice =
               this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
           
           //如果要动态匹配joinPoint
           if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher){
               // Evaluate dynamic method matcher here: static part will already have
               // been evaluated and found to match.
               InterceptorAndDynamicMethodMatcher dm =
                    (InterceptorAndDynamicMethodMatcher)interceptorOrInterceptionAdvice;
               //动态匹配:运行时参数是否满足匹配条件
               if (dm.methodMatcher.matches(this.method, this.targetClass,this.arguments)) {
                    //执行当前Intercetpor
                    returndm.interceptor.invoke(this);
               }
               else {
                    //动态匹配失败时,略过当前Intercetpor,调用下一个Interceptor
                    return proceed();
               }
           }
           else {
               // It's an interceptor, so we just invoke it: The pointcutwill have
               // been evaluated statically before this object was constructed.
               //执行当前Intercetpor
               return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
           }
    }

    Spring使用Cglib动态代理分析

    Cglib2AopProxy 

    package org.springframework.aop.framework;
    final class Cglib2AopProxy implements AopProxy, Serializable {
        private static class DynamicAdvisedInterceptor implements MethodInterceptor, Serializable {
                //注意此处的method 一定是具体类上的method(因此只用放置在具体类上的@Transactional是可以发现的)
            public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
                }
           }
    }

    Spring使用AnnotationTransactionAttributeSource通过查找一个类或方法是否有@Transactional注解事务来返回TransactionAttribute(表示开启事务):

    package org.springframework.transaction.annotation;
    public class AnnotationTransactionAttributeSource extends AbstractFallbackTransactionAttributeSource implements Serializable {
             protected TransactionAttribute determineTransactionAttribute(AnnotatedElement ae) {
            for (TransactionAnnotationParser annotationParser : this.annotationParsers) {
                TransactionAttribute attr = annotationParser.parseTransactionAnnotation(ae);
                if (attr != null) {
                    return attr;
                }
            }
            return null;
        }
    }

    而AnnotationTransactionAttributeSource又使用SpringTransactionAnnotationParser来解析是否有@Transactional注解:

    package org.springframework.transaction.annotation;
    
    public class SpringTransactionAnnotationParser implements TransactionAnnotationParser, Serializable {
    
        public TransactionAttribute parseTransactionAnnotation(AnnotatedElement ae) {
            Transactional ann = AnnotationUtils.getAnnotation(ae, Transactional.class);
            if (ann != null) {
                return parseTransactionAnnotation(ann);
            }
            else {
                return null;
            }
        }
    
        public TransactionAttribute parseTransactionAnnotation(Transactional ann) {
            
        }
    
    }

    此处使用AnnotationUtils.getAnnotation(ae, Transactional.class); 这个方法只能发现当前方法/类上的注解,不能发现父类的注解。 Spring还提供了一个 AnnotationUtils.findAnnotation()方法 可以发现父类/父接口中的注解(但spring没有使用该接口)。 

    如果Spring此处换成AnnotationUtils.findAnnotation(),将可以发现父类/父接口中的注解。

     

    疑惑解答

    问题:

        我们之前说过,基于JDK动态代理时, method 一定是接口上的method(因此放置在接口上的@Transactional是可以发现的),但现在我们放在具体类上,那么Spring是如何发现的呢??

    发现TransactionAttribute是通过AnnotationTransactionAttributeSource吗? 而AnnotationTransactionAttributeSource 继承AbstractFallbackTransactionAttributeSource

    package org.springframework.transaction.interceptor;
    public abstract class AbstractFallbackTransactionAttributeSource implements TransactionAttributeSource {
    
        public TransactionAttribute getTransactionAttribute(Method method, Class<?> targetClass) {
            //第一次 会委托给computeTransactionAttribute
    }
    
        //计算TransactionAttribute的
        private TransactionAttribute computeTransactionAttribute(Method method, Class<?> targetClass) {
            
            //省略
    
            // Ignore CGLIB subclasses - introspect the actual user class.
            Class<?> userClass = ClassUtils.getUserClass(targetClass);
            // The method may be on an interface, but we need attributes from the target class.
            // If the target class is null, the method will be unchanged.
            //①此处将查找当前类覆盖的方法
            Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass);
            // If we are dealing with method with generic parameters, find the original method.
            specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod);
    
            // First try is the method in the target class.
            TransactionAttribute txAtt = findTransactionAttribute(specificMethod);
            if (txAtt != null) {
                return txAtt;
            }
    
            //找类上边的注解
            // Second try is the transaction attribute on the target class.
            txAtt = findTransactionAttribute(specificMethod.getDeclaringClass());
            if (txAtt != null) {
                return txAtt;
            }
            //②如果子类覆盖的方法没有 再直接找当前传过来的
            if (specificMethod != method) {
                // Fallback is to look at the original method.
                txAtt = findTransactionAttribute(method);
                if (txAtt != null) {
                    return txAtt;
                }
                // Last fallback is the class of the original method.
                return findTransactionAttribute(method.getDeclaringClass());
            }
            return null;
        }
    }

    //①此处将查找子类覆盖的方法

    Method specificMethod = ClassUtils.getMostSpecificMethod(method, userClass); 

    // ClassUtils.getMostSpecificMethod

           public static Method getMostSpecificMethod(Method method, Class<?> targetClass) {

           Method specificMethod = null;

           if (method != null && isOverridable(method, targetClass) &&

                  targetClass != null && !targetClass.equals(method.getDeclaringClass())) {

               try {

                  specificMethod = ReflectionUtils.findMethod(targetClass, method.getName(), method.getParameterTypes());

               } catch (AccessControlException ex) {

                  // security settings are disallowing reflective access; leave

                  // 'specificMethod' null and fall back to 'method' below

               }

           }

           return (specificMethod != null ? specificMethod : method);

        }

    可以看出将找到当前类的那个方法。因此我们放置在BaseService countAll方法上的@Transactional起作用了。

    //②如果子类覆盖的方法没有 再直接找当前传过来的

           if (specificMethod != method) {

               // Fallback is to look at the original method.

               txAtt = findTransactionAttribute(method);

               if (txAtt != null) {

                  return txAtt;

               }

               // Last fallback is the class of the original method.

               return findTransactionAttribute(method.getDeclaringClass());

           }

     查找子类失败时直接使用传过来的方法。

     

  • 相关阅读:
    使用TestStack.White进行Windows UI的自动化测试
    基于IDEA的JavaWeb开发环境搭建
    hfish 集群蜜罐搭建
    HFish开源蜜罐搭建
    redis实现查找附近商户信息功能
    WIN10 HYPERV 1503
    RPC
    推荐一个聚合搜索引擎提高学习工作效率
    RocketMQ原理分析&场景问题
    《高性能利器》--异步调用实现原理详解!
  • 原文地址:https://www.cnblogs.com/wade-luffy/p/5767092.html
Copyright © 2011-2022 走看看