Spring Aop详解

24 篇文章 17 订阅
22 篇文章 11 订阅

什么AOP

在软件业,AOP为Aspect Oriented Programming的缩写,意为:面向切面编程,通过预编译方式和运行期间动态代理实现程序功能的统一维护的一种技术。AOP是OOP的延续,是软件开发中的一个热点,也是Spring框架中的一个重要内容,是函数式编程的一种衍生范型。利用AOP可以对业务逻辑的各个部分进行隔离,从而使得业务逻辑各部分之间的耦合度降低,提高程序的可重用性,同时提高了开发的效率。
Aspect Oriented Programming(AOP)是较为热门的一个话题。AOP,国内大致译作“面向切面编程”。“面向切面编程”,这样的名字并不是非常容易理解,且容易产生一些误导。有些人认为“OOP/OOD11即将落伍,AOP是新一代软件开发方式”。显然,发言者并没有理解AOP的含义。Aspect,的确是“方面”的意思。不过,汉语传统语义中的“方面”,大多数情况下指的是一件事情的不同维度、或者说不同角度上的特性,比如我们常说:“这件事情要从几个方面来看待”,往往意思是:需要从不同的角度来看待同一个事物。这里的“方面”,指的是事物的外在特性在不同观察角度下的体现。而在AOP中,Aspect的含义,可能更多的理解为“切面”比较合适。
可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。AOP实际是GoF设计模式的延续,设计模式孜孜不倦追求的是调用者和被调用者之间的解耦,提高代码的灵活性和可扩展性,AOP可以说也是这种目标的一种实现。
在Spring中提供了面向切面编程的丰富支持,允许通过分离应用的业务逻辑与系统级服务(例如审计(auditing)和事务(transaction)管理)进行内聚性的开发。应用对象只实现它们应该做的——完成业务逻辑——仅此而已。它们并不负责(甚至是意识)其它的系统级关注点,例如日志或事务支持。

Spring AOP

Spring框架中对Aop的实现可以说是实现的淋漓尽致,在spring的架构中有一个代理工厂是对AOP的所有解释,ProxyFactory,在ProxyFactory中集成了JDK动态代理和CGLIB代理,而spring在实现的过程中,如果针对于接口的代理,spring使用的是JDK的动态代理,而针对于类的代理,使用的是CGLIB代理,但是这也不是绝对的,spring对其有一定的逻辑判断,比如一个类,有接口的实现,也可以使用JDK的动态代理也可以使用CGLIB代理,spring基于ProxyFactory的代理工厂整合了Aop的所有概念,在spring中,自动代理功能可以有几种方式进行实现,但是不管哪种方式的实现自动代理都是采用了spring的BeanPostProcessor来实现的,比如我们常见的方式@EnableAspectJAutoProxy,这个注解的意思就是开启自动代理的意思,其实这个注解就是利用了spring的扩展点@Import导入一个Registr,然后这个Registr注册一个BeanDefinition,而这个BeanDefinition是一个bean的后置处理器,而我们知道bean的后置处理器会在bean的实例化和初始化前后进行调用,而spring的aop是在bean出现循环依赖可以提前生成代理对象或者在bean的初始化的最后一个阶段进行代理。
所以spring 的aop的实现原理就是利用了spring的扩展注册一个bean的后置处理器,在bean的生命周期的最后一步进行代理,前提是你的bean是切点上;通过@EnableAspectJAutoProxy进行自动代理的其实处理的是手动添加的advisor和加了@Aspject的bean,这里我们大概理解下spring的aop的几个概念,分别是PonintCut、Advice、Advisor;
Pointcut:切点,意思就是满足这个切点的方法都会被拦截执行相应的比如执行方法前、执行方法后等的代码逻辑;
Advice:这个网上大部分的博客都叫做通知,其实我把它理解为建议的逻辑,也就是说是具体要执行的逻辑,比如加了@Before的方法,这个方法就是具体要执行的逻辑,也就是说一段代码逻辑,在符合切点的基础上执行的代码逻辑;
Advisor:这是一个完整的切面,我把它叫做为一个完整的切面,一个完整的切面包括了切点和切点的逻辑,所以它就等于PointCut+Advice,什么意思呢?就是说

@Before(execution(public int com.spring.aop.xxx.*(..)))
public void xxxxss(JoinPoint joinpoint) {
		........
	}

上面的这个@Before加上方法就是一个advisor,就是一个完整的切面,我是这样理解的。

ProxyFactory

示例分析

在spring中,spring对实现了一套完整的代理方案,就是通过ProxyFactory构建的,spring中的自动代理不管是使用哪种方式都是在创建代理对象的时候创建了一个ProxyFactory,ProxyFactory整合了JDK的动态代理和CGLIB的代理,只是它对这两个进行了整合,根据不同的情况选择不同的代理模式,这里我们先来看下如果单独使用这个代理工厂怎么使用,然后了解下这个代理工厂的原理。

package com.example.test;

public class UserService {


   public void test(){
      System.out.println("test....");
   }

}
public class Test {

   public static void main(String[] args) {
      ProxyFactory proxyFactory = new ProxyFactory();
      proxyFactory.setTarget(new UserService());
      proxyFactory.addAdvice(new MethodBeforeAdvice() {
         @Override
         public void before(Method method, Object[] args, Object target) throws Throwable {
            System.out.println("方法执行前...");
         }
      });

      UserService proxy = (UserService)proxyFactory.getProxy();
      proxy.test();

   }
}

这上面就是ProxyFactory的一个简单例子,通过添加一个Advice建议逻辑,在Advice是添加的方法执行前进行拦截,但是上面我说说过一个完整的切面是包括了切点+Advice的,但是上面我们只添加了一个Advice就可以拦截了吗?其实不是,像上面的这种写法是添加了一个Advice,但是默认是拦截了所有的方法的,我们看下源码就知道了
在这里插入图片描述
在构建代理对象的时候切点就是默认为true,就是全部的方法都拦截;
但是这个方式它使用的是JDK的动态代理还是CGLIB代理,我们通过debug一看就知道了
在这里插入图片描述
所以这种使用的是CGLIB代理,那么我们修改下代码增加一个接口

