ToB企服应用市场:ToB评测及商务社交产业平台

标题: AOP中动态署理详解 [打印本页]

作者: 勿忘初心做自己    时间: 2024-12-24 10:49
标题: AOP中动态署理详解
动态署理概述

什么是署理

署理模式(Proxy pattern): 为另一个对象提供一个替人或占位符以控制对这个对象的访问

什么是动态署理?

   动态署理就是,在程序运行期,创建目的对象的署理对象,并对目的对象中的方法进行功能性加强的一种技能。
  在生成署理对象的过程中,目的对象稳定,署理对象中的方法是目的对象方法的加强方法。可以明白为运行期间,对象中方法的动态拦截,在拦截方法的前后执行功能操作。
署理的创建

创建署理的方法是postProcessAfterInitialization:如果bean被子类标识为署理,则利用配置的拦截器创建一个署理
  1. /**
  2.   * Create a proxy with the configured interceptors if the bean is
  3.   * identified as one to proxy by the subclass.
  4.   * @see #getAdvicesAndAdvisorsForBean
  5.   */
  6. @Override
  7. public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
  8.   if (bean != null) {
  9.     Object cacheKey = getCacheKey(bean.getClass(), beanName);
  10.     // 如果不是提前暴露的代理
  11.     if (this.earlyProxyReferences.remove(cacheKey) != bean) {
  12.       return wrapIfNecessary(bean, beanName, cacheKey);
  13.     }
  14.   }
  15.   return bean;
  16. }
复制代码
wrapIfNecessary方法主要用于判断是否须要创建署理,如果Bean可以大概获取到advisor才须要创建署理
  1. /**
  2.   * Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
  3.   * @param bean the raw bean instance
  4.   * @param beanName the name of the bean
  5.   * @param cacheKey the cache key for metadata access
  6.   * @return a proxy wrapping the bean, or the raw bean instance as-is
  7.   */
  8. protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
  9.    // 如果bean是通过TargetSource接口获取
  10.    if (beanName != null && this.targetSourcedBeans.contains(beanName)) {
  11.       return bean;
  12.    }
  13.    // 如果bean是切面类
  14.    if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
  15.       return bean;
  16.    }
  17.    // 如果是aop基础类?是否跳过?
  18.    if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
  19.       this.advisedBeans.put(cacheKey, Boolean.FALSE);
  20.       return bean;
  21.    }
  22.   // 重点:获取所有advisor,如果没有获取到,那说明不要进行增强,也就不需要代理了。
  23.   Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
  24.   if (specificInterceptors != DO_NOT_PROXY) {
  25.     this.advisedBeans.put(cacheKey, Boolean.TRUE);
  26.     // 重点:创建代理
  27.     Object proxy = createProxy(
  28.         bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
  29.     this.proxyTypes.put(cacheKey, proxy.getClass());
  30.     return proxy;
  31.   }
  32.   this.advisedBeans.put(cacheKey, Boolean.FALSE);
  33.   return bean;
  34. }
复制代码
获取全部的Advisor

我们看下获取全部advisor的方法getAdvicesAndAdvisorsForBean
  1. @Override
  2. @Nullable
  3. protected Object[] getAdvicesAndAdvisorsForBean(
  4.     Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {
  5.   List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
  6.   if (advisors.isEmpty()) {
  7.     return DO_NOT_PROXY;
  8.   }
  9.   return advisors.toArray();
  10. }
复制代码
通过findEligibleAdvisors方法获取advisor, 如果获取不到返回DO_NOT_PROXY(不须要创建署理),findEligibleAdvisors方法如下
  1. /**
  2.   * Find all eligible Advisors for auto-proxying this class.
  3.   * @param beanClass the clazz to find advisors for
  4.   * @param beanName the name of the currently proxied bean
  5.   * @return the empty List, not {@code null},
  6.   * if there are no pointcuts or interceptors
  7.   * @see #findCandidateAdvisors
  8.   * @see #sortAdvisors
  9.   * @see #extendAdvisors
  10.   */
  11. protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
  12.   // 和上文一样,获取所有切面类的切面方法生成Advisor
  13.   List<Advisor> candidateAdvisors = findCandidateAdvisors();
  14.   // 找到这些Advisor中能够应用于beanClass的Advisor
  15.   List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
  16.   // 如果需要,交给子类拓展
  17.   extendAdvisors(eligibleAdvisors);
  18.   // 对Advisor排序
  19.   if (!eligibleAdvisors.isEmpty()) {
  20.     eligibleAdvisors = sortAdvisors(eligibleAdvisors);
  21.   }
  22.   return eligibleAdvisors;
  23. }
