SpringAOP执行流程——从源码画流程图

news2024/10/7 18:30:07

文章目录

    • 了解ProxyFactory
      • 入门操作
      • 添加多个Advice的执行顺序
      • 关于异常Advice
      • 关于proceed()方法
      • 指定方法才进行增强逻辑
    • 创建代理对象的其他方式
      • ProxyFactoryBean
      • BeanNameAutoProxyCreator
      • DefaultAdvisorAutoProxyCreator
    • 对SpringAOP的理解
    • TargetSource
    • ProxyFactory选择JDK/CJLIB代理
    • ProxyFactory代理对象执行过程
      • 执行过程
      • 核心思想
      • 执行方法匹配Advisor
      • 具体执行proceed()方法
      • 各注解对应的MethodInterceptor
    • @EnableAspectJAutoProxy
    • 补充知识点




重点在ProxyFactory代理对象执行过程和@EnableAspectJAutoProxy执行过程



了解ProxyFactory

入门操作

我现在创建了几个Advice

public class HushangAfterReturningAdvice implements AfterReturningAdvice {

    @Override
    public void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {
        System.out.println("方法return后执行");
    }
}
public class HushangAroundAdvice implements MethodInterceptor {

    @Nullable
    @Override
    public Object invoke(@NotNull MethodInvocation invocation) throws Throwable {
        System.out.println("方法执行Around前");
        Object proceed = invocation.proceed();
        System.out.println("方法执行Around后");
        return proceed;
    }
}
public class HushangBeforeAdvice implements MethodBeforeAdvice {

    @Override
    public void before(Method method, Object[] args, Object target) throws Throwable {
        System.out.println("方法执行前执行");
    }
}
public class HushangThrowsAdvice implements ThrowsAdvice {

    public void afterThrowing(Method method, Object[] args, Object target, NullPointerException ex) {
        System.out.println("方法抛出异常后执行");
    }

}

接下来有一个UserService的类作为target目标方法

public class UserService {

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

}

接下来就是一个测试类

public class Test {