public static void main(String[] args) {
   ProxyFactory proxyFactory = new ProxyFactory();
   proxyFactory.setTarget(new UserService());
   proxyFactory.addInterface(UserInterface.class);
   proxyFactory.addAdvice(new MethodBeforeAdvice() {
      @Override
      public void before(Method method, Object[] args, Object target) throws Throwable {
         System.out.println("方法执行前...");
      }
   });

   UserInterface proxy = (UserInterface)proxyFactory.getProxy();
   proxy.test();

}

在这里插入图片描述
这个时候使用的就是JDK的动态代理;我们知道JDK的动态代理是可以添加很多接口的,如果你添加了很多接口,那么你通过proxyFactory.getProxy()可以转化为不同的接口对象,调用不同接口中的方法。

源码分析

在这里插入图片描述
ProxyFactory实现了Advised的,所以一个ProxyFactory也是一个Advised
ProxyFactory里面太多了方法和属性了,我这里挑几个来简单说明下


/**
 * Factory for AOP proxies for programmatic use, rather than via declarative
 * setup in a bean factory. This class provides a simple way of obtaining
 * and configuring AOP proxy instances in custom user code.
 *
 * @author Rod Johnson
 * @author Juergen Hoeller
 * @author Rob Harrop
 * @since 14.03.2003
 * ProxyFactory是spring的代理工厂,spring中的代理工厂支持两种代理
 * JDK的动态代理和CGLIB代理,当代理的对象是接口的时候,使用JDK动态代理
 * 当是类对象的时候,采用CGLIB代理
 */
@SuppressWarnings("serial")
public class ProxyFactory extends ProxyCreatorSupport {

   /**
    * Create a new ProxyFactory.
    */
   public ProxyFactory() {
   }

   /**
    * Create a new ProxyFactory.
    * <p>Will proxy all interfaces that the given target implements.
    * @param target the target object to be proxied
    *
    */
   //target是代理的目标对象,这个模式下,一般target是一个类的对象,
   //如果没有添加Interface,那么一般使用的是CGLIB代理
   public ProxyFactory(Object target) {
      setTarget(target);
      //但是当你的目标对象有实现了一些借口,那么这些接口也要添加到接口里面里面
      setInterfaces(ClassUtils.getAllInterfaces(target));
   }
   public Object getProxy() {
   //得到一个代理,通过默认的代理工厂得到一个代理
   //假如你使用的是JDK代理,那么你可以添加几个接口,那么代理的对象可以是任何的接口类型
   //就是可以访问不同的接口中的方法
   return createAopProxy().getProxy();
}

createAopProxy().getProxy();就是得到一个代理对象,选择JDK动态代理还是CGLIB代理,就是在这个方法中进行判断选择的

创建代理工厂

在ProxyFactory中的父类ProxyCreatorSupport中调用的

protected final synchronized AopProxy createAopProxy() {
   if (!this.active) {
      activate();
   }
   return getAopProxyFactory().createAopProxy(this);
}
public class ProxyCreatorSupport extends AdvisedSupport {

   private AopProxyFactory aopProxyFactory;

   private final List<AdvisedSupportListener> listeners = new LinkedList<>();

   /** Set to true when the first AOP proxy has been created. */
   private boolean active = false;


   /**
    * Create a new ProxyCreatorSupport instance.
    */
   public ProxyCreatorSupport() {
      this.aopProxyFactory = new DefaultAopProxyFactory();
   }

其中获取了一个代理工厂,也是唯一的一个代理工厂DefaultAopProxyFactory,获取一个DefaultAopProxyFactory就开始创建一个AOP的代理createAopProxy

createAopProxy

/**
 * 创建一个aop代理 config就是一个ProxyFactory对象
 * 首先判断你没有没有设置isOptimize和isProxyTargetClass
 * 默认都是false,但是你可以设置为true,设置为true的情况下
 * 就会进入if的条件,在if中也不是说就一定是CGLIB的代理,
 * 而是如果你的目标class是接口,那么就使用JDK的动态代理
 * 否则使用CGLIB的代理
 * if的第三个条件就是看你是否添加了接口,如果你添加了接口,那么返回false,直接使用jdk的动态代理
 *
 * @param config the AOP configuration in the form of an
 * AdvisedSupport object
 * @return
 * @throws AopConfigException
 */
@Override
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
   if (config.isOptimize() || config.isProxyTargetClass() || hasNoUserSuppliedProxyInterfaces(config)) {
      Class<?> targetClass = config.getTargetClass();
      if (targetClass == null) {
         throw new AopConfigException("TargetSource cannot determine target class: " +
               "Either an interface or a target is required for proxy creation.");
      }
      if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
         return new JdkDynamicAopProxy(config);
      }
      return new ObjenesisCglibAopProxy(config);
   }
   else {
      return new JdkDynamicAopProxy(config);
   }
}

上面的if条件中的有两个参数isOptimize和isProxyTargetClass是可以设置的,如果使用的是ProxyFactory的话,那么可以自己设置的参数,通过proxyFactory.setOptimize(true);
proxyFactory.setProxyTargetClass(true);,但是就算设置了也不一定就使用CGLIB,如果代理的目标class是接口,那么还是会使用JDK的动态代理。

getProxy()


JDK动态代理:
@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
   if (logger.isTraceEnabled()) {
      logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
   }
   //获取代理的接口列表
   Class<?>[] proxiedInterfaces = AopProxyUtils.completeProxiedInterfaces(this.advised, true);
   //验证
   findDefinedEqualsAndHashCodeMethods(proxiedInterfaces);
   //生成代理对象
   return Proxy.newProxyInstance(classLoader, proxiedInterfaces, this);
}

CGLIB代理:
@Override
public Object getProxy() {
   return getProxy(null);
}