复制代码
获取全部切面类的切面方法生成Advisor
  1. /**
  2.   * Find all candidate Advisors to use in auto-proxying.
  3.   * @return the List of candidate Advisors
  4.   */
  5. protected List<Advisor> findCandidateAdvisors() {
  6.   Assert.state(this.advisorRetrievalHelper != null, "No BeanFactoryAdvisorRetrievalHelper available");
  7.   return this.advisorRetrievalHelper.findAdvisorBeans();
  8. }
复制代码
找到这些Advisor中可以大概应用于beanClass的Advisor
  1. /**
  2.   * Determine the sublist of the {@code candidateAdvisors} list
  3.   * that is applicable to the given class.
  4.   * @param candidateAdvisors the Advisors to evaluate
  5.   * @param clazz the target class
  6.   * @return sublist of Advisors that can apply to an object of the given class
  7.   * (may be the incoming List as-is)
  8.   */
  9. public static List<Advisor> findAdvisorsThatCanApply(List<Advisor> candidateAdvisors, Class<?> clazz) {
  10.   if (candidateAdvisors.isEmpty()) {
  11.     return candidateAdvisors;
  12.   }
  13.   List<Advisor> eligibleAdvisors = new ArrayList<>();
  14.   for (Advisor candidate : candidateAdvisors) {
  15.     // 通过Introduction实现的advice
  16.     if (candidate instanceof IntroductionAdvisor && canApply(candidate, clazz)) {
  17.       eligibleAdvisors.add(candidate);
  18.     }
  19.   }
  20.   boolean hasIntroductions = !eligibleAdvisors.isEmpty();
  21.   for (Advisor candidate : candidateAdvisors) {
  22.     if (candidate instanceof IntroductionAdvisor) {
  23.       // already processed
  24.       continue;
  25.     }
  26.     // 是否能够应用于clazz的Advice
  27.     if (canApply(candidate, clazz, hasIntroductions)) {
  28.       eligibleAdvisors.add(candidate);
  29.     }
  30.   }
  31.   return eligibleAdvisors;
  32. }
复制代码
创建署理的入口方法

获取全部advisor后,如果有advisor,则阐明须要加强,即须要创建署理,创建署理的方法如下:
  1. /**
  2.   * Create an AOP proxy for the given bean.
  3.   * @param beanClass the class of the bean
  4.   * @param beanName the name of the bean
  5.   * @param specificInterceptors the set of interceptors that is
  6.   * specific to this bean (may be empty, but not null)
  7.   * @param targetSource the TargetSource for the proxy,
  8.   * already pre-configured to access the bean
  9.   * @return the AOP proxy for the bean
  10.   * @see #buildAdvisors
  11.   */
  12. protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
  13.     @Nullable Object[] specificInterceptors, TargetSource targetSource) {
  14.   if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
  15.     AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
  16.   }
  17.   ProxyFactory proxyFactory = new ProxyFactory();
  18.   proxyFactory.copyFrom(this);
  19.   if (proxyFactory.isProxyTargetClass()) {
  20.     // Explicit handling of JDK proxy targets (for introduction advice scenarios)
  21.     if (Proxy.isProxyClass(beanClass)) {
  22.       // Must allow for introductions; can't just set interfaces to the proxy's interfaces only.
  23.       for (Class<?> ifc : beanClass.getInterfaces()) {
  24.         proxyFactory.addInterface(ifc);
  25.       }
  26.     }
  27.   }
  28.   else {
  29.     // No proxyTargetClass flag enforced, let's apply our default checks...
  30.     if (shouldProxyTargetClass(beanClass, beanName)) {
  31.       proxyFactory.setProxyTargetClass(true);
  32.     }
  33.     else {
  34.       evaluateProxyInterfaces(beanClass, proxyFactory);
  35.     }
  36.   }
  37.   Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
  38.   proxyFactory.addAdvisors(advisors);
  39.   proxyFactory.setTargetSource(targetSource);
  40.   customizeProxyFactory(proxyFactory);
  41.   proxyFactory.setFrozen(this.freezeProxy);
  42.   if (advisorsPreFiltered()) {
  43.     proxyFactory.setPreFiltered(true);
  44.   }
  45.   // Use original ClassLoader if bean class not locally loaded in overriding class loader
  46.   ClassLoader classLoader = getProxyClassLoader();
  47.   if (classLoader instanceof SmartClassLoader && classLoader != beanClass.getClassLoader()) {
  48.     classLoader = ((SmartClassLoader) classLoader).getOriginalClassLoader();
  49.   }
  50.   return proxyFactory.getProxy(classLoader);
  51. }