    public static void main(String[] args) {
		
        UserService target = new UserService();
        ProxyFactory proxyFactory = new ProxyFactory();
        // 如果不设置target,那么下面的getProxy()方法进行强制转换就会报错
        proxyFactory.setTarget(target);
        // 这里添加一个beforeAdvice
        proxyFactory.addAdvice(new HushangBeforeAdvice());

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

现在的程序运行结果就是

方法执行前执行
test()...

Process finished with exit code 0



添加多个Advice的执行顺序

添加多个Advice,进而查看执行顺序

public class Test {

    public static void main(String[] args) {


        UserService target = new UserService();
        ProxyFactory proxyFactory = new ProxyFactory();
        // 如果不设置target,那么下面的getProxy()方法进行强制转换就会报错
        proxyFactory.setTarget(target);
        // 我添加了三个Advice 根据我添加Advice的顺序执行的相应的增强方法
        proxyFactory.addAdvice(new HushangAroundAdvice());
        proxyFactory.addAdvice(new HushangBeforeAdvice());
        proxyFactory.addAdvice(new HushangAroundAdvice());

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

此时输出结果是

方法执行Around前
方法执行前执行
方法执行Around前
test()...
方法执行Around后
方法执行Around后

Process finished with exit code 0



关于异常Advice

从下面的代码可以发现,ThrowsAdvice接口中没有需要必须重写的方法,而下面这个方法是我自己写的。

public class HushangThrowsAdvice implements ThrowsAdvice {

   public void afterThrowing(Method method, Object[] args, Object target, NullPointerException ex) {
      System.out.println("方法抛出异常后执行");
   }

}

其实在ThrowsAdvice接口中的注释有规定我们应该在实现类中定义什么样子的代码

在这里插入图片描述

在底层源码中就是先校验我们的类型是否为ThrowsAdvice接口,然后在去调用对应的afterThrowing()方法



关于proceed()方法

首先我们可以看到我这里定义的AroundAdvice,在方法中我自己还调用了invocation.proceed();此方法,明明其他几个Advice都不需要我额外多调用方法,直接写相应的增强逻辑就行了。

public class HushangAroundAdvice implements MethodInterceptor {

   @Nullable
   @Override
   public Object invoke(@NotNull MethodInvocation invocation) throws Throwable {
      System.out.println("方法执行Around前");
      Object proceed = invocation.proceed();
      System.out.println("方法执行Around后");
      return proceed;
   }
}

其实我们往ProxyFactory中添加的多个Advice,它底层源码就是调用的proceed()方法,如果我们这里不显示的调用,那么整个调用链路就断了。

比如我现在将调用proceed()方法这行注释掉,然后再去进行测试

public class HushangAroundAdvice implements MethodInterceptor {

   @Nullable
   @Override
   public Object invoke(@NotNull MethodInvocation invocation) throws Throwable {
      System.out.println("方法执行Around前");
       // 我现在将下面这行注释掉
      //Object proceed = invocation.proceed();
      System.out.println("方法执行Around后");
      return proceed;
   }
}
public class Test {
    public static void main(String[] args) {

        UserService target = new UserService();
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(target);
        
        proxyFactory.addAdvice(new HushangBeforeAdvice());
        proxyFactory.addAdvice(new HushangAroundAdvice()); // 直接从这里断开了
        proxyFactory.addAdvice(new HushangBeforeAdvice());
        proxyFactory.addAdvice(new HushangAroundAdvice());

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

此时的输出结果就是

方法执行前执行
方法执行Around前
方法执行Around后

Process finished with exit code 0



指定方法才进行增强逻辑

现在UserService类中有两个方法,我想要特定的方法才去执行Advice的增强,其他方法调用时不去进行增强

public class UserService {

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

}

我们可以使用Advisor来实现满足指定条件的方法才能进行Advice相关的增强。

public class Test {
    public static void main(String[] args) {

        UserService target = new UserService();
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(target);
        // proxyFactory.addAdvice(new HushangBeforeAdvice());

        // 我们可以理解为 Advisor = Pointcut + Advice
        proxyFactory.addAdvisor(new PointcutAdvisor() {
            @Override
            public Pointcut getPointcut() {
                // 在这里使用Pointcut对象来进行判断 哪些对象能进行增强
                return new StaticMethodMatcherPointcut() {
                    @Override
                    public boolean matches(Method method, Class<?> targetClass) {
                        // 判断方法名是否为test
                        return method.getName().equals("test");
                    }
                };
            }

            @Override
            public Advice getAdvice() {
                // 上面的Pointcut校验满足后,才会执行该Advice
                return new HushangBeforeAdvice();
            }

            // 次方法不用管
            @Override
            public boolean isPerInstance() {
                return false;
            }
        });

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



创建代理对象的其他方式

接下来我们希望ProxyFactory所产生的代理对象能直接就是Bean,能直接从Spring容器中得到UserSerivce的代理对象



ProxyFactoryBean

在配置类中创建一个bean

// 这里的具体使用和上面直接使用ProxyFactory差不多
// 添加一个beanName为userService  Type为ProxyFactoryBean的对象
@Bean
public ProxyFactoryBean userService(){
   UserService userService = new UserService();
   ProxyFactoryBean proxyFactoryBean = new ProxyFactoryBean();
    // 指定目标类
   proxyFactoryBean.setTarget(userService);
    // 添加Advice
   proxyFactoryBean.addAdvice(new HushangAroundAdvice());
   return proxyFactoryBean;
}

启动类:

public class Test {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

        UserService userService = (UserService) context.getBean("userService");
        userService.test();
    }
}

输出结果为

方法执行Around前
test()...
方法执行Around后

Process finished with exit code 0



BeanNameAutoProxyCreator

它的实现是基于BeanPostProcessor来实现的

我现在在UserService和HushangAroundAdvice两个类上面都添加了@Component注解,让他们两个都是一个bean。

现在通过指定某个bean的名字,来对该bean进行代理

@Bean
public BeanNameAutoProxyCreator beanNameAutoProxyCreator() {
   BeanNameAutoProxyCreator beanNameAutoProxyCreator = new BeanNameAutoProxyCreator();
   beanNameAutoProxyCreator.setBeanNames("userSe*");
   beanNameAutoProxyCreator.setInterceptorNames("hushangAroundAdvice");

   return beanNameAutoProxyCreator;
}

现在也实现了动态代理的功能

启动类:

public class Test {

    public static void main(String[] args) {
        AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);

        UserService userService = (UserService) context.getBean("userService");
        userService.test();
    }
}

输出结果为

方法执行Around前
test()...
方法执行Around后

Process finished with exit code 0



DefaultAdvisorAutoProxyCreator

DefaultAdvisorAutoProxyCreator它就是一个BeanPostProcessor。它会在实例化后调用,每次创建bean时,它会获取Spring容器中所有的Advisor,在其中的pointcut和我当前创建的bean是否匹配。如果匹配上就表示当前创建的bean需要动态代理,代理的逻辑就是Advisor中的Advice

@Bean
public DefaultPointcutAdvisor defaultPointcutAdvisor(){
    // 创建一个pointcut
    NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
    pointcut.addMethodName("test");

    DefaultPointcutAdvisor defaultPointcutAdvisor = new DefaultPointcutAdvisor();
    // 使用上面创建的pointcut
    defaultPointcutAdvisor.setPointcut(pointcut);
    // 指定Advice
    defaultPointcutAdvisor.setAdvice(new HushangAroundAdvice());

    return defaultPointcutAdvisor;
}

@Bean
public DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator() {

    DefaultAdvisorAutoProxyCreator defaultAdvisorAutoProxyCreator = new DefaultAdvisorAutoProxyCreator();

    return defaultAdvisorAutoProxyCreator;
}

其实对上面的代码进行一个优化,使用@Import注解直接导入DefaultAdvisorAutoProxyCreator

@ComponentScan("com.zhouyu")
@Configuration
@Import(DefaultAdvisorAutoProxyCreator.class)
public class AppConfig {

	@Bean
	public DefaultPointcutAdvisor defaultPointcutAdvisor(){
		// 创建一个pointcut
		NameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();
		pointcut.addMethodName("test");

		DefaultPointcutAdvisor defaultPointcutAdvisor = new DefaultPointcutAdvisor();
		// 使用上面创建的pointcut
		defaultPointcutAdvisor.setPointcut(pointcut);
		// 指定Advice
		defaultPointcutAdvisor.setAdvice(new HushangAroundAdvice());

		return defaultPointcutAdvisor;
	}
}

其实现在上面就是做了两件事:要和那些bean的方法进行匹配,匹配后要进行的增强逻辑是什么

而我们使用注解的方式其实也就是做的这两件事。

我们现在的动态代理其实就是用到的:ProxyFactory、Advisor、pointcut、Advice



对SpringAOP的理解

Spring的用法

@Aspect
@Component
public class HushangAspect {

	@Before("execution(public void com.zhouyu.service.UserService.test())")
	public void husahngBefore(JoinPoint joinPoint){
		System.out.println("before...");
	}

}

切点表达式就是Pointcut、注解+方法就是Advice、这一整个就是Advisor

@ComponentScan("com.zhouyu")
@Configuration
@EnableAspectJAutoProxy
public class AppConfig {
    ...
}

通过查看@EnableAspectJAutoProxy注解的源码,它其实就是往Spring容器中添加一个AnnotationAwareAspectJAutoProxyCreator对象,它是InstantiationAwareBeanPostProcessor类型的,

AnnotationAwareAspectJAutoProxyCreator对象它就是去解析我们的切面类、切点表达式、增强



TargetSource

在我们日常的AOP中,被代理对象就是Bean对象,是由BeanFactory给我们创建出来的,但是Spring AOP中提供了TargetSource机制,可以让我们用来自定义逻辑来创建被代理对象。

public class Test {
    public static void main(String[] args) {

        UserService target = new UserService();
        ProxyFactory proxyFactory = new ProxyFactory();
        
        // 之前直接设置target的方式,其实该方法的底层会把我们这里设置的target包装成一个TargetSource对象
        // proxyFactory.setTarget(target);
        proxyFactory.setTargetSource(new TargetSource() {
            @Override
            public Class<?> getTargetClass() {
                return null;
            }

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

            @Override
            public Object getTarget() throws Exception {
                // 我们可以在此方法中自定义逻辑,来创建被代理对象
                // 当代理对象执行某个方法之前就会调用getTarget()方法,获取被代理对象
                return null;
            }

            @Override
            public void releaseTarget(Object target) throws Exception {

            }
        });


        proxyFactory.addAdvice(new HushangBeforeAdvice());
        proxyFactory.addAdvice(new HushangAroundAdvice());

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

比如之前所提到的@Lazy注解,当加在属性上时,会产生一个代理对象赋值给这个属性,产生代理对象的代码为:

protected Object buildLazyResolutionProxy(final DependencyDescriptor descriptor, final @Nullable String beanName) {
    BeanFactory beanFactory = getBeanFactory();
    Assert.state(beanFactory instanceof DefaultListableBeanFactory,"BeanFactory needs to be a DefaultListableBeanFactory");
    final DefaultListableBeanFactory dlbf = (DefaultListableBeanFactory) beanFactory;

    TargetSource ts = new TargetSource() {
        @Override
        public Class<?> getTargetClass() {
            return descriptor.getDependencyType();
        }
        @Override
        public boolean isStatic() {
            return false;
        }
        @Override
        public Object getTarget() {
            Set<String> autowiredBeanNames = (beanName != null ? new LinkedHashSet<>(1) : null);
            // 依赖注入的方法 根据type找对象
            Object target = dlbf.doResolveDependency(descriptor, beanName, autowiredBeanNames, null);
            if (target == null) {
                Class<?> type = getTargetClass();
                if (Map.class == type) {
                    return Collections.emptyMap();
                }
                else if (List.class == type) {
                    return Collections.emptyList();
                }
                else if (Set.class == type || Collection.class == type) {
                    return Collections.emptySet();
                }
                throw new NoSuchBeanDefinitionException(descriptor.getResolvableType(),
                                                        "Optional dependency not present for lazy injection point");
            }
            if (autowiredBeanNames != null) {
                for (String autowiredBeanName : autowiredBeanNames) {
                    if (dlbf.containsBean(autowiredBeanName)) {
                        dlbf.registerDependentBean(autowiredBeanName, beanName);
                    }
                }
            }
            return target;
        }
        @Override
        public void releaseTarget(Object target) {
        }
    };

    // 在为ProxyFactory设置一个TargetSource
    ProxyFactory pf = new ProxyFactory();
    pf.setTargetSource(ts);
    Class<?> dependencyType = descriptor.getDependencyType();
    if (dependencyType.isInterface()) {
        pf.addInterface(dependencyType);
    }
    // 再返回ProxyFactory创建的代理对象
    return pf.getProxy(dlbf.getBeanClassLoader());
}



ProxyFactory选择JDK/CJLIB代理

ProxyFactory的简单使用案例如下

public class Test {
    public static void main(String[] args) {

        UserService target = new UserService();
        ProxyFactory proxyFactory = new ProxyFactory();
        proxyFactory.setTarget(target);
        
        proxyFactory.addAdvice(new HushangBeforeAdvice());
        proxyFactory.addAdvice(new HushangAroundAdvice());
        
        // 我们还可以为ProxyFactory设定一些其他的值
        proxyFactory.setOptimize(true);
		proxyFactory.setProxyTargetClass(true);
		proxyFactory.setInterfaces(UserInterface.class); 

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

在上面的代码中,这里setInterfaces()设置接口,如果UserService类实现了UserInterface接口,但是没有调用该方法进行设置,那么下方源码中的if判断还是为没有实现接口,ProxyFactory不会自动判断类有没有实现接口,Spring它是自己做了相应的处理逻辑

那么ProxyFactory在源码中是如何选择去进行JDK还是cjlib的动态代理嘞?我们通过getProxy() —> createAopProxy()

public Object getProxy() {
    return createAopProxy().getProxy();
}
public AopProxy createAopProxy(AdvisedSupport config) throws AopConfigException {
   // inNativeImage()是不是在GraalVM虚拟机上运行
   // 如果ProxyFactory的isOptimize为true,Spring认为cglib比jdk动态代理要快
   // 或者isProxyTargetClass为true,
   // 或者被代理对象没有实现接口,
   if (!NativeDetector.inNativeImage() &&
         (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.");
      }
       // 如果被代理类是接口,或者被代理类已经是进行过JDK动态代理而生成的代理类了则只能进行JDK动态代理
      if (targetClass.isInterface() || Proxy.isProxyClass(targetClass)) {
         return new JdkDynamicAopProxy(config);
      }
      return new ObjenesisCglibAopProxy(config);
   }
   else {
      return new JdkDynamicAopProxy(config);
   }
}

通过上面的createAopProxy()方法我们已经知道了ProxyFactory应该使用哪一种动态代理,接下来就是调用getProxy()方法去创建代理对象了,在AopProxy接口中getProxy()方法有两个实现类

在这里插入图片描述

就拿JDK动态代理举例,直接调用newProxyInstance()方法创建一个代理对象返回。

@Override
public Object getProxy() {
   return getProxy(ClassUtils.getDefaultClassLoader());
}

@Override
public Object getProxy(@Nullable ClassLoader classLoader) {
   if (logger.isTraceEnabled()) {
      logger.trace("Creating JDK dynamic proxy: " + this.advised.getTargetSource());
   }
   // this实现了InvocationHandler
   return Proxy.newProxyInstance(classLoader, this.proxiedInterfaces, this);
}



ProxyFactory代理对象执行过程

在线高清流程图

在这里插入图片描述



执行过程

不过是通过jdk还是cjlib创建的动态代理对象,他们的执行过程其实是一样的。就拿jdk动态代理举例,在JdkDynamicAopProxy类的invoke()方法

  1. 在创建ProxyFactory代理对象之前,需要往ProxyFactory中先添加Advisor

  2. 代理对象在执行某个方法时,会把ProxyFactory中的Advisor都取出来进行与该方法进行匹配筛选

  3. 把和方法匹配的Advisor封装成MethodInterceptor

  4. 把和方法匹配的Advisor封装成MethodInterceptor链、代理对象、目标对象、目标方法、目标方法参数、目标类class封装成一个MethodInvocation对象

  5. 执行MethodInvocation对象的proceed()方法,开始执行各个MethodInterceptor以及被代理对象的目标方法

  6. 按照循序调用每个MethodInterceptor的invoke()方法,invoke()方法里面会递归调用proceed()方法,并且把MethodInvocation对象传入invoke()方法

  7. 直到调用完最后一个MethodInterceptor,就会去调用invokeJoinpoint()方法执行被代理类的目标方法



核心思想

核心思想就是:

  1. proceed()判断是不是最后一个MethodInterceptor,如果不是就按顺序取MethodInterceptor并调用它的的invoke()方法

  2. 各个MethodInterceptor的invoke()方法中,再去递归调用proceed()方法

    各个MethodInterceptor的invoke()方法中有自己的实现逻辑,比如MethodBeforeAdviceInterceptor,它先调用advice对应方法再去调用proceed()方法,而AfterReturningAdviceInterceptor它的invoke()方法中就是先递归调用proceed()方法,再调用advice对应方法



执行方法匹配Advisor

在执行过程中,重点的两个位置就是找到匹配的Advisor并封装成MethodInterceptor、递归调用proceed()方法去执行

代理对象在执行某个方法时,根据方法筛选出匹配的Advisor,并适配成Interceptor。接下来看具体的实现,入口方法是AdvisedSupport.getInterceptorsAndDynamicInterceptionAdvice()

一个完整的Pointcut,它其实是可以进行类匹配,还可以继续方法匹配的,还可以进行带有方法参数的匹配

new Pointcut() {
    @Override
    public ClassFilter getClassFilter() {
        return new ClassFilter() {
            @Override
            public boolean matches(Class<?> clazz) {
                // 这里进行类的匹配
                return false;
            }
        };
    }

    @Override
    public MethodMatcher getMethodMatcher() {
        return new MethodMatcher() {
            @Override
            public boolean matches(Method method, Class<?> targetClass) {
                // 这里进行方法的匹配
                return false;
            }

            @Override
            public boolean isRuntime() {
                // 这里返回true时,才会去进行下面带有方法参数的匹配
                return false;
            }

            @Override
            public boolean matches(Method method, Class<?> targetClass, Object... args) {
                // 带有方法参数的匹配
                return false;
            }
        };
    }
};

源码的匹配代码如下

public List<Object> getInterceptorsAndDynamicInterceptionAdvice(
    Advised config, Method method, @Nullable Class<?> targetClass) {

    AdvisorAdapterRegistry registry = GlobalAdvisorAdapterRegistry.getInstance();
    // 从ProxyFactory中拿到所设置的Advice(添加时被封装成了DefaultPointcutAdvisor)
    // 添加的时候会控制顺序
    Advisor[] advisors = config.getAdvisors();
    List<Object> interceptorList = new ArrayList<>(advisors.length);
    Class<?> actualClass = (targetClass != null ? targetClass : method.getDeclaringClass());
    Boolean hasIntroductions = null;

    // 遍历Advisor
    for (Advisor advisor : advisors) {
        if (advisor instanceof PointcutAdvisor) {
            PointcutAdvisor pointcutAdvisor = (PointcutAdvisor) advisor;
            // 先匹配类
            if (config.isPreFiltered() || pointcutAdvisor.getPointcut().getClassFilter().matches(actualClass)) {

                // 再匹配方法
                MethodMatcher mm = pointcutAdvisor.getPointcut().getMethodMatcher();
                boolean match;
                if (mm instanceof IntroductionAwareMethodMatcher) {
                    if (hasIntroductions == null) {
                        hasIntroductions = hasMatchingIntroductions(advisors, actualClass);
                    }
                    match = ((IntroductionAwareMethodMatcher) mm).matches(method, actualClass, hasIntroductions);
                }
                else {
                    match = mm.matches(method, actualClass);
                }

                // 如果匹配
                if (match) {
					// 将Advisor封装成为Interceptor,当前Advisor中的Advice可能即是MethodBeforeAdvice,也是ThrowsAdvice
                    // 一般情况下 一个Advisor只会对应一个MethodInterceptor,数组中一般就只有一个
                    MethodInterceptor[] interceptors = registry.getInterceptors(advisor);
                    // 如果这里为true,那么就会把匹配的MethodInterceptor封装为InterceptorAndDynamicMethodMatcher类型保存
					// 在后面执行proceed()方法时就会去进行带有方法参数的匹配
                    // 如果为false,那么就直接保存
                    if (mm.isRuntime()) {
                        for (MethodInterceptor interceptor : interceptors) {
                            interceptorList.add(new InterceptorAndDynamicMethodMatcher(interceptor, mm));
                        }
                    }
                    else {
                        interceptorList.addAll(Arrays.asList(interceptors));
                    }
                }

                // 最终,interceptorList中存储的是当前正在执行的Method所匹配的MethodInterceptor,可能动态的,也可能是非动态的,
                // 找到Method所匹配的MethodInterceptor后,就会开始调用这些MethodInterceptor,如果是动态的,会额外进行方法参数的匹配
            }
        }
        else if (advisor instanceof IntroductionAdvisor) {
            IntroductionAdvisor ia = (IntroductionAdvisor) advisor;
            if (config.isPreFiltered() || ia.getClassFilter().matches(actualClass)) {
                Interceptor[] interceptors = registry.getInterceptors(advisor);
                interceptorList.addAll(Arrays.asList(interceptors));
            }
        }
        else {
            // 将Advisor封装成为Interceptor
            Interceptor[] interceptors = registry.getInterceptors(advisor);
            interceptorList.addAll(Arrays.asList(interceptors));
        }
    }

    return interceptorList;
}



具体执行proceed()方法

在执行过程中,重点的两个位置就是找到匹配的Advisor并封装成MethodInterceptor、递归调用proceed()方法去执行

在该方法中就会从MethodInterceptor集合中取出来,并调用各自的invoke()方法,在调用过程中会把this自己传递过去,而在MethodInterceptor的invoke()方法中又会调用proceed()方法,就这样完成了循环遍历。

public Object proceed() throws Throwable {

    // We start with an index of -1 and increment early.
    // currentInterceptorIndex初始值为-1,每调用一个interceptor就会加1
    // 当调用完了最后一个interceptor后就会执行被代理方法
    if (this.currentInterceptorIndex == this.interceptorsAndDynamicMethodMatchers.size() - 1) {
        return invokeJoinpoint();
    }

    // currentInterceptorIndex初始值为-1,取一个MethodInterceptor出来
    Object interceptorOrInterceptionAdvice =
        this.interceptorsAndDynamicMethodMatchers.get(++this.currentInterceptorIndex);

    // 当前interceptor是InterceptorAndDynamicMethodMatcher,则先进行匹配,匹配成功后再调用该interceptor
    // 如果没有匹配则递归调用proceed()方法,调用下一个interceptor
    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.
            // 不匹配则执行下一个MethodInterceptor,跳过当前的MethodInterceptor
            return proceed();
        }
    }
    else {

        // It's an interceptor, so we just invoke it: The pointcut will have
        // been evaluated statically before this object was constructed.
        // 直接调用MethodInterceptor,传入this,在内部会再次调用proceed()方法进行递归
        // 比如MethodBeforeAdviceInterceptor
        return ((MethodInterceptor) interceptorOrInterceptionAdvice).invoke(this);
    }
}



各注解对应的MethodInterceptor

  • @Before对应的是AspectJMethodBeforeAdvice,在进行动态代理时会把AspectJMethodBeforeAdvice转成MethodBeforeAdviceInterceptor
    • 先执行advice对应的方法
    • 再执行MethodInvocation的proceed(),会执行下一个Interceptor,如果没有下一个Interceptor了,会执行target对应的方法
  • @After对应的是AspectJAfterAdvice,直接实现了MethodInterceptor
    • 先执行MethodInvocation的proceed(),会执行下一个Interceptor,如果没有下一个Interceptor了,会执行target对应的方法
    • 再执行advice对应的方法
  • @Around对应的是AspectJAroundAdvice,直接实现了MethodInterceptor
    • 直接执行advice对应的方法,由@Around自己决定要不要继续往后面调用
  • @AfterThrowing对应的是AspectJAfterThrowingAdvice,直接实现了MethodInterceptor
    • 先执行MethodInvocation的proceed(),会执行下一个Interceptor,如果没有下一个Interceptor了,会执行target对应的方法
    • 如果上面抛了Throwable,那么则会执行advice对应的方法
  • @AfterReturning对应的是AspectJAfterReturningAdvice,在进行动态代理时会把AspectJAfterReturningAdvice转成AfterReturningAdviceInterceptor
    • 先执行MethodInvocation的proceed(),会执行下一个Interceptor,如果没有下一个Interceptor了,会执行target对应的方法
    • 执行上面的方法后得到最终的方法的返回值
    • 再执行Advice对应的方法
try{
    Around1
    Before
    targetMethod();
    AfterReturning
    Around2
} catch(){
    AfterThrowing
} finaly{
   After 
}






@EnableAspectJAutoProxy

在线高清流程图

在这里插入图片描述



上面的内容都是在分析ProxyFactory,也是为了更好的了解SpringAOP。接下来就来分析Spring中的实现

我们一般都是先定义一个切面类,

@Aspect
@Component
public class HushangAspect {

	@Before("execution(public void com.zhouyu.service.UserService.test())")
	public void husahngBefore(JoinPoint joinPoint){
		System.out.println("before...");
	}

    
	@Pointcut("execution(public void com.zhouyu.service.UserService.test())")
	public void a(){

	}

	@After("a()")
	public void hushangAfter(JoinPoint joinPoint){
		System.out.println("After...");
	}

	@AfterThrowing("a()")
	public void hushangAfterThrowing(){
		System.out.println("AfterThrowing...");
	}

	@Around("a()")
	public void hushangAround(ProceedingJoinPoint joinPoint) throws Throwable {
		System.out.println("Aroud1...");
		joinPoint.proceed();
		System.out.println("Aroud2...");
	}
}

但是此时该类方法上面的注解Spring是没有去解析的,我们还需要在配置类上面加一个@EnableAspectJAutoProxy注解

@EnableAspectJAutoProxy注解它所做的事其实就是把@EnableAspectJAutoProxy注解中设置的proxyTargetClassexposeProxy这两个参数值赋值给BeanDefinition,并且往Spring容器中添加一个AnnotationAwareAspectJAutoProxyCreator类型的bean对象,它的父类是AbstractAutoProxyCreator,它是一个BeanPostProcessor类型。在初始化后调用BeanPostProcessor时就会执行AnnotationAwareAspectJAutoProxyCreator这个类

在这里插入图片描述

  1. 初始化后,进行AOP的入口是AbstractAutoProxyCreator.postProcessAfterInitialization(),这里会去找到当前创建的bean所有匹配的Interceptor,然后把当前bean封装成为一个TargetSource对象,一起通过ProxyFactory去创建一个代理对象。

  2. 对找到的Advisor与当前创建的bean进行匹配筛选的是AbstractAdvisorAutoProxyCreator.findEligibleAdvisors()方法

  3. 去找所有的Advisor就是AnnotationAwareAspectJAutoProxyCreator.findCandidateAdvisors()方法了,它会去找所有的@Aspect注解修饰的类,会去解析所有的切点表达式与相应的方法,进而生成一个Advisor集合



补充知识点

如果目标方法有参数,

@Component
public class UserService {

   public void test(String a, String b){
      System.out.println("test()...");
   }

}

我在切面的方法增强中想要获取

@Before(value = "execution(public void com.zhouyu.service.UserService.test(..)) && args(a,b)", argNames = "a,b")
public void husahngBefore(String a, String b){
   System.out.println(a);
   System.out.println(b);
   System.out.println("before...");
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1866050.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

我对AI赋能的未来畅想

个人名片 &#x1f393;作者简介&#xff1a;java领域优质创作者 &#x1f310;个人主页&#xff1a;码农阿豪 &#x1f4de;工作室&#xff1a;新空间代码工作室&#xff08;提供各种软件服务&#xff09; &#x1f48c;个人邮箱&#xff1a;[2435024119qq.com] &#x1f4f1…

C# 入门—实现 Hello, World!

目录 一、.net 平台 二、.net 都能干什么&#xff1f; 三、.net 两种交互模式 四、使用 VS Code 开发 C# 程序 五、实现 Hello, World! 一、.net 平台 下载 .NET(Linux、macOS 和 Windows) (microsoft.com) .NET 简介 - .NET | Microsoft Learn C# :一种编程语言,可以开…

python-docx 设置页面边距、页眉页脚高度

本文目录 前言一、docx 页面边距在哪里二、对 <w:pgMar> 的详细说明1、上边距的说明2、右边距的说明3、下边距的说明4、左边距的说明5、页眉高度的说明6、页脚高度的说明三、设置 docx 页边距、页眉页脚高度1、完整代码2、代码执行效果图四、补充一些内容1、页面边距的两…

jenkins中执行docker命令

1. 修改docker.sock文件的所属组 命令如下&#xff1a; sudo chown root:root docker.sock 2. 对这个文件赋予权限&#xff0c;供其他用户使用&#xff0c;给定权限命令如下&#xff1a; sudo chmod orw docker.sock 3. docker容器映射 这里需要两个文件&#xff1a; 一个…

PS教程29

图层蒙版 以案例来解释蒙版的作用 将这两张图片原框背景切换将图二的背景选中使用套索工具选中区域切换图一CtrlA全选CtrlC复制编辑-选择性粘贴-贴入即可贴入如果位置不对用移动工具进行调整 这就是图层蒙版 图层蒙版本质作用&#xff1a;是临时通道&#xff0c;支持黑白灰三种…

火车头采集器Discuz采集发布模块插件

火车头采集器怎么采集发布数据到Discuz系统的论坛帖子或门户文章&#xff1f; 可按照以下步骤配置&#xff1a; 1. 火车头采集器Discuz采集发布插件下载安装&#xff1a; 火车头采集器Discuz发布模块插件下载地址-CSDN 2. 在火车头采集器工具导入Discuz采集发布模块插件&am…

Java 8 新特性:Lambda表达式让你的代码焕然一新——掌握它,让编程变得轻松又高效!

前言 Java 8 是 Java 发展史上的一次重要里程碑。作为企业级开发语言&#xff0c;它在性能和功能上做了巨大的提升。这其中&#xff0c;Lambda表达式是一个关键的新特性&#xff0c;它为 Java 语言带来了函数式编程的概念。本篇文章将深入探讨Lambda表达式&#xff0c;并结合热…

【Linux】解锁并发:多线程同步技术详解与应用实践

文章目录 前言&#xff1a;1. 同步概念2. 条件变量&#xff1a;实现线程间同步的&#xff01;2.1. 条件变量是什么&#xff1f;2.2. 认识条件变量接口 3. 写一个测试代码——验证线程的同步机制4. 生产消费模型5. 生产消费模型 条件变量6. 线程池7. 可重入 VS 线程安全7.1. 概…

使用Python进行大数据处理Dask与Apache Spark的对比

&#x1f47d;发现宝藏 前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。【点击进入巨牛的人工智能学习网站】。 使用Python进行大数据处理Dask与Apache Spark的对比 随着数据量的增加和数据处理需求的增长…

基于IM948(Low-cost IMU+蓝牙)模块的高精度PDR(Pedestrian Dead Reckoning)定位系统 — 可以提供模块和配套代码

一、背景与意义 行人PDR定位系统中的PDR&#xff08;Pedestrian Dead Reckoning&#xff0c;即行人航位推算&#xff09;背景意义在于其提供了一种在GPS信号不可用或不可靠的环境下&#xff0c;对行人进行精确定位和导航的解决方案。以下是关于PDR背景意义的详细描述&#xff1…

Go语言学习:每日一练1

Go语言学习&#xff1a;每日一练1 目录 Go语言学习&#xff1a;每日一练1变量声明函数定义流程控制 ifrange遍历switch 变量声明 package main//定义变量 var a 1 const Message “hello,world”func main() {b : 2 //短变量声明var c 3c TestMethod(a, b, c)} //定义函数…

【UE5.3】笔记2--资源导入

资源导入 方式一&#xff1a;内置资源--初学者内容包 方式二&#xff1a;虚幻商城 搜索免费资源&#xff1a; 添加到工程之后 搜素&#xff1a;虚幻学习工具包&#xff0c;需要注意的是支持的引擎版本 当然商城里包含了大量的免费的资源&#xff0c;初期学习不想投入太多可以…

node 实现导出, 在导出excel中包含图片(附件)

如果想查看 node mySql 实现数据的导入导出&#xff0c;以及导入批量插入的sql语句&#xff0c;连接如下 node mySql 实现数据的导入导出&#xff0c;以及导入批量插入的sql语句-CSDN博客https://blog.csdn.net/snows_l/article/details/139998373 一、效果如图&#xff1a; 二…

设计师进阶指南:掌握这6条版式设计要点

布局设计是设计师的必修课。优秀的排版不是强制性的“东拼西凑”&#xff0c;而是通过设计师独特的排版获得的。这不是简单的信息列表&#xff0c;而是认真思考如何分层、有节奏地组织和安排元素。今天我将给你带来它 6 文章还附带了布局设计模板资源&#xff0c;设计师朋友一定…

第三十三篇——互联网广告:为什么Google搜索的广告效果好?

目录 一、背景介绍二、思路&方案三、过程1.思维导图2.文章中经典的句子理解3.学习之后对于投资市场的理解4.通过这篇文章结合我知道的东西我能想到什么&#xff1f; 四、总结五、升华 一、背景介绍 对于信息的利用&#xff0c;再广告这个维度中去洞察&#xff0c;你又能发…

PS系统教程30

图层蒙版组合使用 案例介绍 全选背景图-复制背景图粘贴背景图CtrlI反选背景色填充黑色快速选区工具框柱需要素材画笔涂抹白色 步骤截图 1-3 4-5 图层蒙版与渐变工具结合使用 案例2 注意 使用PS的渐变工具覆盖全部的原因可能包括操作不当或设置错误。 操作不当&#xff1…

【Qt】Qt多线程编程指南:提升应用性能与用户体验

文章目录 前言1. Qt 多线程概述2. QThread 常用 API3. 使用线程4. 多线的使用场景5. 线程安全问题5.1. 加锁5.2. QReadWriteLocker、QReadLocker、QWriteLocker 6. 条件变量 与 信号量6.1. 条件变量6.2 信号量 总结 前言 在现代软件开发中&#xff0c;多线程编程已成为一个不可…

越有水平的领导,越擅长用这3个字来管人,怪不得执行力强

越有水平的领导&#xff0c;越擅长用这3个字来管人&#xff0c;怪不得执行力强 第一个字&#xff1a;“实” 要想提高执行力&#xff0c;必须发扬务实、实干、刻苦勤勉的工作精神。纸上谈兵&#xff0c;夸夸其谈的事情少做&#xff0c;多行动&#xff0c;少说话。 沉浸在表面…

问界M9累计大定破10万,创中国豪车新纪录

ChatGPT狂飙160天&#xff0c;世界已经不是之前的样子。 更多资源欢迎关注 6月26日消息&#xff0c;华为常务董事、终端BG董事长、智能汽车解决方案BU董事长余承东今日宣布&#xff0c;问界M9上市6个月&#xff0c;累计大定突破10万辆。 这一成绩&#xff0c;也创造了中国市场…

视觉分割的定义与性能度量

文章目录 视觉分割的定义语义分割(Semantic Segmentation)实例分割(instance Segmentation)全景分割(Panoptic Segmentation)视频语义分割(Video Semantic Segmentation)视频实例分割(Video instance Segmentation)视频全景分割(Video Panoptic Segmentation)各任务对比 视觉分…