@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
   if (logger.isTraceEnabled()) {
      logger.trace("Creating CGLIB proxy: " + this.advised.getTargetSource());
   }

   try {
      Class<?> rootClass = this.advised.getTargetClass();
      Assert.state(rootClass != null, "Target class must be available for creating a CGLIB proxy");

      Class<?> proxySuperClass = rootClass;
      if (rootClass.getName().contains(ClassUtils.CGLIB_CLASS_SEPARATOR)) {
         proxySuperClass = rootClass.getSuperclass();
         Class<?>[] additionalInterfaces = rootClass.getInterfaces();
         for (Class<?> additionalInterface : additionalInterfaces) {
            this.advised.addInterface(additionalInterface);
         }
      }

      // Validate the class, writing log messages as necessary.
      validateClassIfNecessary(proxySuperClass, classLoader);

      // Configure CGLIB Enhancer...
      Enhancer enhancer = createEnhancer();
      if (classLoader != null) {
         enhancer.setClassLoader(classLoader);
         if (classLoader instanceof SmartClassLoader &&
               ((SmartClassLoader) classLoader).isClassReloadable(proxySuperClass)) {
            enhancer.setUseCache(false);
         }
      }
      enhancer.setSuperclass(proxySuperClass);
      enhancer.setInterfaces(AopProxyUtils.completeProxiedInterfaces(this.advised));
      enhancer.setNamingPolicy(SpringNamingPolicy.INSTANCE);
      enhancer.setStrategy(new ClassLoaderAwareGeneratorStrategy(classLoader));

      Callback[] callbacks = getCallbacks(rootClass);
      Class<?>[] types = new Class<?>[callbacks.length];
      for (int x = 0; x < types.length; x++) {
         types[x] = callbacks[x].getClass();
      }
      // fixedInterceptorMap only populated at this point, after getCallbacks call above
      enhancer.setCallbackFilter(new ProxyCallbackFilter(
            this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset));
      enhancer.setCallbackTypes(types);

      // Generate the proxy class and create a proxy instance.
      return createProxyClassAndInstance(enhancer, callbacks);
   }
   catch (CodeGenerationException | IllegalArgumentException ex) {
      throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
            ": Common causes of this problem include using a final class or a non-visible class",
            ex);
   }
   catch (Throwable ex) {
      // TargetSource.getTarget() failed
      throw new AopConfigException("Unexpected AOP exception", ex);
   }
}

我们知道如果是JDK的动态代理,那么JdkDynamicAopProxy肯定会实现InvokeHandler类,并且需要实现了方法Invoke,所以如果使用的是JDK的动态代理,那么会调用里面的invoke方法

JDK动态代理的调用

/**
    * 代理对象真正调用的地方
    * Implementation of {@code InvocationHandler.invoke}.
    * <p>Callers will see exactly the exception thrown by the target,
    * unless a hook method throws an exception.
    */
   @Override
   @Nullable
   public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      Object oldProxy = null;
      boolean setProxyContext = false;

      TargetSource targetSource = this.advised.targetSource;
      Object target = null;

      try {
         //当创建代理对象的时候,如果不是equal方法,但是这里又重新去判断了如果是equals方法,就调用
         if (!this.equalsDefined && AopUtils.isEqualsMethod(method)) {
            // The target does not implement the equals(Object) method itself.
            return equals(args[0]);
         }
         else if (!this.hashCodeDefined && AopUtils.isHashCodeMethod(method)) {
            // The target does not implement the hashCode() method itself.
            return hashCode();
         }
         else if (method.getDeclaringClass() == DecoratingProxy.class) {
            // There is only getDecoratedClass() declared -> dispatch to proxy config.
            return AopProxyUtils.ultimateTargetClass(this.advised);
         }
         else 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;

         //exposeProxy是一个参数,开发者可以设置的,比如说我们通过注解
//@EnableAspectJAutoProxy可以设置它的参数,代表什么意思呢?就是说可以将你当前的代理对象
         //设置到当前的线程缓存中,在本线程中共享这个代理对象,类似于在本线程中
         //可通过AopContext.currentProxy()得到一个本线程的代理对象,可以转换成
         //你的代理类,比如UserService,去调用本对象的其他方法

         if (this.advised.exposeProxy) {
            // Make invocation available if necessary.
            oldProxy = AopContext.setCurrentProxy(proxy);

            setProxyContext = true;
         }

         // Get as late as possible to minimize the time we "own" the target,
         // in case it comes from a pool.
         //获取目标对象,就是代理的目标对象
         target = targetSource.getTarget();
         Class<?> targetClass = (target != null ? target.getClass() : null);

         // Get the interception chain for this method.
         //获取advised的调用链
         /**
          * 什么意思呢?我们知道spring中的有几个概念,ponincut,advice、advise
          * 其中ponincut是切点,advice是通知,也就是业务逻辑,advise就是一个
          * 完整的切面,所以说如果一个aop就表示为advise=ponincut+advice
          * ponincut表示切点在那里,advice表示匹配到了切点要怎么调用,也就是业务逻辑
          * 所以完整一个切面及时advise=ponincut+advice
          * 所以这里是得到所有的完整切面advise列表
          */
         List<Object> chain = this.advised.getInterceptorsAndDynamicInterceptionAdvice(method, targetClass);

         // Check whether we have any advice. If we don't, we can fallback on direct
         // reflective invocation of the target, and avoid creating a MethodInvocation.
         if (chain.isEmpty()) {
            // 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 = AopUtils.invokeJoinpointUsingReflection(target, method, argsToUse);
         }
         else {
            // We need to create a method invocation...
            //把代理的一些参数,比如目标对象,代理对象,切面的链封装成一个方法调用对象MethodInvocation
            MethodInvocation invocation =
                  new ReflectiveMethodInvocation(proxy, target, method, args, targetClass, chain);
            // Proceed to the joinpoint through the interceptor chain.
            //对拦截器调用链进行调用
            retVal = invocation.proceed();
         }

         // Massage return value if necessary.
         Class<?> returnType = method.getReturnType();
         if (retVal != null && retVal == target &&
               returnType != Object.class && returnType.isInstance(proxy) &&
               !RawTargetAccess.class.isAssignableFrom(method.getDeclaringClass())) {
            // Special case: it returned "this" and the return type of the method
            // is type-compatible. Note that we can't help if the target sets
            // a reference to itself in another returned object.
            retVal = proxy;
         }
         else if (retVal == null && returnType != Void.TYPE && returnType.isPrimitive()) {
            throw new AopInvocationException(
                  "Null return value from advice does not match primitive return type for: " + method);
         }
         return retVal;
      }
      finally {
         if (target != null && !targetSource.isStatic()) {
            // Must have come from TargetSource.
            targetSource.releaseTarget(target);
         }
         if (setProxyContext) {
            // Restore old proxy.
            AopContext.setCurrentProxy(oldProxy);
         }
      }
   }