复制代码
proxyFactory.getProxy(classLoader)

  1. /**
  2.   * Create a new proxy according to the settings in this factory.
  3.   * <p>Can be called repeatedly. Effect will vary if we've added
  4.   * or removed interfaces. Can add and remove interceptors.
  5.   * <p>Uses the given class loader (if necessary for proxy creation).
  6.   * @param classLoader the class loader to create the proxy with
  7.   * (or {@code null} for the low-level proxy facility's default)
  8.   * @return the proxy object
  9.   */
  10. public Object getProxy(@Nullable ClassLoader classLoader) {
  11.   return createAopProxy().getProxy(classLoader);
  12. }
复制代码
依据条件创建署理(jdk或cglib)

DefaultAopProxyFactory.createAopProxy
  1. @Override
  2. public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
  3.   if (!NativeDetector.inNativeImage() &&
  4.       (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config))) {
  5.     Class<?> targetClass = config.getTargetClass();
  6.     if (targetClass == null) {
  7.       throw new AopConfigException("TargetSource cannot determine target class: " +
  8.           "Either an interface or a target is required for proxy creation.");
  9.     }
  10.     if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
  11.       return new JdkDynamicAopProxy(config);
  12.     }
  13.     return new ObjenesisCglibAopProxy(config);
  14.   }
  15.   else {
  16.     return new JdkDynamicAopProxy(config);
  17.   }
  18. }
复制代码
小结


由此可以知道:
Spring默认在目的类实现接口时是通过JDK署理实现的,只有非接口的是通过Cglib署理实现的。当设置proxy-target-class为true时在目的类不是接口大概署理类时优先利用cglib署理实现。
JDK署理

JDK动态署理是有JDK提供的工具类Proxy实现的,动态署理类是在运行时生成指定接口的署理类,每个署理实例(实现须要署理的接口)都有一个关联的调用处置处罚程序对象,此对象实现了InvocationHandler,终极的业务逻辑是在InvocationHandler实现类的invoke方法上。
JDK署理的流程如下:
   JDK署理自动生成的class是由sun.misc.ProxyGenerator来生成的。
  ProxyGenerator生成代码

我们看下sun.misc.ProxyGenerator生成代码的逻辑:
  1. /**
  2.     * Generate a proxy class given a name and a list of proxy interfaces.
  3.     *
  4.     * @param name        the class name of the proxy class
  5.     * @param interfaces  proxy interfaces
  6.     * @param accessFlags access flags of the proxy class
  7. */
  8. public static byte[] generateProxyClass(final String name,
  9.                                         Class<?>[] interfaces,
  10.                                         int accessFlags)
  11. {
  12.     ProxyGenerator gen = new ProxyGenerator(name, interfaces, accessFlags);
  13.     final byte[] classFile = gen.generateClassFile();
  14.     ...
  15. }
