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

文章目录

    • 了解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 {@Overridepublic void afterReturning(Object returnValue, Method method, Object[] args, Object target) throws Throwable {System.out.println("方法return后执行");}
}
public class HushangAroundAdvice implements MethodInterceptor {@Nullable@Overridepublic 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 {@Overridepublic 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);// 这里添加一个beforeAdviceproxyFactory.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@Overridepublic 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@Overridepublic 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 + AdviceproxyFactory.addAdvisor(new PointcutAdvisor() {@Overridepublic Pointcut getPointcut() {// 在这里使用Pointcut对象来进行判断 哪些对象能进行增强return new StaticMethodMatcherPointcut() {@Overridepublic boolean matches(Method method, Class<?> targetClass) {// 判断方法名是否为testreturn method.getName().equals("test");}};}@Overridepublic Advice getAdvice() {// 上面的Pointcut校验满足后,才会执行该Advicereturn new HushangBeforeAdvice();}// 次方法不用管@Overridepublic 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);// 添加AdviceproxyFactoryBean.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(){// 创建一个pointcutNameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();pointcut.addMethodName("test");DefaultPointcutAdvisor defaultPointcutAdvisor = new DefaultPointcutAdvisor();// 使用上面创建的pointcutdefaultPointcutAdvisor.setPointcut(pointcut);// 指定AdvicedefaultPointcutAdvisor.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 {@Beanpublic DefaultPointcutAdvisor defaultPointcutAdvisor(){// 创建一个pointcutNameMatchMethodPointcut pointcut = new NameMatchMethodPointcut();pointcut.addMethodName("test");DefaultPointcutAdvisor defaultPointcutAdvisor = new DefaultPointcutAdvisor();// 使用上面创建的pointcutdefaultPointcutAdvisor.setPointcut(pointcut);// 指定AdvicedefaultPointcutAdvisor.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() {@Overridepublic Class<?> getTargetClass() {return null;}@Overridepublic boolean isStatic() {return false;}@Overridepublic Object getTarget() throws Exception {// 我们可以在此方法中自定义逻辑,来创建被代理对象// 当代理对象执行某个方法之前就会调用getTarget()方法,获取被代理对象return null;}@Overridepublic 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() {@Overridepublic Class<?> getTargetClass() {return descriptor.getDependencyType();}@Overridepublic boolean isStatic() {return false;}@Overridepublic 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;}@Overridepublic void releaseTarget(Object target) {}};// 在为ProxyFactory设置一个TargetSourceProxyFactory 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实现了InvocationHandlerreturn 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() {@Overridepublic ClassFilter getClassFilter() {return new ClassFilter() {@Overridepublic boolean matches(Class<?> clazz) {// 这里进行类的匹配return false;}};}@Overridepublic MethodMatcher getMethodMatcher() {return new MethodMatcher() {@Overridepublic boolean matches(Method method, Class<?> targetClass) {// 这里进行方法的匹配return false;}@Overridepublic boolean isRuntime() {// 这里返回true时,才会去进行下面带有方法参数的匹配return false;}@Overridepublic 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;// 遍历Advisorfor (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封装成为InterceptorInterceptor[] 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()方法,调用下一个interceptorif (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,跳过当前的MethodInterceptorreturn 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()方法进行递归// 比如MethodBeforeAdviceInterceptorreturn ((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{Around1BeforetargetMethod();AfterReturningAround2
} 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.mzph.cn/web/34707.shtml

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

相关文章

【教学类-64-05】20240625彩棒鱼骨图(二)AB排列 6.5*1CM 6选2根 30种

背景需求&#xff1a; 【教学类-64-04】20240619彩棒鱼骨图&#xff08;一&#xff09;6.5*1CM 6根棒子720种-CSDN博客文章浏览阅读897次&#xff0c;点赞23次&#xff0c;收藏13次。【教学类-64-04】20240619彩棒鱼骨图&#xff08;一&#xff09;6.5*1CM 6根棒子720种https:…

JetBrains Rider 2024安装教程

一、下载Rider 1、进入官网&#xff0c;点击“下载” 2、下载完毕 二、安装Rider 1、双击下载的exe文件 2、点击“下一步” 3、可以点击“浏览”选择安装路径&#xff0c;之后点击“下一步” 4、选中图中四项&#xff0c;点击“下一步” 5、选中图中四项&#xff0c;点击“下…

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)} //定义函数…

进程、CPU、MMU与PCB之间的关系

目录 进程与cpu&#xff08;中央处理器&#xff09; 源代码、程序、cpu与进程的关系 cpu超线程 CPU的简易架构与处理数据过程 进程与MMU&#xff08;内存管理单元&#xff09; mmu作用 cpu和mmu的关系 进程与PCB&#xff08;进程控制块&#xff09; PCB介绍与内部成员…

组合数学、圆排列、离散数学多重集合笔记

自用 如果能帮到您&#xff0c;那也值得高兴 知识点 离散数学经典题目 多重集合组合 补充容斥原理公式 隔板法题目 全排列题目&#xff1a;

Loki部署及使用

简介 loki 是云原生的日志服务,本文讲解loki的部署,日志接入和查询日志的简单使用。 理论 Loki 分两部分,Loki 是日志引擎部分,Promtail 是收集日志端。 Loki 是主服务器,负责存储日志和处理查询 。 promtail 是代理,负责收集日志并将其发送给 loki 。 promtail 是日志…

武汉星起航:全球化舞台,中国跨境电商品牌力与竞争力双提升