1.上面的方法有个比较值得说的地方就是exposeProxy这个参数的用法, 默认是false,但是我们可以设置为true,设置为true有什么作用呢?我们知道我没调用方法是在一个线程内完成的,所以这个参数为true的话,那么spring就会在当前的线程内设置为当前的代理对象到ThreadLocal中,那么我们在调用代理对象的其他方法也会走代理的逻辑,而不是方法的调用,比如说:
在这里插入图片描述
在这里插入图片描述
上面的运行效果只对test方法进行拦截,而test2就没有拦截,因为在方法里面的调用,没有走代理的逻辑,但是如果启用了exposeProxy的话,这样来写
在这里插入图片描述
说白了就是在启用了exposeProxy的话,那么在调用代理对象的时候会在ThreadLocal中存入当前调用的代理对象,然后通过AopContext.cuurentProxy()就可以得到一个相同的代理对象,也就是可以调用了。
2.invocation.proceed();这个就是spring的aop获取了调用链,也就是advisor列表,然后进行调用

/**
 * 下面的这个方法就是调用的逻辑
 * 首先从调用链中依次得到每个调用链,然后根据类型去调用,比如是方法前的调用,方法后的调用
 * 执行结果返回的调用,异常的调用,所以是一个递归的调用,每次调用完成过后又来调用processed方法
 * 最后如果都调用完成了就开始调用我们的业务层的方法
 * @return
 * @throws Throwable
 */
@Override
@Nullable
public Object proceed() throws Throwable {
   // We start with an index of -1 and increment early.
   /**
    * 这里的这个判断的意思就是说我们在给代理方法调用的时候
    * 这个方法的切面可能有多个,就是前面获取的调用链,这些切点的方法都调用完毕了以后
    * 那么就代表我们的方法可以被调用了,所以invokeJoinpoint()就是调用我们真正的业务层的方法
    * 所以这个方法的逻辑就是根据不同的类型进行调用,就是说
    * 有方法前 的调用,方法后的调用,返回结果的调用,就是通过这个方法根据切面的类型完成一连串的调用
    */
   if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
      return invokeJoinpoint();
   }

   Object interceptorOrInterceptionAdvice =
         this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);
   if (interceptorOrInterceptionAdvice instanceof InterceptorAndDynamicMethodMatcher) {
      // Evaluate dynamic method matcher here: static part will already have
      // been evaluated and found to match.
      InterceptorAndDynamicMethodMatcher dm =
            (InterceptorAndDynamicMethodMatcher) interceptorOrInterceptionAdvice;
      Class<?> targetClass = (this.targetClass != null ? this.targetClass : this.method.getDeclaringClass());
      if (dm.methodMatcher.matches(this.method, targetClass, this.arguments)) {
         return dm.interceptor.invoke(this);
      }
      else {
         // Dynamic matching failed.
         // Skip this interceptor and invoke the next in the chain.
         return proceed();
      }
   }
   else {
      // It's an interceptor, so we just invoke it: The pointcut will have
      // been evaluated statically before this object was constructed.
      return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
   }
}