复制代码
generateClassFile方法如下:
  1. /**
  2.     * Generate a class file for the proxy class.  This method drives the
  3.     * class file generation process.
  4.     */
  5. private byte[] generateClassFile() {
  6.     /* 第一步:将所有方法包装成ProxyMethod对象 */
  7.    
  8.     // 将Object类中hashCode、equals、toString方法包装成ProxyMethod对象
  9.     addProxyMethod(hashCodeMethod, Object.class);
  10.     addProxyMethod(equalsMethod, Object.class);
  11.     addProxyMethod(toStringMethod, Object.class);
  12.     // 将代理类接口方法包装成ProxyMethod对象
  13.     for (Class<?> intf : interfaces) {
  14.         for (Method m : intf.getMethods()) {
  15.             addProxyMethod(m, intf);
  16.         }
  17.     }
  18.     // 校验返回类型
  19.     for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
  20.         checkReturnTypes(sigmethods);
  21.     }
  22.     /* 第二步:为代理类组装字段,构造函数,方法,static初始化块等 */
  23.     try {
  24.         // 添加构造函数,参数是InvocationHandler
  25.         methods.add(generateConstructor());
  26.         // 代理方法
  27.         for (List<ProxyMethod> sigmethods : proxyMethods.values()) {
  28.             for (ProxyMethod pm : sigmethods) {
  29.                 // 字段
  30.                 fields.add(new FieldInfo(pm.methodFieldName,
  31.                     "Ljava/lang/reflect/Method;",
  32.                         ACC_PRIVATE | ACC_STATIC));
  33.                 // 上述ProxyMethod中的方法
  34.                 methods.add(pm.generateMethod());
  35.             }
  36.         }
  37.         // static初始化块
  38.         methods.add(generateStaticInitializer());
  39.     } catch (IOException e) {
  40.         throw new InternalError("unexpected I/O Exception", e);
  41.     }
  42.     if (methods.size() > 65535) {
  43.         throw new IllegalArgumentException("method limit exceeded");
  44.     }
  45.     if (fields.size() > 65535) {
  46.         throw new IllegalArgumentException("field limit exceeded");
  47.     }
  48.     /* 第三步:写入class文件 */
  49.     /*
  50.         * Make sure that constant pool indexes are reserved for the
  51.         * following items before starting to write the final class file.
  52.         */
  53.     cp.getClass(dotToSlash(className));
  54.     cp.getClass(superclassName);
  55.     for (Class<?> intf: interfaces) {
  56.         cp.getClass(dotToSlash(intf.getName()));
  57.     }
  58.     /*
  59.         * Disallow new constant pool additions beyond this point, since
  60.         * we are about to write the final constant pool table.
  61.         */
  62.     cp.setReadOnly();
  63.     ByteArrayOutputStream bout = new ByteArrayOutputStream();
  64.     DataOutputStream dout = new DataOutputStream(bout);
  65.     try {
  66.         /*
  67.             * Write all the items of the "ClassFile" structure.
  68.             * See JVMS section 4.1.
  69.             */
  70.                                     // u4 magic;
  71.         dout.writeInt(0xCAFEBABE);
  72.                                     // u2 minor_version;
  73.         dout.writeShort(CLASSFILE_MINOR_VERSION);
  74.                                     // u2 major_version;
  75.         dout.writeShort(CLASSFILE_MAJOR_VERSION);
  76.         cp.write(dout);             // (write constant pool)
  77.                                     // u2 access_flags;
  78.         dout.writeShort(accessFlags);
  79.                                     // u2 this_class;
  80.         dout.writeShort(cp.getClass(dotToSlash(className)));
  81.                                     // u2 super_class;
  82.         dout.writeShort(cp.getClass(superclassName));
  83.                                     // u2 interfaces_count;
  84.         dout.writeShort(interfaces.length);
  85.                                     // u2 interfaces[interfaces_count];
  86.         for (Class<?> intf : interfaces) {
  87.             dout.writeShort(cp.getClass(
  88.                 dotToSlash(intf.getName())));
  89.         }
  90.                                     // u2 fields_count;
  91.         dout.writeShort(fields.size());
  92.                                     // field_info fields[fields_count];
  93.         for (FieldInfo f : fields) {
  94.             f.write(dout);
  95.         }
  96.                                     // u2 methods_count;
  97.         dout.writeShort(methods.size());
  98.                                     // method_info methods[methods_count];
  99.         for (MethodInfo m : methods) {
  100.             m.write(dout);
  101.         }
  102.                                         // u2 attributes_count;
  103.         dout.writeShort(0); // (no ClassFile attributes for proxy classes)
  104.     } catch (IOException e) {
  105.         throw new InternalError("unexpected I/O Exception", e);
  106.     }
  107.     return bout.toByteArray();
  108. }
复制代码
一共三个步骤(把大象装进冰箱分几步?):

从生成的Proxy代码看执行流程

从上述sun.misc.ProxyGenerator类中可以看到,这个类里面有一个配置参数sun.misc.ProxyGenerator.saveGeneratedFiles,可以通过这个参数将生成的Proxy类生存在本地,比如设置为true 执行后,生成的文件如下:

我们看下生成后的代码:
  1. //
  2. // Source code recreated from a .class file by IntelliJ IDEA
  3. // (powered by FernFlower decompiler)
  4. //
  5. package com.sun.proxy;
  6. import java.lang.reflect.InvocationHandler;
  7. import java.lang.reflect.Method;
  8. import java.lang.reflect.Proxy;
  9. import java.lang.reflect.UndeclaredThrowableException;
  10. import java.util.List;
  11. import tech.pdai.springframework.service.IUserService;
  12. // 所有类和方法都是final类型的
  13. public final class $Proxy0 extends Proxy implements IUserService {
  14.     private static Method m1;
  15.     private static Method m3;
  16.     private static Method m2;
  17.     private static Method m0;
  18.     private static Method m4;
  19.     // 构造函数注入 InvocationHandler
  20.     public $Proxy0(InvocationHandler var1) throws  {
  21.         super(var1);
  22.     }
  23.     public final boolean equals(Object var1) throws  {
  24.         try {
  25.             return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
  26.         } catch (RuntimeException | Error var3) {
  27.             throw var3;
  28.         } catch (Throwable var4) {
  29.             throw new UndeclaredThrowableException(var4);
  30.         }
  31.     }
  32.     public final List findUserList() throws  {
  33.         try {
  34.             return (List)super.h.invoke(this, m3, (Object[])null);
  35.         } catch (RuntimeException | Error var2) {
  36.             throw var2;
  37.         } catch (Throwable var3) {
  38.             throw new UndeclaredThrowableException(var3);
  39.         }
  40.     }
  41.     public final String toString() throws  {
  42.         try {
  43.             return (String)super.h.invoke(this, m2, (Object[])null);
  44.         } catch (RuntimeException | Error var2) {
  45.             throw var2;
  46.         } catch (Throwable var3) {
  47.             throw new UndeclaredThrowableException(var3);
  48.         }
  49.     }
  50.     public final int hashCode() throws  {
  51.         try {
  52.             return (Integer)super.h.invoke(this, m0, (Object[])null);
  53.         } catch (RuntimeException | Error var2) {
  54.             throw var2;
  55.         } catch (Throwable var3) {
  56.             throw new UndeclaredThrowableException(var3);
  57.         }
  58.     }
  59.     public final void addUser() throws  {
  60.         try {
  61.             super.h.invoke(this, m4, (Object[])null);
  62.         } catch (RuntimeException | Error var2) {
  63.             throw var2;
  64.         } catch (Throwable var3) {
  65.             throw new UndeclaredThrowableException(var3);
  66.         }
  67.     }
  68.     static {
  69.         try {
  70.             // 初始化 methods, 2个IUserService接口中的方法,3个Object中的接口
  71.             m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
  72.             m3 = Class.forName("tech.pdai.springframework.service.IUserService").getMethod("findUserList");
  73.             m2 = Class.forName("java.lang.Object").getMethod("toString");
  74.             m0 = Class.forName("java.lang.Object").getMethod("hashCode");
  75.             m4 = Class.forName("tech.pdai.springframework.service.IUserService").getMethod("addUser");
  76.         } catch (NoSuchMethodException var2) {
  77.             throw new NoSuchMethodError(var2.getMessage());
  78.         } catch (ClassNotFoundException var3) {
  79.             throw new NoClassDefFoundError(var3.getMessage());
  80.         }
  81.     }
  82. }
复制代码
上述代码是比较容易明白的,我就不绘图了。
主要流程是:

SpringAOP中JDK署理的实现

SpringAOP饰演的是JDK署理的创建和调用两个脚色,我们通过这两个方向来看下SpringAOP的代码(JdkDynamicAopProxy类)
SpringAOP Jdk署理的创建

署理的创建比较简单,调用getProxy方法,然后直接调用JDK中Proxy.newProxyInstance()方法将classloader和被署理的接口方法传入即可。
  1. @Override
  2. public Object getProxy() {
  3.     return getProxy(ClassUtils.getDefaultClassLoader());
  4. }
  5. @Override
  6. public Object getProxy(@Nullable ClassLoader classLoader) {
  7.     if (logger.isTraceEnabled()) {
  8.         logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
  9.     }
  10.     return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
  11. }
复制代码
SpringAOP Jdk署理的执行