随着全球化步伐的加快和数字技术的迅猛发展&#xff0c;跨境出口电商模式已经成为中国企业海外拓展的重要战略选择。这一模式不仅为中国的中小型企业提供了进军全球市场的机会&#xff0c;更为它们在全球舞台上展示独特的竞争优势提供了强有力的支撑。武汉星起航将从市场拓宽、…

江协科技51单片机学习- p19 串口通信

前言&#xff1a; 本文是根据哔哩哔哩网站上“江协科技51单片机”视频的学习笔记&#xff0c;在这里会记录下江协科技51单片机开发板的配套视频教程所作的实验和学习笔记内容。本文大量引用了江协科技51单片机教学视频和链接中的内容。 引用&#xff1a; 51单片机入门教程-2…

Java Stream API揭秘:掌握List流操作,打造高效数据处理流程

序言 Java Stream API是Java 8中引入的一个非常重要的功能组成部分&#xff0c;它提供了一种声明式的处理数据集合的方法。它主要特点是基于函数式编程的理念&#xff0c;允许我们以更加简洁、高效的方式进行集合的处理、转换和过滤。通过Stream API&#xff0c;我们可以灵活地…

【LangChain系列——案例分析】【基于SQL+CSV的案例分析】【持续更新中】

目录 前言一、LangChain介绍二、在SQL问答时如何更好的提示&#xff1f;2-1、安装2-2、SQLite 样例数据2-3、使用langchain与其进行交互2-4、查看模型提示语2-5、提供表定义和示例行2-6、将表信息插入到Prompt中去2-7、添加自然语言->SQL示例2-8、在向量数据库中查找最相关的…

JAVA开发的一套医院绩效考核系统源码:KPI关键绩效指标的清晰归纳

KPI是关键绩效指标&#xff08;Key Performance Indicators&#xff09;的缩写&#xff0c;它是一种用于衡量员工或组织绩效的量化指标。这些指标通常与组织的目标和战略相关&#xff0c;并帮助管理层评估员工和组织的实际表现。KPI还可以为员工提供清晰的方向&#xff0c;使他…

办公软件汇总

1、OCR 1.1 pearOCR pearOCR 是一个免费的免费在线文字提取OCR工具网站。PearOCR界面简洁&#xff0c;所有过程均在网页端完成,无需下载任何软件&#xff0c;点开即用。官方地址&#xff1a;https://pearocr.com/ 参考&#xff1a;9款文字识别&#xff08;OCR&#xff09;工具…

Android性能分析工具-Perfetto基本使用

文章目录 一、Perfetto介绍二、抓取方法2.1 手机端直接抓取2.1.1 打开系统跟踪2.1.2 开始录制 2.2 使用 adb 抓取2.3 通过 Perfetto 网页自定义抓取 三、trace分析方法3.1 打开trace文件3.2 查看方法 一、Perfetto介绍 Perfetto 是一个用于性能检测和跟踪分析的生产级开源堆栈。…

Call_once

call_once和once_flag的声明 struct once_flag {constexpr once_flag() noexcept;once_flag(const once_flag&) delete;once_flag& operator(const once_flag&) delete; }; template<class Callable, class ...Args>void call_once(once_flag& flag, …

程序员如何用ChatGPT解决常见编程问题:实例解析

引言 在现代编程的世界中&#xff0c;技术进步日新月异&#xff0c;程序员们面临着各种各样的挑战和问题。解决这些问题的过程中&#xff0c;找到合适的工具至关重要。ChatGPT作为一种先进的人工智能语言模型&#xff0c;能够帮助程序员迅速、高效地解决常见的编程问题。本文将…

windows10/win11截图快捷键 和 剪贴板历史记录 快捷键

后知后觉的我今天又学了两招&#xff1a; windows10/win11截图快捷键 按 Windows 徽标键‌ Shift S。 选择屏幕截图的区域时&#xff0c;桌面将变暗。 默认情况下&#xff0c;选择“矩形模式”。 可以通过在工具栏中选择以下选项之一来更改截图的形状&#xff1a;“矩形模式”…

深度神经网络(dnn)--风格迁移(简单易懂)

概括 深度神经网络&#xff08;DNN&#xff09;在风格迁移领域的应用&#xff0c;实现了将一幅图像的艺术风格迁移到另一幅图像上的目标。该技术基于深度学习模型&#xff0c;特别是卷积神经网络&#xff08;CNN&#xff09;&#xff0c;通过提取内容图像的内容特征和风格图像的…

Python+Pytest+Yaml+Request+Allure+GitLab+Jenkins接口自动化测试框架概解

PythonPytestYamlAllure整体框架目录&#xff08;源代码请等下篇&#xff09; 框架详解 common:公共方法包 –get_path.py:获取文件路径方法 –logger_util.py:输出日志方法 –parameters_until.py&#xff1a;传参方式方法封装 –requests_util.py&#xff1a;请求方式方法封…

星坤Type-A连接器:创新快充技术,引领电子连接!

快速发展的电子时代&#xff0c;消费者对电子设备的性能和便利性有着更高的要求。特别是在充电和数据传输方面&#xff0c;快充技术和高速传输已成为市场的新宠。中国星坤公司推出的Type-A连接器系列&#xff0c;以其卓越的性能和创新的设计&#xff0c;满足了市场对高效、稳定…

天润融通:AI赋能客户体验,推动企业收入和业绩增长

“客户体验已经成为全球企业差异化的关键。人工智能与数据分析等创新技术正在加速推动企业在客户体验计划中取得成功&#xff0c;以保持领先地位”。Customer Insights & Analysis 研究经理Craig Simpson说道。 客户体验 (CX&#xff0c;Customer Experience) 是客户在与企…