((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);这里是方法拦截器的调用,有方法之前前的调用,方法执行后的调用等。

JdkDynamicAopProxy创建的代理对象执行过程

1.如果通过ProxyFactory.setExposeProxy()把exposeProxy设置为了true,那么则把代理对象设置到一个ThreadLocal(currentProxy)中去。

2.获取通过ProxyFactory所设置的target,如果设置的是targetClass,那么target将为null

3.根据当前所调用的方法对象寻找ProxyFactory中所添加的并匹配的Advisor,并且把Advisor封装为MethodInterceptor返回,得到MethodInterceptor链叫做chain

4.如果chain为空,则直接执行target对应的当前方法,如果target为null会报错

5.如果chain不为空,则会依次执行chain中的MethodInterceptor
a.如果当前MethodInterceptor是MethodBeforeAdviceInterceptor,那么则先执行
Advisor中所advice的before()方法,然后执行下一个MethodInterceptor
b.如果当前MethodInterceptor是AfterReturningAdviceInterceptor,那么则先执行下一个MethodInterceptor,拿到返回值之后,再执行Advisor中所advice的afterReturning()方法。

CGLIB的调用过程和JDK差不多,就是创建代理的过程有点不一致,有兴趣可以下来去研究下。

spring的自动代理功能

spring的自动代理功能有三种的方式,其实不管哪种方式都是往spring容器中添加一个bean的后置处理器,然后在bean初始化后的后置处理器中进行调用的

BeanNameAutoProxyCreator

@ComponentScan("com.example")
public class AppConfig {

   @Bean
   public BeanNameAutoProxyCreator beanNameAutoProxyCreator(){
      BeanNameAutoProxyCreator creator = new BeanNameAutoProxyCreator();
      creator.setBeanNames("userService");
      creator.setInterceptorNames("myAdvisor");
      return creator;
   }
}

BeanNameAutoProxyCreator 向spring中添加一个bean后置处理器,添加代理,设置beanNames,当初始化调用后置处理器如果存在beanNames的就开启生成代理对象,生成代理对象的条件是切点和advice,所以我们可以手动添加一个advisor


@Component
public class MyAdvisor implements PointcutAdvisor {
   @Override
   public Pointcut getPointcut() {
      NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
      pointcut.addMethodName("test");
      return pointcut;
   }

   @Override
   public Advice getAdvice() {
      return new MethodBeforeAdvice() {
         @Override
         public void before(Method method, Object[] args, Object target) throws Throwable {
            System.out.println("方法执行前");
         }
      };
   }
   .......

这个我手动添加的advisor,由poincut和advice组成,我这个切点的意思就是只要有test方法都拦截,执行如下:
在这里插入图片描述
说明是成功的,下面我们就来分析下这个自动代理的底层实现逻辑
源码分析
BeanNameAutoProxyCreator 是继承了AbstractAutoProxyCreator,AbstractAutoProxyCreator是继承了ProxyProcessorSupport和BeanPostProcessor的,所以BeanNameAutoProxyCreator 是一个bean的后置处理器,代理的逻辑是在AbstractAutoProxyCreator的初始化后的方法里面调用的

/**
 * Create a proxy with the configured interceptors if the bean is
 * identified as one to proxy by the subclass.
 * @see #getAdvicesAndAdvisorsForBean
 * 这个是spring的初始化后的bean后置处理器
 * 当一个bean执行到这里的时候,则bean的生命周期已经到了最后一步了,这一步就是判断
 * 你的bean是否开启了aop,如果时候系统中有了这个后置处理器,那么证明是开启了自动代理的功能的,
 * 之前的笔记中写了spring的循环依赖,如果你开启了aop,并且你的这个bean又是符合advise的切面逻辑的
 * 那么这里就会开启自动代理的功能,就是为你的bean创建一个代理对象
 * spring在循环依赖中,如果出现了循环依赖,而这个时候bean又是符合切面的逻辑的,那么在解决循环依赖的过程中
 * 就会创建aop代理,并且将创建的aop代理对象放入二级缓存中
 * earlyProxyReferences就是在bean的实例化过程中出现的循环依赖而添加到二级缓存中的bean代理对象
 * 如果说某个bean没有出现循环依赖,那么earlyProxyReferences这里面是不会有值的
 */
@Override
public Object postProcessAfterInitialization(@Nullable Object bean, String beanName) {
   if (bean != null) {
      Object cacheKey = getCacheKey(bean.getClass(), beanName);
      //排除在循环依赖阶段添加的bean代理
      if (this.earlyProxyReferences.remove(cacheKey) != bean) {
         return wrapIfNecessary(bean, beanName, cacheKey);
      }
   }
   return bean;
}

/**
 * Wrap the given bean if necessary, i.e. if it is eligible for being proxied.
 * @param bean the raw bean instance
 * @param beanName the name of the bean
 * @param cacheKey the cache key for metadata access
 * @return a proxy wrapping the bean, or the raw bean instance as-is
 * 这个方法就是来判断是否开启代理,如果需要就返回代理对象
 */
protected Object wrapIfNecessary(Object bean, String beanName, Object cacheKey) {
   //如果说已经存在targetSourcedBeans中的,就直接返回对象,targetSourcedBeans感觉是已经是一个代理对象的列表中的一员了
   if (StringUtils.hasLength(beanName) && this.targetSourcedBeans.contains(beanName)) {
      return bean;
   }
   /**
    * advisedBeans中存入就是每个bean是否要开启代理的一个集合,false表示不开启代理,true表示要开启代理
    */
   if (Boolean.FALSE.equals(this.advisedBeans.get(cacheKey))) {
      return bean;
   }
   /**
    * 这里的判断是判断bean是否是Advisor、PoinCut类型的bean,如果是就直接返回
    * 还有这个bea是否是要跳过,这个是是否要跳过是留子类是可以去扩展的,默认是不跳过
    */
   if (isInfrastructureClass(bean.getClass()) || shouldSkip(bean.getClass(), beanName)) {
      //当满足上面两个条件就表示不开启代理,设置一个false
      this.advisedBeans.put(cacheKey, Boolean.FALSE);
      return bean;
   }

   // Create proxy if we have advice.
   Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);
   if (specificInterceptors != DO_NOT_PROXY) {
      this.advisedBeans.put(cacheKey, Boolean.TRUE);
      Object proxy = createProxy(
            bean.getClass(), beanName, specificInterceptors, new SingletonTargetSource(bean));
      this.proxyTypes.put(cacheKey, proxy.getClass());
      return proxy;
   }

   this.advisedBeans.put(cacheKey, Boolean.FALSE);
   return bean;
}

上面的代码都是父类的aop创建代理对象的父类的逻辑,而获取代理的Advisor是在不同的子类中实现的,也就是getAdvicesAndAdvisorsForBean方法,现在我们这里说的是BeanNameAutoProxyCreator ,所以这里我们看BeanNameAutoProxyCreator 中的实现的方法getAdvicesAndAdvisorsForBean

/**
 * Set the names of the beans that should automatically get wrapped with proxies.
 * A name can specify a prefix to match by ending with "*", e.g. "myBean,tx*"
 * will match the bean named "myBean" and all beans whose name start with "tx".
 * <p><b>NOTE:</b> In case of a FactoryBean, only the objects created by the
 * FactoryBean will get proxied. This default behavior applies as of Spring 2.0.
 * If you intend to proxy a FactoryBean instance itself (a rare use case, but
 * Spring 1.2's default behavior), specify the bean name of the FactoryBean
 * including the factory-bean prefix "&": e.g. "&myFactoryBean".
 * @see org.springframework.beans.factory.FactoryBean
 * @see org.springframework.beans.factory.BeanFactory#FACTORY_BEAN_PREFIX
 * 这里就是通过@Bean中创建的BeanNameAutoProxyCreator设置的要代理的beanNames
 * 是一个数组,可以添加多个
 */
public void setBeanNames(String... beanNames) {
   Assert.notEmpty(beanNames, "'beanNames' must not be empty");
   this.beanNames = new ArrayList<>(beanNames.length);
   for (String mappedName : beanNames) {
      this.beanNames.add(StringUtils.trimWhitespace(mappedName));
   }
}

protected Object[] getAdvicesAndAdvisorsForBean(
      Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {

   if (this.beanNames != null) {
      //循环所有的beanNemes,看当前实例化的beanName是否在设置的代理的beanNames中
      //如果在,就直接返回一个Object[0],如果没有就返回一个null
      for (String mappedName : this.beanNames) {
         if (FactoryBean.class.isAssignableFrom(beanClass)) {
            if (!mappedName.startsWith(BeanFactory.FACTORY_BEAN_PREFIX)) {
               continue;
            }
            mappedName = mappedName.substring(BeanFactory.FACTORY_BEAN_PREFIX.length());
         }
         if (isMatch(beanName, mappedName)) {
            return PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS;
         }
         BeanFactory beanFactory = getBeanFactory();
         if (beanFactory != null) {
            String[] aliases = beanFactory.getAliases(beanName);
            for (String alias : aliases) {
               if (isMatch(alias, mappedName)) {
                  return PROXY_WITHOUT_ADDITIONAL_INTERCEPTORS;
               }
            }
         }
      }
   }
   return DO_NOT_PROXY;
}

createProxy

/**
 * Create an AOP proxy for the given bean.
 * @param beanClass the class of the bean
 * @param beanName the name of the bean
 * @param specificInterceptors the set of interceptors that is
 * specific to this bean (may be empty, but not null)
 * @param targetSource the TargetSource for the proxy,
 * already pre-configured to access the bean
 * @return the AOP proxy for the bean
 * @see #buildAdvisors
 * 通过ProxyFactory创建一个代理对象
 * 1.创建一个代理工厂ProxyFacatory
 * 2.设置beanclass,就是当前beanName对应的class设置为目标对象的class;
 * 3.找到这个bean锁对应的所有advisor;
 * 4.将找到的advisor列表添加到代理工厂对象中
 * 5.根据代理的模式选择JDK动态代理或者CGLIB代理创建一个代理对象。
 */
protected Object createProxy(Class<?> beanClass, @Nullable String beanName,
      @Nullable Object[] specificInterceptors, TargetSource targetSource) {

   if (this.beanFactory instanceof ConfigurableListableBeanFactory) {
      AutoProxyUtils.exposeTargetClass((ConfigurableListableBeanFactory) this.beanFactory, beanName, beanClass);
   }

   //通过ProxyFactory创建一个代理对象

   ProxyFactory proxyFactory = new ProxyFactory();
   //赋值,对象封装
   proxyFactory.copyFrom(this);

   if (!proxyFactory.isProxyTargetClass()) {
      if (shouldProxyTargetClass(beanClass, beanName)) {
         proxyFactory.setProxyTargetClass(true);
      }
      else {
         evaluateProxyInterfaces(beanClass, proxyFactory);
      }
   }
   //找到这个beanName所有的切面信息advisor,包括所有的设置开发者自定义的Advisor和通过
   //@Aspject bean设置的@Before 和@After
   Advisor[] advisors = buildAdvisors(beanName, specificInterceptors);
   //将当前beanName的所有切点添加到代理工厂
   proxyFactory.addAdvisors(advisors);
   proxyFactory.setTargetSource(targetSource);
   customizeProxyFactory(proxyFactory);

   proxyFactory.setFrozen(this.freezeProxy);
   if (advisorsPreFiltered()) {
      proxyFactory.setPreFiltered(true);
   }

   //获取一个代理对象
   return proxyFactory.getProxy(getProxyClassLoader());
}
/**
 * Determine the advisors for the given bean, including the specific interceptors
 * as well as the common interceptor, all adapted to the Advisor interface.
 * @param beanName the name of the bean
 * @param specificInterceptors the set of interceptors that is
 * specific to this bean (may be empty, but not null)
 * @return the list of Advisors for the given bean
 * 得到当前beanName所代理的切面信息advisor(poincut+advice)
 */
protected Advisor[] buildAdvisors(@Nullable String beanName, @Nullable Object[] specificInterceptors) {
   // Handle prototypes correctly...
   /**
    * 这个方法resolveInterceptorNames就是得到所有的拦截器advisor
    * 如果使用的是BeanNameAutoProxyCreator,那么这里是根据BeanNameAutoProxyCreator
    * 设置的InterceptorNames去获取到所有的bean,也就是PoincutAdvisor类型的Ban
    * 因为PoincutAdvisor是一个完整的切面,所以这个方法就是获取整个开发者自定义的
    * Advisor
    */
   Advisor[] commonInterceptors = resolveInterceptorNames();

   List<Object> allInterceptors = new ArrayList<>();
   if (specificInterceptors != null) {
      //specificInterceptors不为空的情况下,先把specificInterceptors找到的
      //Advisor添加到拦截器列表中,然后在把开发者实现的Advisor添加到列表中
      //如果使用的@EnableAspjectProxy这个注解的话,那么specificInterceptors这里面
      //就是所有@Aspject中@Before、@After等注解的切面信息(advisor)
      allInterceptors.addAll(Arrays.asList(specificInterceptors));
      if (commonInterceptors.length > 0) {
         if (this.applyCommonInterceptorsFirst) {
            allInterceptors.addAll(0, Arrays.asList(commonInterceptors));
         }
         else {
            allInterceptors.addAll(Arrays.asList(commonInterceptors));
         }
      }
   }
   if (logger.isTraceEnabled()) {
      int nrOfCommonInterceptors = commonInterceptors.length;
      int nrOfSpecificInterceptors = (specificInterceptors != null ? specificInterceptors.length : 0);
      logger.trace("Creating implicit proxy for bean '" + beanName + "' with " + nrOfCommonInterceptors +
            " common interceptors and " + nrOfSpecificInterceptors + " specific interceptors");
   }

   Advisor[] advisors = new Advisor[allInterceptors.size()];
   for (int i = 0; i < allInterceptors.size(); i++) {
      advisors[i] = this.advisorAdapterRegistry.wrap(allInterceptors.get(i));
   }
   return advisors;
}

DefaultAdvisorAutoProxyCreator

DefaultAdvisorAutoProxyCreator这个也是bean的后置处理器,也是在spring的初始化进行调用的,也是进行aop代理的,但是它和BeanNameAutoProxyCreator 有区别的,BeanNameAutoProxyCreator 是需要你设置一个需要代理的beanNames的数组和advisor的beanNames的数组,然后在处理的过程中,spring会根据当前beanName是否设置了代理,如果设置了,那么这个beanName会应用到所有的添加了advisor的切面逻辑里面,如果符合切点的条件,那么会加入到advisor列表中,然后创建代理对象,在代理对象指向目标对象的方法时,会根据代理对象的拦截器也就是advisor进行拦截,比如方法前调用,方法后调用等等。
而DefaultAdvisorAutoProxyCreator这个代理器又比BeanNameAutoProxyCreator 要智能很多,它不会去设置代理器Advisor的名字和要代理的beanName名字,而是启动去扫描,也就是说当前你的bean在创建的最后一步初始化调用后置处理器去创建代理对象的时候,会根据当前bean去找到系统中的所有自定义了advisor的bean,根据切点看当前bean是否满足,如果满足就加如advisor列表,作为当前bean的拦截器,当调用的时候会根据拦截器的类型去拦截相应的方法做处理,使用方法也和BeanNameAutoProxyCreator 一样,向spring容器中添加一个bean,这个bean是一个bean的后置处理器;其中
DefaultAdvisorAutoProxyCreator继承了AbstractAdvisorAutoProxyCreator
AbstractAdvisorAutoProxyCreator继承了AbstractAutoProxyCreator
所以归根到底,还是使用了顶层的父类AbstractAutoProxyCreator来实现了aop代理,而这些子类主要为父类实现aop代理时提供了一些扩展实现,就比如BeanNameAutoProxyCreator 中的getAdvicesAndAdvisorsForBean一样。

示例分析

@Component
public class UserService {


   public void test(){
      System.out.println("test....");
   }
}
@ComponentScan("com.example")
public class AppConfig {

   @Bean
   public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
      return new DefaultAdvisorAutoProxyCreator();
   }
}

@Component
public class MyAdvisor implements PointcutAdvisor {

   @Override
   public Pointcut getPointcut() {
      NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
      pointcut.addMethodName("test");
      return pointcut;
   }

   @Override
   public Advice getAdvice() {
      return new MethodBeforeAdvice() {
         @Override
         public void before(Method method, Object[] args, Object target) throws Throwable {
            System.out.println("方法执行前....");
         }
      };
   }

   @Override
   public boolean isPerInstance() {
      return false;
   }
}

这样开发者自定义的advisor也是会作为一个拦截器注册到容器中,执行结果如下:
在这里插入图片描述
所以也是可以的,可以在开发者自定义的Advisor中写自己的逻辑来实现
源码分析
父类的源码分析在上面已经有了,这里我们就看下需要子类实现的getAdvicesAndAdvisorsForBean方法即可
getAdvicesAndAdvisorsForBean

/**
 * 重写了父类的方法,这个方法就是子类自己需要添加找到的advisor列表
 * 让父类来判断如果当前bean是符合切点的条件的,就加入到一个advisor列表中
 * @param beanClass the class of the bean to advise
 * @param beanName the name of the bean
 * @param targetSource
 * @return
 */
@Override
@Nullable
protected Object[] getAdvicesAndAdvisorsForBean(
      Class<?> beanClass, String beanName, @Nullable TargetSource targetSource) {

   //核心的方法是findEligibleAdvisors

   List<Advisor> advisors = findEligibleAdvisors(beanClass, beanName);
   if (advisors.isEmpty()) {
      return DO_NOT_PROXY;
   }
   return advisors.toArray();
}
protected List<Advisor> findEligibleAdvisors(Class<?> beanClass, String beanName) {
   //从容器中找到所有符合Advisor的bean,然后返回
   /**
    * 1.找到所有type是Advisor的beanname;
    * 2.根据beanname去容器获取bean,如果bean美欧创建,就创建这个bean;
    */
   List<Advisor> candidateAdvisors = findCandidateAdvisors();
   /**
    * 过滤找到的advisor,是要根据当前要创建的bean是满足找到的Advisor的切点条件的就返回,
    * 最终根据当前正在创建的beanname找到的切面advisor列表就是eligibleAdvisors
    * 如果说eligibleAdvisors为空,则返回null,证明这个bean不要aop代理
    */
   List<Advisor> eligibleAdvisors = findAdvisorsThatCanApply(candidateAdvisors, beanClass, beanName);
   extendAdvisors(eligibleAdvisors);
   if (!eligibleAdvisors.isEmpty()) {
      eligibleAdvisors = sortAdvisors(eligibleAdvisors);
   }
   return eligibleAdvisors;
}
/**
 * Find all eligible Advisor beans in the current bean factory,
 * ignoring FactoryBeans and excluding beans that are currently in creation.
 * @return the list of {@link org.springframework.aop.Advisor} beans
 * 这个方法的核心逻辑是就是从容器中找到所有type是Advisor的beanName,然后
 * 从容器中获取并且创建出来
 * @see #isEligibleBean
 */
public List<Advisor> findAdvisorBeans() {
   // Determine list of advisor bean names, if not cached already.
   String[] advisorNames = this.cachedAdvisorBeanNames;
   if (advisorNames == null) {
      // Do not initialize FactoryBeans here: We need to leave all regular beans
      // uninitialized to let the auto-proxy creator apply to them!
      advisorNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
            this.beanFactory, Advisor.class, true, false);
      this.cachedAdvisorBeanNames = advisorNames;
   }
   if (advisorNames.length == 0) {
      return new ArrayList<>();
   }

   List<Advisor> advisors = new ArrayList<>();
   for (String name : advisorNames) {
      if (isEligibleBean(name)) {
         if (this.beanFactory.isCurrentlyInCreation(name)) {
            if (logger.isTraceEnabled()) {
               logger.trace("Skipping currently created advisor '" + name + "'");
            }
         }
         else {
            try {
               advisors.add(this.beanFactory.getBean(name, Advisor.class));
            }
            catch (BeanCreationException ex) {
               Throwable rootCause = ex.getMostSpecificCause();
               if (rootCause instanceof BeanCurrentlyInCreationException) {
                  BeanCreationException bce = (BeanCreationException) rootCause;
                  String bceBeanName = bce.getBeanName();
                  if (bceBeanName != null && this.beanFactory.isCurrentlyInCreation(bceBeanName)) {
                     if (logger.isTraceEnabled()) {
                        logger.trace("Skipping advisor '" + name +
                              "' with dependency on currently created bean: " + ex.getMessage());
                     }
                     // Ignore: indicates a reference back to the bean we're trying to advise.
                     // We want to find advisors other than the currently created bean itself.
                     continue;
                  }
               }
               throw ex;
            }
         }
      }
   }
   return advisors;
}