执行的方法如下:
  1. /**
  2.     * Implementation of {@code InvocationHandler.invoke}.
  3.     * <p>Callers will see exactly the exception thrown by the target,
  4.     * unless a hook method throws an exception.
  5.     */
  6. @Override
  7. @Nullable
  8. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  9.     Object oldProxy = null;
  10.     boolean setProxyContext = false;
  11.     TargetSource targetSource = this.advised.targetSource;
  12.     Object target = null;
  13.     try {
  14.         // 执行的是equal方法
  15.         if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
  16.             // The target does not implement the equals(Object) method itself.
  17.             return equals(args[0]);
  18.         }
  19.         // 执行的是hashcode方法
  20.         else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
  21.             // The target does not implement the hashCode() method itself.
  22.             return hashCode();
  23.         }
  24.         // 如果是包装类,则dispatch to proxy config
  25.         else if (method.getDeclaringClass() == DecoratingProxy.class) {
  26.             // There is only getDecoratedClass() declared -> dispatch to proxy config.
  27.             return AopProxyUtils.ultimateTargetClass(this.advised);
  28.         }
  29.         // 用反射方式来执行切点
  30.         else if (!this.advised.opaque && method.getDeclaringClass().isInterface() &&
  31.                 method.getDeclaringClass().isAssignableFrom(Advised.class)) {
  32.             // Service invocations on ProxyConfig with the proxy config...
  33.             return AopUtils.invokeJoinpointUsingReflection(this.advised, method, args);
  34.         }
  35.         Object retVal;
  36.         if (this.advised.exposeProxy) {
  37.             // Make invocation available if necessary.
  38.             oldProxy = AopContext.setCurrentProxy(proxy);
  39.             setProxyContext = true;
  40.         }
  41.         // Get as late as possible to minimize the time we "own" the target,
  42.         // in case it comes from a pool.
  43.         target = targetSource.getTarget();
  44.         Class<?> targetClass = (target != null ? target.getClass() : null);
  45.         // 获取拦截链
  46.         List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);
  47.         // Check whether we have any advice. If we don't, we can fallback on direct
  48.         // reflective invocation of the target, and avoid creating a MethodInvocation.
  49.         if (chain.isEmpty()) {
  50.             // We can skip creating a MethodInvocation: just invoke the target directly
  51.             // Note that the final invoker must be an InvokerInterceptor so we know it does
  52.             // nothing but a reflective operation on the target, and no hot swapping or fancy proxying.
  53.             Object[] argsToUse = AopProxyUtils.adaptArgumentsIfNecessary(method, args);
  54.             retVal = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
  55.         }
  56.         else {
  57.             // We need to create a method invocation...
  58.             MethodInvocation invocation =
  59.                     new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
  60.             // Proceed to the joinpoint through the interceptor chain.
  61.             retVal = invocation.proceed();
  62.         }
  63.         // Massage return value if necessary.
  64.         Class<?> returnType = method.getReturnType();
  65.         if (retVal != null && retVal == target &&
  66.                 returnType != Object.class && returnType.isInstance(proxy) &&
  67.                 !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
  68.             // Special case: it returned "this" and the return type of the method
  69.             // is type-compatible. Note that we can't help if the target sets
  70.             // a reference to itself in another returned object.
  71.             retVal = proxy;
  72.         }
  73.         else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
  74.             throw new AopInvocationException(
  75.                     "Null return value from advice does not match primitive return type for: " + method);
  76.         }
  77.         return retVal;
  78.     }
  79.     finally {
  80.         if (target != null && !targetSource.isStatic()) {
  81.             // Must have come from TargetSource.
  82.             targetSource.releaseTarget(target);
  83.         }
  84.         if (setProxyContext) {
  85.             // Restore old proxy.
  86.             AopContext.setCurrentProxy(oldProxy);
  87.         }
  88.     }
  89. }
复制代码
CGLIB署理

署理的流程



SpringAOP中Cglib署理的实现

   SpringAOP封装了cglib,通过其进举措态署理的创建。
  我们看下CglibAopProxy的getProxy方法
  1. @Override
  2. public Object getProxy() {
  3.   return getProxy(null);
  4. }
  5. @Override
  6. public Object getProxy(@Nullable ClassLoader classLoader) {
  7.   if (logger.isTraceEnabled()) {
  8.     logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());
  9.   }
  10.   try {
  11.     Class<?> rootClass = this.advised.getTargetClass();
  12.     Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");
  13.     // 上面流程图中的目标类
  14.     Class<?> proxySuperClass = rootClass;
  15.     if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
  16.       proxySuperClass = rootClass.getSuperclass();
  17.       Class<?>[] additionalInterfaces = rootClass.getInterfaces();
  18.       for (Class<?> additionalInterface : additionalInterfaces) {
  19.         this.advised.addInterface(additionalInterface);
  20.       }
  21.     }
  22.     // Validate the class, writing log messages as necessary.
  23.     validateClassIfNecessary(proxySuperClass, classLoader);
  24.     // 重点看这里,就是上图的enhancer,设置各种参数来构建
  25.     Enhancer enhancer = createEnhancer();
  26.     if (classLoader != null) {
  27.       enhancer.setClassLoader(classLoader);
  28.       if (classLoader instanceof SmartClassLoader &&
  29.           ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
  30.         enhancer.setUseCache(false);
  31.       }
  32.     }
  33.     enhancer.setSuperclass(proxySuperClass);
  34.     enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
  35.     enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
  36.     enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));
  37.     // 设置callback回调接口,即方法的增强点
  38.     Callback[] callbacks = getCallbacks(rootClass);
  39.     Class<?>[] types = new Class<?>[callbacks.length];
  40.     for (int x = 0; x < types.length; x++) {
  41.       types[x] = callbacks[x].getClass();
  42.     }
  43.     // 上节说到的filter
  44.     enhancer.setCallbackFilter(new ProxyCallbackFilter(
  45.         this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
  46.     enhancer.setCallbackTypes(types);
  47.     // 重点:创建proxy和其实例
  48.     return createProxyClassAndInstance(enhancer, callbacks);
  49.   }
  50.   catch (CodeGenerationException | IllegalArgumentException ex) {
  51.     throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
  52.         ": Common causes of this problem include using a final class or a non-visible class",
  53.         ex);
  54.   }
  55.   catch (Throwable ex) {
  56.     // TargetSource.getTarget() failed
  57.     throw new AopConfigException("Unexpected AOP exception", ex);
  58.   }
  59. }
