SpringAOP模拟实现

文章目录

    • 1_底层切点、通知、切面
    • 2_切点匹配
    • 3_从 @Aspect 到 Advisor
      • 1_代理创建器
      • 2_代理创建时机
      • 3_@Before 对应的低级通知
    • 4_静态通知调用
      • 1_通知调用过程
      • 2_模拟 MethodInvocation
    • 5_动态通知调用

1_底层切点、通知、切面


注意点:

  1. 底层的切点实现
  2. 底层的通知实现
  3. 底层的切面实现
  4. ProxyFactory 用来创建代理
    • 如果指定了接口,且 proxyTargetClass = false,使用 JdkDynamicAopProxy
    • 如果没有指定接口,或者 proxyTargetClass = true,使用 ObjenesisCglibAopProxy
    • 例外:如果目标是接口类型或已经是 jdk 代理,使用 JdkDynamicAopProxy
public class A15 {public static void main(String[] args) {/*两个切面概念aspect =通知1(advice) +  切点1(pointcut)通知2(advice) +  切点2(pointcut)通知3(advice) +  切点3(pointcut)...advisor = 更细粒度的切面,包含一个通知和切点*/// 1. 备好切点AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();pointcut.setExpression("execution(* foo())");// 2. 备好通知MethodInterceptor advice = invocation -> {System.out.println("before...");Object result = invocation.proceed(); // 调用目标System.out.println("after...");return result;};// 3. 备好切面DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);/*4. 创建代理a. proxyTargetClass = false, 目标实现了接口, 用 jdk 实现b. proxyTargetClass = false,  目标没有实现接口, 用 cglib 实现c. proxyTargetClass = true, 总是使用 cglib 实现*/Target2 target = new Target2();ProxyFactory factory = new ProxyFactory();factory.setTarget(target);factory.addAdvisor(advisor);factory.setInterfaces(target.getClass().getInterfaces());factory.setProxyTargetClass(false);Target2 proxy = (Target2) factory.getProxy();System.out.println(proxy.getClass());proxy.foo();proxy.bar();/*学到了什么a. Spring 的代理选择规则b. 底层的切点实现c. 底层的通知实现d. ProxyFactory 是用来创建代理的核心实现, 用 AopProxyFactory 选择具体代理实现- JdkDynamicAopProxy- ObjenesisCglibAopProxy*/}interface I1 {void foo();void bar();}static class Target1 implements I1 {public void foo() {System.out.println("target1 foo");}public void bar() {System.out.println("target1 bar");}}static class Target2 {public void foo() {System.out.println("target2 foo");}public void bar() {System.out.println("target2 bar");}}
}

2_切点匹配


切点匹配:

  1. 常见 aspectj 切点用法
  2. aspectj 切点的局限性,实际的 @Transactional 切点实现
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.support.StaticMethodMatcherPointcut;
import org.springframework.core.annotation.MergedAnnotations;
import org.springframework.transaction.annotation.Transactional;import java.lang.reflect.Method;public class A16 {public static void main(String[] args) throws NoSuchMethodException {
//        AspectJExpressionPointcut pt1 = new AspectJExpressionPointcut();
//        pt1.setExpression("execution(* bar())");
//        System.out.println(pt1.matches(T1.class.getMethod("foo"), T1.class));
//        System.out.println(pt1.matches(T1.class.getMethod("bar"), T1.class));
//
//        AspectJExpressionPointcut pt2 = new AspectJExpressionPointcut();
//        pt2.setExpression("@annotation(org.springframework.transaction.annotation.Transactional)");
//        System.out.println(pt2.matches(T1.class.getMethod("foo"), T1.class));
//        System.out.println(pt2.matches(T1.class.getMethod("bar"), T1.class));StaticMethodMatcherPointcut pt3 = new StaticMethodMatcherPointcut() {@Overridepublic boolean matches(Method method, Class<?> targetClass) {// 检查方法上是否加了 Transactional 注解MergedAnnotations annotations = MergedAnnotations.from(method);if (annotations.isPresent(Transactional.class)) {return true;}// 查看类上是否加了 Transactional 注解annotations = MergedAnnotations.from(targetClass, MergedAnnotations.SearchStrategy.TYPE_HIERARCHY);if (annotations.isPresent(Transactional.class)) {return true;}return false;}};System.out.println(pt3.matches(T1.class.getMethod("foo"), T1.class));System.out.println(pt3.matches(T1.class.getMethod("bar"), T1.class));System.out.println(pt3.matches(T2.class.getMethod("foo"), T2.class));System.out.println(pt3.matches(T3.class.getMethod("foo"), T3.class));/*学到了什么a. 底层切点实现是如何匹配的: 调用了 aspectj 的匹配方法b. 比较关键的是它实现了 MethodMatcher 接口, 用来执行方法的匹配*/}static class T1 {@Transactionalpublic void foo() {}public void bar() {}}@Transactionalstatic class T2 {public void foo() {}}@Transactionalinterface I3 {void foo();}static class T3 implements I3 {public void foo() {}}
}

3_从 @Aspect 到 Advisor

1_代理创建器


注意点:

  1. AnnotationAwareAspectJAutoProxyCreator 的作用
    • 将高级 @Aspect 切面统一为低级 Advisor 切面。
    • 在合适的时机创建代理。
  2. findEligibleAdvisors 找到有【资格】的 Advisors
    • 有【资格】的 Advisor 一部分是低级的, 可以由自己编写, 如本例 A17 中的 advisor3。
    • 有【资格】的 Advisor 另一部分是高级的, 由解析 @Aspect 后获得。
  3. wrapIfNecessary
    • 它内部调用 findEligibleAdvisors, 只要返回集合不空, 则表示需要创建代理。
    • 它的调用时机通常在原始对象初始化后执行, 但碰到循环依赖会提前至依赖注入之前执行。
package org.springframework.aop.framework.autoproxy;import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.transaction.annotation.Transactional;import java.util.List;/*** @author shenyang* @version 1.0* @info TestAop* @since 2024/8/17 20:26*/
public class A17 {@Aspect//高级切面static class Aspect1{@Before("execution(* foo())")public void before(){System.out.println("aspect1 before.....");}@After("execution(* foo())")public void after(){System.out.println("aspect1 after.....");}}@Configurationstatic class Config {@Bean//低级切面public Advisor advisor3(MethodInterceptor advice3) {AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();pointcut.setExpression("execution(* foo())");return new DefaultPointcutAdvisor(pointcut, advice3);}@Beanpublic MethodInterceptor advice3(){return new MethodInterceptor() {@Overridepublic Object invoke(MethodInvocation methodInvocation) throws Throwable {System.out.println("advice3 before.....");Object proceed = methodInvocation.proceed();System.out.println("advice3 after.....");return proceed;}};}}static class T1 {public void foo() {System.out.println("target1 foo");}}static class T2 {public void bar() {System.out.println("target2 bar");}}public static void main(String[] args) {GenericApplicationContext context = new GenericApplicationContext();context.registerBean("aspect1",Aspect1.class);context.registerBean("config",Config.class);context.registerBean(ConfigurationClassPostProcessor.class);context.registerBean(AnnotationAwareAspectJAutoProxyCreator.class);context.refresh();AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);List<Advisor> advisors = creator.findEligibleAdvisors(T2.class, "target2");System.out.println("====================");
//        advisors.forEach(System.out::println);T1 o1 = (T1) creator.wrapIfNecessary(new T1(), "target1", "target1");T2 o2 = (T2) creator.wrapIfNecessary(new T2(), "target2", "target2");System.out.println(o1.getClass()+" "+o2.getClass());o1.foo();o2.bar();}}

2_代理创建时机


注意点:

  1. 代理的创建时机
    • 初始化之后 (无循环依赖时)
    • 实例创建后, 依赖注入前 (有循环依赖时), 并暂存于二级缓存。
  2. 依赖注入与初始化不应该被增强, 仍应被施加于原始对象
package org.springframework.aop.framework.autoproxy;import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.aop.support.DefaultPointcutAdvisor;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.CommonAnnotationBeanPostProcessor;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;import javax.annotation.PostConstruct;public class A17_1 {public static void main(String[] args) {GenericApplicationContext context = new GenericApplicationContext();context.registerBean(ConfigurationClassPostProcessor.class);context.registerBean(Config.class);context.refresh();context.close();// 创建 -> (*) 依赖注入 -> 初始化 (*)/*学到了什么a. 代理的创建时机1. 初始化之后 (无循环依赖时)2. 实例创建后, 依赖注入前 (有循环依赖时), 并暂存于二级缓存b. 依赖注入与初始化不应该被增强, 仍应被施加于原始对象*/}@Configurationstatic class Config {@Bean // 解析 @Aspect、产生代理public AnnotationAwareAspectJAutoProxyCreator annotationAwareAspectJAutoProxyCreator() {return new AnnotationAwareAspectJAutoProxyCreator();}@Bean // 解析 @Autowiredpublic AutowiredAnnotationBeanPostProcessor autowiredAnnotationBeanPostProcessor() {return new AutowiredAnnotationBeanPostProcessor();}@Bean // 解析 @PostConstructpublic CommonAnnotationBeanPostProcessor commonAnnotationBeanPostProcessor() {return new CommonAnnotationBeanPostProcessor();}@Beanpublic Advisor advisor(MethodInterceptor advice) {AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();pointcut.setExpression("execution(* foo())");return new DefaultPointcutAdvisor(pointcut, advice);}@Beanpublic MethodInterceptor advice() {return (MethodInvocation invocation) -> {System.out.println("before...");return invocation.proceed();};}@Beanpublic Bean1 bean1() {return new Bean1();}@Beanpublic Bean2 bean2() {return new Bean2();}}static class Bean1 {public void foo() {}public Bean1() {System.out.println("Bean1()");}@Autowired public void setBean2(Bean2 bean2) {System.out.println("Bean1 setBean2(bean2) class is: " + bean2.getClass());}@PostConstruct public void init() {System.out.println("Bean1 init()");}}static class Bean2 {public Bean2() {System.out.println("Bean2()");}@Autowired public void setBean1(Bean1 bean1) {System.out.println("Bean2 setBean1(bean1) class is: " + bean1.getClass());}@PostConstruct public void init() {System.out.println("Bean2 init()");}}
}

3_@Before 对应的低级通知


注意点:

  1. @Before 前置通知会被转换为原始的 AspectJMethodBeforeAdvice 形式, 该对象包含了如下信息
    • 通知代码从哪儿来
    • 切点是什么(这里为啥要切点, 后面解释)
    • 通知对象如何创建, 本例共用同一个 Aspect 对象
  2. 类似的还有
    • AspectJAroundAdvice (环绕通知)
    • AspectJAfterReturningAdvice
    • AspectJAfterThrowingAdvice (环绕通知)
    • AspectJAfterAdvice (环绕通知)
package org.springframework.aop.framework.autoproxy;import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Before;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.AspectInstanceFactory;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.aspectj.AspectJMethodBeforeAdvice;
import org.springframework.aop.aspectj.SingletonAspectInstanceFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;public class A17_2 {static class Aspect {@Before("execution(* foo())")public void before1() {System.out.println("before1");}@Before("execution(* foo())")public void before2() {System.out.println("before2");}public void after() {System.out.println("after");}public void afterReturning() {System.out.println("afterReturning");}public void afterThrowing() {System.out.println("afterThrowing");}public Object around(ProceedingJoinPoint pjp) throws Throwable {try {System.out.println("around...before");return pjp.proceed();} finally {System.out.println("around...after");}}}static class Target {public void foo() {System.out.println("target foo");}}@SuppressWarnings("all")public static void main(String[] args) throws Throwable {AspectInstanceFactory factory = new SingletonAspectInstanceFactory(new Aspect());// 高级切面转低级切面类List<Advisor> list = new ArrayList<>();for (Method method : Aspect.class.getDeclaredMethods()) {if (method.isAnnotationPresent(Before.class)) {// 解析切点String expression = method.getAnnotation(Before.class).value();AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();pointcut.setExpression(expression);// 通知类AspectJMethodBeforeAdvice advice = new AspectJMethodBeforeAdvice(method, pointcut, factory);// 切面Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);list.add(advisor);}}for (Advisor advisor : list) {System.out.println(advisor);}/*@Before 前置通知会被转换为下面原始的 AspectJMethodBeforeAdvice 形式, 该对象包含了如下信息a. 通知代码从哪儿来b. 切点是什么(这里为啥要切点, 后面解释)c. 通知对象如何创建, 本例共用同一个 Aspect 对象类似的通知还有1. AspectJAroundAdvice (环绕通知)2. AspectJAfterReturningAdvice3. AspectJAfterThrowingAdvice4. AspectJAfterAdvice (环绕通知)*/}
}

4_静态通知调用


代理对象调用流程如下(以 JDK 动态代理实现为例)

  • 从 ProxyFactory 获得 Target 和环绕通知链,根据他俩创建 MethodInvocation,简称 mi
  • 首次执行 mi.proceed() 发现有下一个环绕通知,调用它的 invoke(mi)
  • 进入环绕通知1,执行前增强,再次调用 mi.proceed() 发现有下一个环绕通知,调用它的 invoke(mi)
  • 进入环绕通知2,执行前增强,调用 mi.proceed() 发现没有环绕通知,调用 mi.invokeJoinPoint() 执行目标方法
  • 目标方法执行结束,将结果返回给环绕通知2,执行环绕通知2 的后增强
  • 环绕通知2继续将结果返回给环绕通知1,执行环绕通知1 的后增强
  • 环绕通知1返回最终的结果

图中不同颜色对应一次环绕通知或目标的调用起始至终结

Proxy InvocationHandler MethodInvocation ProxyFactory MethodInterceptor1 MethodInterceptor2 Target invoke() 获得 Target 获得 MethodInterceptor 链 创建 mi mi.proceed() invoke(mi) 前增强 mi.proceed() invoke(mi) 前增强 mi.proceed() mi.invokeJoinPoint() 结果 后增强 结果 后增强 结果 Proxy InvocationHandler MethodInvocation ProxyFactory MethodInterceptor1 MethodInterceptor2 Target

1_通知调用过程


代理方法执行时会做如下工作:

  1. 通过 proxyFactory 的 getInterceptorsAndDynamicInterceptionAdvice() 将其他通知统一转换为 MethodInterceptor 环绕通知
    • MethodBeforeAdviceAdapter 将 @Before AspectJMethodBeforeAdvice 适配为 MethodBeforeAdviceInterceptor
    • AfterReturningAdviceAdapter 将 @AfterReturning AspectJAfterReturningAdvice 适配为 AfterReturningAdviceInterceptor
    • 这体现的是适配器设计模式
  2. 所谓静态通知,体现在上面方法的 Interceptors 部分,这些通知调用时无需再次检查切点,直接调用即可
  3. 结合目标与环绕通知链,创建 MethodInvocation 对象,通过它完成整个调用
package org.springframework.aop.framework;import org.aopalliance.intercept.MethodInvocation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.AfterThrowing;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Before;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.*;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.framework.ReflectiveMethodInvocation;
import org.springframework.aop.interceptor.ExposeInvocationInterceptor;
import org.springframework.aop.support.DefaultPointcutAdvisor;import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;public class A18 {static class Aspect {@Before("execution(* foo())")public void before1() {System.out.println("before1");}@Before("execution(* foo())")public void before2() {System.out.println("before2");}public void after() {System.out.println("after");}@AfterReturning("execution(* foo())")public void afterReturning() {System.out.println("afterReturning");}@AfterThrowing("execution(* foo())")public void afterThrowing(Exception e) {System.out.println("afterThrowing " + e.getMessage());}@Around("execution(* foo())")public Object around(ProceedingJoinPoint pjp) throws Throwable {try {System.out.println("around...before");return pjp.proceed();} finally {System.out.println("around...after");}}}static class Target {public void foo() {System.out.println("target foo");}}@SuppressWarnings("all")public static void main(String[] args) throws Throwable {AspectInstanceFactory factory = new SingletonAspectInstanceFactory(new Aspect());// 1. 高级切面转低级切面类List<Advisor> list = new ArrayList<>();for (Method method : Aspect.class.getDeclaredMethods()) {if (method.isAnnotationPresent(Before.class)) {// 解析切点String expression = method.getAnnotation(Before.class).value();AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();pointcut.setExpression(expression);// 通知类AspectJMethodBeforeAdvice advice = new AspectJMethodBeforeAdvice(method, pointcut, factory);// 切面Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);list.add(advisor);} else if (method.isAnnotationPresent(AfterReturning.class)) {// 解析切点String expression = method.getAnnotation(AfterReturning.class).value();AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();pointcut.setExpression(expression);// 通知类AspectJAfterReturningAdvice advice = new AspectJAfterReturningAdvice(method, pointcut, factory);// 切面Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);list.add(advisor);} else if (method.isAnnotationPresent(Around.class)) {// 解析切点String expression = method.getAnnotation(Around.class).value();AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();pointcut.setExpression(expression);// 通知类AspectJAroundAdvice advice = new AspectJAroundAdvice(method, pointcut, factory);// 切面Advisor advisor = new DefaultPointcutAdvisor(pointcut, advice);list.add(advisor);}}for (Advisor advisor : list) {System.out.println(advisor);}/*@Before 前置通知会被转换为下面原始的 AspectJMethodBeforeAdvice 形式, 该对象包含了如下信息a. 通知代码从哪儿来b. 切点是什么c. 通知对象如何创建, 本例共用同一个 Aspect 对象类似的通知还有1. AspectJAroundAdvice (环绕通知)2. AspectJAfterReturningAdvice3. AspectJAfterThrowingAdvice (环绕通知)4. AspectJAfterAdvice (环绕通知)*/// 2. 通知统一转换为环绕通知 MethodInterceptor/*其实无论 ProxyFactory 基于哪种方式创建代理, 最后干活(调用 advice)的是一个 MethodInvocation 对象a. 因为 advisor 有多个, 且一个套一个调用, 因此需要一个调用链对象, 即 MethodInvocationb. MethodInvocation 要知道 advice 有哪些, 还要知道目标, 调用次序如下将 MethodInvocation 放入当前线程|-> before1 ----------------------------------- 从当前线程获取 MethodInvocation|                                             ||   |-> before2 --------------------          | 从当前线程获取 MethodInvocation|   |                              |          ||   |   |-> target ------ 目标   advice2    advice1|   |                              |          ||   |-> after2 ---------------------          ||                                             ||-> after1 ------------------------------------c. 从上图看出, 环绕通知才适合作为 advice, 因此其他 before、afterReturning 都会被转换成环绕通知d. 统一转换为环绕通知, 体现的是设计模式中的适配器模式- 对外是为了方便使用要区分 before、afterReturning- 对内统一都是环绕通知, 统一用 MethodInterceptor 表示此步获取所有执行时需要的 advice (静态)a. 即统一转换为 MethodInterceptor 环绕通知, 这体现在方法名中的 Interceptors 上b. 适配如下- MethodBeforeAdviceAdapter 将 @Before AspectJMethodBeforeAdvice 适配为 MethodBeforeAdviceInterceptor- AfterReturningAdviceAdapter 将 @AfterReturning AspectJAfterReturningAdvice 适配为 AfterReturningAdviceInterceptor*/Target target = new Target();ProxyFactory proxyFactory = new ProxyFactory();proxyFactory.setTarget(target);proxyFactory.addAdvice(ExposeInvocationInterceptor.INSTANCE); // 准备把 MethodInvocation 放入当前线程proxyFactory.addAdvisors(list);System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");List<Object> methodInterceptorList = proxyFactory.getInterceptorsAndDynamicInterceptionAdvice(Target.class.getMethod("foo"), Target.class);for (Object o : methodInterceptorList) {System.out.println(o);}System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");// 3. 创建并执行调用链 (环绕通知s + 目标)MethodInvocation methodInvocation = new ReflectiveMethodInvocation(null, target, Target.class.getMethod("foo"), new Object[0], Target.class, methodInterceptorList);methodInvocation.proceed();/*学到了什么a. 无参数绑定的通知如何被调用b. MethodInvocation 编程技巧: 拦截器、过滤器等等实现都与此类似c. 适配器模式在 Spring 中的体现*/}
}

2_模拟 MethodInvocation


注意点:

  1. proceed() 方法调用链中下一个环绕通知
  2. 每个环绕通知内部继续调用 proceed()
  3. 调用到没有更多通知了, 就调用目标方法

MethodInvocation 的编程技巧在实现拦截器、过滤器时能用上

package org.springframework.aop.framework;import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;import java.lang.reflect.AccessibleObject;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;/*模拟调用链过程, 是一个简单的递归过程1. proceed() 方法调用链中下一个环绕通知2. 每个环绕通知内部继续调用 proceed()3. 调用到没有更多通知了, 就调用目标方法*/
public class A18_1 {static class Target {public void foo() {System.out.println("Target.foo()");}}static class Advice1 implements MethodInterceptor {public Object invoke(MethodInvocation invocation) throws Throwable {System.out.println("Advice1.before()");Object result = invocation.proceed();// 调用下一个通知或目标System.out.println("Advice1.after()");return result;}}static class Advice2 implements MethodInterceptor {public Object invoke(MethodInvocation invocation) throws Throwable {System.out.println("Advice2.before()");Object result = invocation.proceed();// 调用下一个通知或目标System.out.println("Advice2.after()");return result;}}static class MyInvocation implements MethodInvocation {private Object target;  // 1private Method method;private Object[] args;List<MethodInterceptor> methodInterceptorList; // 2private int count = 1; // 调用次数public MyInvocation(Object target, Method method, Object[] args, List<MethodInterceptor> methodInterceptorList) {this.target = target;this.method = method;this.args = args;this.methodInterceptorList = methodInterceptorList;}@Overridepublic Method getMethod() {return method;}@Overridepublic Object[] getArguments() {return args;}@Overridepublic Object proceed() throws Throwable { // 调用每一个环绕通知, 调用目标if (count > methodInterceptorList.size()) {// 调用目标, 返回并结束递归return method.invoke(target, args);}// 逐一调用通知, count + 1MethodInterceptor methodInterceptor = methodInterceptorList.get(count++ - 1);return methodInterceptor.invoke(this);}@Overridepublic Object getThis() {return target;}@Overridepublic AccessibleObject getStaticPart() {return method;}}public static void main(String[] args) throws Throwable {Target target = new Target();List<MethodInterceptor> list = new ArrayList<>();list.add(new Advice1());list.add(new Advice2());MyInvocation invocation = new MyInvocation(target, Target.class.getMethod("foo"), new Object[0], list);invocation.proceed();}
}

5_动态通知调用


注意点:

  1. 通过 proxyFactory 的 getInterceptorsAndDynamicInterceptionAdvice() 将其他通知统一转换为 MethodInterceptor 环绕通知
  2. 所谓动态通知,体现在上面方法的 DynamicInterceptionAdvice 部分,这些通知调用时因为要为通知方法绑定参数,还需再次利用切点表达式
  3. 动态通知调用复杂程度高,性能较低
package org.springframework.aop.framework.autoproxy;import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.springframework.aop.Advisor;
import org.springframework.aop.aspectj.annotation.AnnotationAwareAspectJAutoProxyCreator;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.framework.ReflectiveMethodInvocation;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;import java.lang.reflect.Field;
import java.util.List;public class A19 {@Aspectstatic class MyAspect {@Before("execution(* foo(..))") // 静态通知调用,不带参数绑定,执行时不需要切点public void before1() {System.out.println("before1");}@Before("execution(* foo(..)) && args(x)") // 动态通知调用,需要参数绑定,执行时还需要切点对象public void before2(int x) {System.out.printf("before2(%d)%n", x);}}static class Target {public void foo(int x) {System.out.printf("target foo(%d)%n", x);}}@Configurationstatic class MyConfig {@BeanAnnotationAwareAspectJAutoProxyCreator proxyCreator() {return new AnnotationAwareAspectJAutoProxyCreator();}@Beanpublic MyAspect myAspect() {return new MyAspect();}}public static void main(String[] args) throws Throwable {GenericApplicationContext context = new GenericApplicationContext();context.registerBean(ConfigurationClassPostProcessor.class);context.registerBean(MyConfig.class);context.refresh();AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);List<Advisor> list = creator.findEligibleAdvisors(Target.class, "target");Target target = new Target();ProxyFactory factory = new ProxyFactory();factory.setTarget(target);factory.addAdvisors(list);Object proxy = factory.getProxy(); // 获取代理List<Object> interceptorList = factory.getInterceptorsAndDynamicInterceptionAdvice(Target.class.getMethod("foo", int.class), Target.class);for (Object o : interceptorList) {showDetail(o);}System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>");ReflectiveMethodInvocation invocation = new ReflectiveMethodInvocation(proxy, target, Target.class.getMethod("foo", int.class), new Object[]{100}, Target.class, interceptorList) {};invocation.proceed();/*学到了什么a. 有参数绑定的通知调用时还需要切点,对参数进行匹配及绑定b. 复杂程度高, 性能比无参数绑定的通知调用低*/}public static void showDetail(Object o) {try {Class<?> clazz = Class.forName("org.springframework.aop.framework.InterceptorAndDynamicMethodMatcher");if (clazz.isInstance(o)) {Field methodMatcher = clazz.getDeclaredField("methodMatcher");methodMatcher.setAccessible(true);Field methodInterceptor = clazz.getDeclaredField("interceptor");methodInterceptor.setAccessible(true);System.out.println("环绕通知和切点:" + o);System.out.println("\t切点为:" + methodMatcher.get(o));System.out.println("\t通知为:" + methodInterceptor.get(o));} else {System.out.println("普通环绕通知:" + o);}} catch (Exception e) {e.printStackTrace();}}
}

补充—— Advice 常见子接口:

增强类型常见 Advice 子接口/子类描述
前置通知MethodBeforeAdvice方法调用前执行
后置通知AfterReturningAdvice方法调用后且正常返回时执行
环绕通知MethodInterceptor方法调用前后都可增强,控制流程
异常通知ThrowsAdvice方法抛出异常时增强
引介通知DelegatingIntroductionInterceptor为目标对象动态添加接口实现

在这里插入图片描述

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

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

相关文章

头歌——VLAN基本配置第一关

任务描述 本关任务&#xff1a;实现跨交换机的VLAN。 相关知识 为了完成本关任务&#xff0c;你需要掌握&#xff1a;1.VLAN的定义&#xff0c;2.VLAN的类型。 实验步骤 &#xff08;1&#xff09;新建Packet Tracer拓扑图&#xff1b; &#xff08;2&#xff09;划分VLAN&…

CentOS使用中遇到的问题及解决方法

一、CentOS 7网络配置&#xff08;安装后无法联网问题&#xff09; 现象说明 在安装CentOS系统后&#xff0c;有可能出现无法联网的问题&#xff0c;虚拟机中的网络配置并没有问题&#xff0c;而系统却无法联网,也ping不通。 原因描述 CentOS默认开机不启动网络&#xff0c;因…

【AI+教育】一些记录@2024.11.16

《万字长文&#xff0c;探讨关于ChatGPT的五个最核心问题》 万字长文&#xff0c;探讨关于ChatGPT的五个最核心问题关于 ChatGPT 铺天盖地的信息让人无所适从。本文则试图提炼出五个关键问题&#xff1a;如何理解这次范式突破&#xff0c;未来能达到的技术天花板&#xff0c;行…

CEF编译指南2024 Windows篇-CEF简介(一)

1. 引言 在现代桌面应用程序开发中&#xff0c;Web技术的应用越来越广泛。许多开发者希望能够在传统桌面应用中嵌入Web内容&#xff0c;既保留了原生应用的性能优势&#xff0c;又能享受Web开发的便利性。CEF&#xff08;Chromium Embedded Framework&#xff09;作为一个基于…

机器翻译-基础与模型

一、机器翻译发展历程 基于规则的-->基于实例的-->基于统计方法的-->基于神经网络的 传统统计机器翻译把词序列看作离散空间里的由多个特征函数描述的点&#xff0c;类似 于 n-gram 语言模型&#xff0c;这类模型对数据稀疏问题非常敏感。神经机器翻译把文字序列表示…

计算机网络——路由选择算法

路由算法 路由的计算都是以子网为单位计算的——找到从原子网到目标子网的路径 链路状态算法 序号——&#xff08;源路由器&#xff0c;序号&#xff09;——如果发现这个序号重复或者老了——就不扩散 先测量——再泛洪获得路由 路由转发情况 若S——>W是21则不更改——…

【金融风控项目-07】:业务规则挖掘案例

文章目录 1.规则挖掘简介2 规则挖掘案例2.1 案例背景2.2 规则挖掘流程2.3 特征衍生2.4 训练决策树模型2.5 利用结果划分分组 1.规则挖掘简介 两种常见的风险规避手段&#xff1a; AI模型规则 如何使用规则进行风控 **使用一系列逻辑判断(以往从职人员的经验)**对客户群体进行区…

实时监控,智能分析:输电线路多目视频监控装置提升运维效率

在快速变迁的现代社会中&#xff0c;安全监控技术已成为各行各业安全管理体系的核心组成部分。无论是工厂生产线的安全保障&#xff0c;城市治安的维护&#xff0c;还是偏远区域电力巡检的顺利进行&#xff0c;都离不开高效且智能的监控解决方案。 在高压输电线路的监测领域&am…

【MCU】GD32H7定时器使用外部时钟源

1、定时器可以使用系统内部的mcuclk&#xff0c;也可以通过管脚使用外部输入的时钟源 2、GD32H7怎么使用外部的源来驱动定时器呢 3、GD32H7通用定时器的框图如下&#xff1a; 这是官方手册上的图&#xff0c;不得不说&#xff0c;画的不够详细&#xff0c;只是一个大概的框图…

IDEA2023 SpringBoot整合Web开发(二)

一、SpringBoot介绍 由Pivotal团队提供的全新框架&#xff0c;其设计目的是用来简化Spring应用的初始搭建以及开发过程。该框架使用了特定的方式来进行配置&#xff0c;从而使开发人员不再需要定义样板化的配置。SpringBoot提供了一种新的编程范式&#xff0c;可以更加快速便捷…

英伟达AI超大规模组网平台方案

英伟达GPU&#xff1a;AI超大规模组网 "英伟达&#xff0c;引领GPU技术革新&#xff0c;持续加速AI超大规模组网进程。自2024年起&#xff0c;英伟达每2年将推出一代新型GPU架构&#xff0c;如今的Blackwell芯片已投入生产。展望未来&#xff0c;2025年&#xff0c;我们将…

计算机网络:概述知识点及习题练习

网课资源&#xff1a; 湖科大教书匠 1、因特网 网络之间需要路由器进行互联&#xff0c;互联网是网络的网络&#xff0c;因特网是最大的互联网&#xff0c;连接到网络的设备称为主机&#xff0c;一般不叫路由器为主机。 因特网发展&#xff1a;ARPNET->三级结构因特网&am…

makefile速通

makefile速通 文章目录 makefile速通1.基础显式规则隐含规则%*通配符 赋值 伪目标CFLAGS 2.函数wildcardpatsubst 3.项目实例 1.基础 显式规则 目标文件&#xff1a;依赖文件 [TAB] 指令隐含规则 % 任意* 所有通配符 符号含义$^所有依赖文件$所有目标文件$<所有依赖文…

分数加减

#include <stdio.h> #include <stdlib.h>// 求最大公因数 int gcd(int a, int b) {return b 0? a : gcd(b, a % b); }// 化简分数 void simplify(int *num, int *den) {int g gcd(*num, *den);*num / g;*den / g;if (*den < 0) {*num * -1;*den * -1;} }//…

Misc_01转二维码(不是二进制)

例题ctfhub/隐写v2.0 打开是一张图片 文件分离得到zip&#xff0c;爆破密码得到7878 打开得到0和1&#xff0c; !!!不是二进制转图片&#xff0c;直接是二维码 缩小能看到 000000000000000000000000000000000000000000000000000000000000000000000 000000000000000000000000…

如何修改网络ip地址:一步步指南‌

在当今这个数字化时代&#xff0c;网络已成为我们日常生活与工作中不可或缺的一部分。无论是浏览网页、在线办公还是享受流媒体服务&#xff0c;稳定的网络连接和适当的IP地址管理都是确保良好体验的关键。然而&#xff0c;出于隐私保护、绕过地理限制或测试网络环境等需要&…

STL——vector(1)

博客ID&#xff1a;LanFuRenC系列专栏&#xff1a;C语言重点部分 C语言注意点 C基础 Linux 数据结构 C注意点 今日好题 声明等级&#xff1a;黑色->蓝色->红色 欢迎新粉加入&#xff0c;会一直努力提供更优质的编程博客&#xff0c;希望大家三连支持一下啦 目录 尾…

iOS 18 导航栏插入动画会导致背景短暂变白的解决

问题现象 在最新的 iOS 18 系统中,如果我们执行导航栏的插入动画,可能会造成导航栏背景短暂地变为白色: 如上图所示:我们分别向主视图和 Sheet 弹出视图的导航栏插入了消息,并应用了动画效果。可以看到,前者的导航栏背景会在消息插入那一霎那“变白”,而后者则没有任何…

SPP:空间金字塔池化

今天水一篇博客&#xff0c;讲讲SPP池化结构&#xff1b;那这是个什么东西呢&#xff1f;它的作用又是什么呢&#xff1f;在了解它之前我们先简单了解一下大部分的神经网络&#xff1b; 引入&#xff1a; 在大部分的神经网络中&#xff0c;都将神经网络分为Backbone主干网络、…

网络安全与防范

1.重要性 随着互联网的发达&#xff0c;各种WEB应用也变得越来越复杂&#xff0c;满足了用户的各种需求&#xff0c;但是随之而来的就是各种网络安全的问题。了解常见的前端攻击形式和保护我们的网站不受攻击是我们每个优秀fronter必备的技能。 2.分类 XSS攻击CSRF攻击网络劫…