@EnableAspectJAutoProxy

上面的两种自动代理的方式在处理上还是会有缺陷,因为我得要定义多个Advisor,为此,spirng又提供了一种的新的方式,通过注解的方式,这种方式也就是我们常常使用的@Aspject作为一个切面类来使用的,这种方式的实现spring也是采用了它自己的扩展@Import了一个Registr来实现注册一个bean的后置处理器,最终来完成了代理功能的实现,使用注解的方式,spring会读取开发者自定义的,比如上面我们定义的Advisor,和通过注解@Aspject定义的切面类的@Before,@After之类的一些advisor信息放入一个advisor的列表,也就是说通过注解的方式最终找到的切面信息有:自定义的切面advisor+@Aspject;
这个注解主要是添加了一个AnnotationAwareAspectJAutoProxyCreator类型的BeanDefinition。
这个BeanDefinition也是一个bean的后置处理器,也是在spring的初始化后的最后一步去调用,创建代理对象的;
示例分析

@ComponentScan("com.example")
@EnableAspectJAutoProxy
public class AppConfig {

// @Bean
// public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator(){
//    return new DefaultAdvisorAutoProxyCreator();
// }
}
@Component
@Aspect
public class AspjectC {

   @Before("execution(* com.example.service.UserService.test())")
   public void beforeMethod(JoinPoint jp){
      String methodName = jp.getSignature().getName();
      System.out.println("【前置通知】the method 【" + methodName + "】 begins with " + Arrays
            .asList(jp.getArgs()));
   }

}