复制代码
获取callback的方法如下,提几个明白的要点吧,具体读者在学习的时间发起把我的例子跑一下,然后打一个断点进行明白。

  1. private Callback[] getCallbacks(Class<?> rootClass) throws Exception {
  2.   // Parameters used for optimization choices...
  3.   boolean exposeProxy = this.advised.isExposeProxy();
  4.   boolean isFrozen = this.advised.isFrozen();
  5.   boolean isStatic = this.advised.getTargetSource().isStatic();
  6.   // Choose an "aop" interceptor (used for AOP calls).
  7.   Callback aopInterceptor = new DynamicAdvisedInterceptor(this.advised);
  8.   // Choose a "straight to target" interceptor. (used for calls that are
  9.   // unadvised but can return this). May be required to expose the proxy.
  10.   Callback targetInterceptor;
  11.   if (exposeProxy) {
  12.     targetInterceptor = (isStatic ?
  13.         new StaticUnadvisedExposedInterceptor(this.advised.getTargetSource().getTarget()) :
  14.         new DynamicUnadvisedExposedInterceptor(this.advised.getTargetSource()));
  15.   }
  16.   else {
  17.     targetInterceptor = (isStatic ?
  18.         new StaticUnadvisedInterceptor(this.advised.getTargetSource().getTarget()) :
  19.         new DynamicUnadvisedInterceptor(this.advised.getTargetSource()));
  20.   }
  21.   // Choose a "direct to target" dispatcher (used for
  22.   // unadvised calls to static targets that cannot return this).
  23.   Callback targetDispatcher = (isStatic ?
  24.       new StaticDispatcher(this.advised.getTargetSource().getTarget()) : new SerializableNoOp());
  25.   Callback[] mainCallbacks = new Callback[] {
  26.       aopInterceptor,  //
  27.       targetInterceptor,  // invoke target without considering advice, if optimized
  28.       new SerializableNoOp(),  // no override for methods mapped to this
  29.       targetDispatcher, this.advisedDispatcher,
  30.       new EqualsInterceptor(this.advised),
  31.       new HashCodeInterceptor(this.advised)
  32.   };
  33.   Callback[] callbacks;
  34.   // If the target is a static one and the advice chain is frozen,
  35.   // then we can make some optimizations by sending the AOP calls
  36.   // direct to the target using the fixed chain for that method.
  37.   if (isStatic && isFrozen) {
  38.     Method[] methods = rootClass.getMethods();
  39.     Callback[] fixedCallbacks = new Callback[methods.length];
  40.     this.fixedInterceptorMap = CollectionUtils.newHashMap(methods.length);
  41.     // TODO: small memory optimization here (can skip creation for methods with no advice)
  42.     for (int x = 0; x < methods.length; x++) {
  43.       Method method = methods[x];
  44.       List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, rootClass);
  45.       fixedCallbacks[x] = new FixedChainStaticTargetInterceptor(
  46.           chain, this.advised.getTargetSource().getTarget(), this.advised.getTargetClass());
  47.       this.fixedInterceptorMap.put(method, x);
  48.     }
  49.     // Now copy both the callbacks from mainCallbacks
  50.     // and fixedCallbacks into the callbacks array.
  51.     callbacks = new Callback[mainCallbacks.length + fixedCallbacks.length];
  52.     System.arraycopy(mainCallbacks, 0, callbacks, 0, mainCallbacks.length);
  53.     System.arraycopy(fixedCallbacks, 0, callbacks, mainCallbacks.length, fixedCallbacks.length);
  54.     this.fixedInterceptorOffset = mainCallbacks.length;
  55.   }
  56.   else {
  57.     callbacks = mainCallbacks;
  58.   }
  59.   return callbacks;
  60. }