执行:
在这里插入图片描述
这只是一个简单的,关于很多的@Aspject的通知类型和表达式的写法网上一大推,我这里不列举了,下来可以自己试一下。

AspectJAwareAdvisorAutoProxyCreator继承了AbstractAdvisorAutoProxyCreator,重写了shouldSkip(Class<?> beanClass, String beanName)方法,表示某个bean需不需要进行AOP,在shouldSkip()方法中:
拿到所有的Advisor
遍历所有的Advisor,如果当前bean是AspectJPointcutAdvisor,那么则跳过

AnnotationAwareAspectJAutoProxyCreator继承了AspectJAwareAdvisorAutoProxyCreator,重写了findCandidateAdvisors()方法,它即可以找到Advisor类型的bean,也能把所有@Aspect注解标注的类扫描出来并生成Advisor。
findCandidateAdvisors

protected List<Advisor> findCandidateAdvisors() {
   // Add all the Spring advisors found according to superclass rules.
   //这里找到父类的也就是DefaultAdvisorAutoProxyCreator中找advisor的逻辑
   //简单来说就是找到开发者自定义的advior,类似于我们前面定义的MyAdvisor
   List<Advisor> advisors = super.findCandidateAdvisors();
   // Build Advisors for all AspectJ aspects in the bean factory.
   if (this.aspectJAdvisorsBuilder != null) {
      //这里就是去找@Aspject中定义的切面方法,比如@Before,@After等注解封装成一个advisor
      advisors.addAll(this.aspectJAdvisorsBuilder.buildAspectJAdvisors());
   }
   return advisors;
}

其他的没有什么了,流程都一样,只是在使用方式上有很多中,鉴于最近实在太忙了,这aop写的有点粗糙,下来有兴趣的可以进行进一步详细的研究;但是基本的概念和理念都已经点到了。

注解和源码对应关系

@Before对应的是AspectJMethodBeforeAdvice,直接实现MethodBeforeAdvice,在进行动态代理时会把AspectJMethodBeforeAdvice转成MethodBeforeAdviceInterceptor,也就转变成了MethodBeforeAdviceInterceptor
先执行advice对应的方法
再执行MethodInvocation的proceed(),会执行下一个Interceptor,如果没有下一个Interceptor了,会执行target对应的方法
@After对应的是AspectJAfterAdvice,直接实现了MethodInterceptor
先执行MethodInvocation的proceed(),会执行下一个Interceptor,如果没有下一个Interceptor了,会执行target对应的方法
再执行advice对应的方法
@Around对应的是AspectJAroundAdvice,直接实现了MethodInterceptor
直接执行advice对应的方法
@AfterThrowing对应的是AspectJAfterThrowingAdvice,直接实现了MethodInterceptor
先执行MethodInvocation的proceed(),会执行下一个Interceptor,如果没有下一个Interceptor了,会执行target对应的方法
如果上面抛了Throwable,那么则会执行advice对应的方法
@AfterReturning对应的是AspectJAfterReturningAdvice,实现了AfterReturningAdvice,在进行动态代理时会把AspectJAfterReturningAdvice转成AfterReturningAdviceInterceptor,也就转变成了MethodInterceptor
先执行MethodInvocation的proceed(),会执行下一个Interceptor,如果没有下一个Interceptor了,会执行target对应的方法
执行上面的方法后得到最终的方法的返回值
再执行Advice对应的方法
Spring AOP中的流程图:
在这里插入图片描述

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值