复制代码
可以结合调试,方便明白

AOP在嵌套方法调用时不生效

在一个实现类中,有2个方法,方法A,方法B,其中方法B上面有个注解切面,当方法B被外部调用的时间,会进入切面方法。
但当方法B是被方法A调用时,并不能从方法B的注解上,进入到切面方法,即我们经常碰到的方法嵌套时,AOP注解不生效的标题。
案例

外部调用AOP方法正常进入

通过外部,调用方法B,可以正常进入切面方法,这个场景的代码如下:

  1. @Target({ElementType.METHOD, ElementType.TYPE})
  2. @Retention(RetentionPolicy.RUNTIME)
  3. public @interface DemoAnno {
  4. }
复制代码

  1. @Aspect
  2. @Order(-1)
  3. @Component
  4. public class DemoAspect {
  5.     @Before("@annotation(da)")
  6.     public void beforDoSomething(JoinPoint point, DemoAnno da) throws Exception {
  7.         System.out.println("before method B, print 'hello,world' " );
  8.     }
  9. }
复制代码

  1. public interface DemoService {
  2.     void methodDemoA();
  3.     void methodDemoB();
  4. }
复制代码

  1. @Service
  2. public class DemoServiceImpl implements DemoService {
  3.     @Override
  4.     public void methodDemoA(){
  5.         System.out.println("this is method A");
  6.     }
  7.     @Override
  8.     @DemoAnno
  9.     public void methodDemoB() {
  10.         System.out.println("this is method B");
  11.     }
  12. }
复制代码

  1. @Autowired
  2. DemoService demoService;
  3. @Test
  4. public void testMethod(){
  5.     demoService.methodDemoA();
  6.     demoService.methodDemoB();
  7. }
复制代码
输出效果:
  1. this is method A
  2. before method B, print 'hello,world'
  3. this is method B
复制代码
方法嵌套调用,AOP不生效

上面的代码,做下修改。在DemoServiceImpl实现类中,通过方法A去调用方法B,然后再单元测试类中,调用方法A。代码修改后如下:

  1. @Service
  2. public class DemoServiceImpl implements DemoService {
  3.     @Override
  4.     public void methodDemoA(){
  5.         System.out.println("this is method A");
  6.         methodDemoB();
  7.     }
  8.     @Override
  9.     @DemoAnno
  10.     public void methodDemoB() {
  11.         System.out.println("this is method B");
  12.     }
  13. }
复制代码

  1. this is method A
  2. this is method B
复制代码
原因分析

场景1中,通过外部调用方法B,是由于spring在启动时,根据切面类及注解,生成了DemoService的署理类,在调用方法B时,现实上是署理类先对目的方法进行了业务加强处置处罚(执行切面类中的业务逻辑),然后再调用方法B本身。以是场景1可以正常进入切面方法;
场景2中,通过外部调用的是方法A,虽然spring也会创建一个cglib的署理类去调用方法A,但当方法A调用方法B的时间,属于类里面的内部调用,利用的是实例对象本身去去调用方法B,非aop的cglib署理对象调用,方法B天然就不会进入到切面方法了。
解决方案

但现实上我们期望的是,方法A在调用方法B的时间,仍然可以大概进入切面方法,即须要AOP切面生效。这种环境下,在调用方法B的时间,须要利用AopContext.currentProxy()获取当前的署理对象,然后利用署理对象调用方法B。
   注:须要开启 exposeProxy=true 的配置,springboot项目中,可以在启动类上面,添加 @EnableAspectJAutoProxy(exposeProxy = true)注解。
  1. @Service
  2. public class DemoServiceImpl implements DemoService {
  3.     @Override
  4.     public void methodDemoA(){
  5.         System.out.println("this is method A");
  6.         DemoService service = (DemoService) AopContext.currentProxy();
  7.         service.methodDemoB();
  8.     }
  9.     @Override
  10.     @DemoAnno
  11.     public void methodDemoB() {
  12.         System.out.println("this is method B");
  13.     }
  14. }
复制代码
面试题专栏

Java面试题专栏已上线,欢迎访问。

那么可以私信我,我会尽我所能帮助你。

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4