AOP原理(二)切面和切点

笔记参考B站黑马程序员视频:spring原理

1.spring代理选择

AOP(面向切面编程)中的两个重要概念:切面(aspect)和切点(pointcut)。

  • 切面(Aspect):切面是横切关注点的模块化,它包含了通知(advice)和切点(pointcut)的组合。在AOP中,切面可以看作是一种将特定行为横向切割出来的模块,用于对系统的多个部分进行横切式的功能增强。一个切面可以包含多个通知和对应的切点。
  • 切点(Pointcut):切点定义了在哪些连接点上应用通知。通常,切点使用表达式来描述哪些方法将被拦截并应用通知。切点实际上是一个条件,它决定了通知应该在哪里执行。
  • 通知(Advice):通知是切面的具体行为(具体方法),它定义了在连接点上执行的操作。常见的通知类型包括前置通知(Before advice)、后置通知(After advice)、环绕通知(Around advice)、异常通知(After throwing advice)等。
  • Advisor:Advisor是Spring AOP框架中的一个概念,用于描述细粒度的切面。一个Advisor包含一个通知(Advice)和一个切点(Pointcut)。Advisor用于提供更细粒度的控制,可以选择性地在特定的切点上应用通知。

 

切点Pointcut

实现类

使用的示例代码:

加入依赖:

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
</dependency>

全部代码如下:

package com.dreams.demo14;

import org.aopalliance.intercept.MethodInterceptor;
import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.DefaultPointcutAdvisor;

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

        // 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);

        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();
    }

    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");
        }
    }
}

运行结果:

 

代码解释:

AspectJExpressionPointcut 是 Spring Framework 中用于定义切点表达式的类。它允许开发人员使用 AspectJ 风格的表达式来描述在哪些连接点上应该应用通知。在 Spring AOP 中,切点表达式可以用于配置切面,以指定在哪些方法或类上应用通知。

// 1. 备好切点
AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
pointcut.setExpression("execution(* foo())");

 

使用MethodInterceptor (注意不是CGLIB的同名)需要实现 invoke 方法,该方法接收一个 MethodInvocation 对象作为参数,然后可以在目标方法被调用之前或之后执行额外的逻辑。

// 2. 备好通知
MethodInterceptor advice = invocation -> {
    System.out.println("before...");
    Object result = invocation.proceed(); // 调用目标
    System.out.println("after...");
    return result;
};

 

DefaultPointcutAdvisor 是 Spring Framework 中用于定义切面(Aspect)的一个常用类。它实现了 Advisor 接口,用于将特定的通知(Advice)应用于特定的切点(Pointcut)。

// 3. 备好切面
DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice);

 

ProxyFactory factory = new ProxyFactory()创建了一个 ProxyFactory 对象,用于创建代理对象,这个代理对象可以拦截并增强目标对象的方法调用,以实现横切关注点(cross-cutting concerns)的功能。。factory.setTarget(target); 设置目标对象为 factory 的目标。factory.addAdvisor(advisor); 向 factory 中添加了一个切面通知器(Advisor),这个通知器包括了切点和通知。factory.setInterfaces(target.getClass().getInterfaces()); 设置代理对象实现的接口,这里是使用目标对象的接口作为代理对象的接口,这里只有设置进去ProxyTargetClass属性才能生效。factory.setProxyTargetClass(false); 设置是否使用 CGLIB 动态代理,默认为 false,表示不强制使用 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();

上面的代码底层选择使用JDK动态代理或CGLIB代理,关键是proxyTargetClass属性

proxyTargetClass = false, 目标实现了接口, 用 jdk 实现
proxyTargetClass = false, 目标没有实现接口, 用 cglib 实现
proxyTargetClass = true, 总是使用 cglib 实现

 

2.切点匹配

匹配使用matches 方法,作用是判断某个方法是否匹配指定的切点表达式。

package com.dreams.demo16;

import org.springframework.aop.aspectj.AspectJExpressionPointcut;
import org.springframework.transaction.annotation.Transactional;

public class Demo {
    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));

    }


    static class T1 {
        @Transactional
        public void foo() {
        }
        public void bar() {
        }
    }

    @Transactional
    static class T2 {
        public void foo() {
        }
    }

    @Transactional
    interface I3 {
        void foo();
    }
    static class T3 implements I3 {
        public void foo() {
        }
    }
}

pt2.setExpression(“@annotation(org.springframework.transaction.annotation.Transactional)”); 设置了切点表达式,这里使用了 AspectJ 的注解方式来定义切点,表示匹配带有 @Transactional 注解的方法。pt2.matches(T1.class.getMethod(“foo”), T1.class) 调用 matches 方法,传入要匹配的方法对象以及目标类对象,判断指定的方法是否匹配切点。

matches 方法用于判断某个方法是否匹配指定的切点表达式。

运行

不过上面的方法只能注解加在方法上的

 

所以spring底层使用另一种方法

public static void main(String[] args) throws NoSuchMethodException {

    StaticMethodMatcherPointcut pt3 = new StaticMethodMatcherPointcut() {
        @Override
        public 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));
}

方法首先检查传入的方法对象 (method) 是否带有 @Transactional 注解。使用 MergedAnnotations.from(method) 获取方法上的注解并检查是否存在 @Transactional 注解。如果方法上没有注解,再检查类级别的注解。使用 MergedAnnotations.from(targetClass,MergedAnnotations.SearchStrategy.TYPE_HIERARCHY) 获取类及其层次结构上的注解,并检查是否存在 @Transactional 注解。

 

3.@Aspect与Advisor

定义一个高级切面和低级切面

package com.dreams.autoproxy;

import org.aopalliance.intercept.MethodInterceptor;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;
import org.springframework.core.annotation.Order;

public class Demo {
    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.refresh();
        for (String name : context.getBeanDefinitionNames()) {
            System.out.println(name);
        }
    }

    static class Target1 {
        public void foo() {
            System.out.println("target1 foo");
        }
    }

    static class Target2 {
        public void bar() {
            System.out.println("target2 bar");
        }
    }

    @Aspect // 高级切面类
    @Order(1)
    static class Aspect1 {
        @Before("execution(* foo())")
        public void before1() {
            System.out.println("aspect1 before1...");
        }

        @Before("execution(* foo())")
        public void before2() {
            System.out.println("aspect1 before2...");
        }
    }

    @Configuration
    static class Config {
        @Bean // 低级切面
        public Advisor advisor3(MethodInterceptor advice3) {
            AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
            pointcut.setExpression("execution(* foo())");
            DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice3);
            return advisor;
        }
        @Bean
        public MethodInterceptor advice3() {
            return invocation -> {
                System.out.println("advice3 before...");
                Object result = invocation.proceed();
                System.out.println("advice3 after...");
                return result;
            };
        }
    }

}

运行如下:

要想处理上面的切面,就得加入专门的Bean处理器

context.registerBean(AnnotationAwareAspectJAutoProxyCreator.class);

AnnotationAwareAspectJAutoProxyCreator 的主要责任是在 Spring 容器启动时,检测带有 @Aspect 注解的类,并为它们创建动态代理。这样一来,被 @Aspect 注解的类就可以作为切面类来实现 AOP 编程。

findEligibleAdvisors 方法用于查找符合条件的 Advisor。找的是低级切面,如果是高级切面需要转换成低级切面。

 

wrapIfNecessary判断是否需要代理,检查给定的 Bean 是否符合代理的条件,例如是否有匹配的 Advisor 或者切面。如果需要代理,则创建相应的代理对象。

 

不过这些方法都是受保护的,所以可以取同名包下来创建(java中不行,都是spring可以)。

复制上面的代码,放到同名包下

package org.springframework.aop.framework.autoproxy;

import org.aopalliance.intercept.MethodInterceptor;
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.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.ConfigurationClassPostProcessor;
import org.springframework.context.support.GenericApplicationContext;

import java.util.List;

public class A17 {
    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(Target2.class, "target2");
        for (Advisor advisor : advisors) {
            System.out.println(advisor);
        }
    }

    static class Target1 {
        public void foo() {
            System.out.println("target1 foo");
        }
    }

    static class Target2 {
        public void bar() {
            System.out.println("target2 bar");
        }
    }

    @Aspect // 高级切面类
    static class Aspect1 {
        @Before("execution(* foo())")
        public void before1() {
            System.out.println("aspect1 before1...");
        }

        @Before("execution(* foo())")
        public void before2() {
            System.out.println("aspect1 before2...");
        }
    }

    @Configuration
    static class Config {
        @Bean // 低级切面
        public Advisor advisor3(MethodInterceptor advice3) {
            AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
            pointcut.setExpression("execution(* foo())");
            DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice3);
            return advisor;
        }
        @Bean
        public MethodInterceptor advice3() {
            return invocation -> {
                System.out.println("advice3 before...");
                Object result = invocation.proceed();
                System.out.println("advice3 after...");
                return result;
            };
        }
    }

}

我们测试一下findEligibleAdvisors 方法,返回与之对应的advisors

AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
List<Advisor> advisors = creator.findEligibleAdvisors(Target2.class, "target2");
for (Advisor advisor : advisors) {
    System.out.println(advisor);
}

Target2没有符合切面管理,所以没有输出

换成Target1

输出了4个

org.springframework.aop.interceptor.ExposeInvocationInterceptor.ADVISOR是spring给所有切面都要加的,它会将 MethodInvocation 放入当前线程的上下文中。这使得在方法调用链中的其他地方(例如,在业务逻辑代码中)可以访问当前的 MethodInvocation,从而获得有关调用的详细信息。

org.springframework.aop.support.DefaultPointcutAdvisor就是我们上面定义的低级切面

剩下的两个InstantiationModelAwarePointcutAdvisor就是我们定义的高级切面转换后的低级切面

 

wrapIfNecessary 方法首先检查当前 Bean 是否应该跳过代理(可能基于某些配置或条件)。查找符合条件的 Advisor,内部会使用 findEligibleAdvisors 方法查找与当前 Bean 匹配的 Advisor。根据找到的 Advisor 和其他条件来判断是否需要为当前 Bean 创建代理。
如果需要代理,则实际创建代理对象(通常通过 JDK 动态代理或 CGLIB 代理)。如果创建了代理对象,则返回该代理对象;否则,返回原始的 Bean。

AnnotationAwareAspectJAutoProxyCreator creator = context.getBean(AnnotationAwareAspectJAutoProxyCreator.class);
Object o1 = creator.wrapIfNecessary(new Target1(), "target1", "target1");
System.out.println(o1.getClass());
Object o2 = creator.wrapIfNecessary(new Target2(), "target2", "target2");
System.out.println(o2.getClass());

((Target1) o1).foo();

 

高级切面与低级切面的执行顺序

@Aspect // 高级切面类
static class Aspect1 {
    @Before("execution(* foo())")
    public void before1() {
        System.out.println("aspect1 before1...");
    }

    @Before("execution(* foo())")
    public void before2() {
        System.out.println("aspect1 before2...");
    }

    @After("execution(* foo())")
    public void after() {
        System.out.println("aspect1 after...");
    }
}

@Configuration
static class Config {
    @Bean // 低级切面
    public Advisor advisor3(MethodInterceptor advice3) {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("execution(* foo())");
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice3);
        return advisor;
    }
    @Bean
    public MethodInterceptor advice3() {
        return invocation -> {
            System.out.println("advice3 before...");
            Object result = invocation.proceed();
            System.out.println("advice3 after...");
            return result;
        };
    }
}

默认顺序

 

如果给高级切面加上@Order(1)

@Aspect // 高级切面类
@Order(1)
static class Aspect1 {
    @Before("execution(* foo())")
    public void before1() {
        System.out.println("aspect1 before1...");
    }

    @Before("execution(* foo())")
    public void before2() {
        System.out.println("aspect1 before2...");
    }

    @After("execution(* foo())")
    public void after() {
        System.out.println("aspect1 after...");
    }
}

如果低级切面想要设置,就不能加注解,而是要直接代码配置。

@Configuration
static class Config {
    @Bean // 低级切面
    public Advisor advisor3(MethodInterceptor advice3) {
        AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
        pointcut.setExpression("execution(* foo())");
        DefaultPointcutAdvisor advisor = new DefaultPointcutAdvisor(pointcut, advice3);
        advisor.setOrder(2);
        return advisor;
    }
    @Bean
    public MethodInterceptor advice3() {
        return invocation -> {
            System.out.println("advice3 before...");
            Object result = invocation.proceed();
            System.out.println("advice3 after...");
            return result;
        };
    }
}

 

 

4.代理对象创建时机

看如下代码:

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 Demo {

    public static void main(String[] args) {
        GenericApplicationContext context = new GenericApplicationContext();
        context.registerBean(ConfigurationClassPostProcessor.class);
        context.registerBean(Config.class);
        context.refresh();
        context.close();
        // 创建 -> (*) 依赖注入 -> 初始化 (*)
    }

    @Configuration
    static class Config {
        @Bean // 解析 @Aspect、产生代理
        public AnnotationAwareAspectJAutoProxyCreator annotationAwareAspectJAutoProxyCreator() {
            return new AnnotationAwareAspectJAutoProxyCreator();
        }

        @Bean // 解析 @Autowired
        public AutowiredAnnotationBeanPostProcessor autowiredAnnotationBeanPostProcessor() {
            return new AutowiredAnnotationBeanPostProcessor();
        }

        @Bean // 解析 @PostConstruct
        public CommonAnnotationBeanPostProcessor commonAnnotationBeanPostProcessor() {
            return new CommonAnnotationBeanPostProcessor();
        }

        @Bean
        public Advisor advisor(MethodInterceptor advice) {
            AspectJExpressionPointcut pointcut = new AspectJExpressionPointcut();
            pointcut.setExpression("execution(* foo())");
            return new DefaultPointcutAdvisor(pointcut, advice);
        }

        @Bean
        public MethodInterceptor advice() {
            return (MethodInvocation invocation) -> {
                System.out.println("before...");
                return invocation.proceed();
            };
        }

        @Bean
        public Bean1 bean1() {
            return new Bean1();
        }

        @Bean
        public Bean2 bean2() {
            return new Bean2();
        }
    }

    static class Bean1 {
        public void foo() {

        }
        public Bean1() {
            System.out.println("Bean1()");
        }
        @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()");
        }
    }
}

Bean1 类有一个 foo() 方法和一个 @PostConstruct 注解的 init() 方法。
Bean2 类在构造函数中输出,同时通过 @Autowired 注解注入 Bean1 实例。 这种情况也就是无循环依赖时,代理的创建时机是初始化(@PostConstruct注init方法)之后。如下图:

如果Bean1同样注入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()");
    }
}

如下图:

这里的顺序是:

  1. Bean1() 和 Bean2() 的构造函数被调用。Bean1() 和 Bean2() 的初始化是在Bean实例化时发生的,这时还没有完成依赖注入。
  2. Spring 使用了AOP(面向切面编程)来创建代理,这是由于使用了Spring的自动代理功能,如 AnnotationAwareAspectJAutoProxyCreator 提示的那样。这里创建Bean1的代理。
  3. Bean2 的 setBean1(bean1) 方法被调用,传入的参数是 Bean1 的代理对象,即 Demo$Bean1$$EnhancerBySpringCGLIB$$b5922e5b。
    这里涉及到Spring通过CGLIB动态代理技术为 Bean1 生成的代理对象。
  4. Bean2 的初始化方法 init() 被调用。
    init() 方法是在 Bean2 的所有依赖注入完成后调用的初始化方法。
  5. Bean1 的 setBean2(bean2) 方法被调用,传入的参数是 Bean2 的原始实例。
  6. Bean1 的初始化方法 init() 被调用。init() 方法是在 Bean1 的所有依赖注入完成后调用的初始化方法。

如下图:

创建代理对象在初始化(@PostConstruct注init方法)之前了

可以看到依赖注入与初始化不被增强, 仍被施加于原始对象

 

 

5.高级切面转换低级切面

高级切面转换低级切面示例代码

package org.springframework.aop.framework.autoproxy;

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 Test {

    static class Aspect {
        @Before("execution(* foo())")
        public void before1() {
            System.out.println("before1");
        }

        @Before("execution(* foo())")
        public void before2() {
            System.out.println("before2");
        }
    }

    static class Target {
        public void foo() {
            System.out.println("target foo");
        }
    }

    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);
        }

    }
}

通过反射获取 Aspect 类中的所有方法,并检查哪些方法标有 @Before 注解。对于每个标有 @Before 注解的方法:获取注解中的切点表达式。创建一个 AspectJExpressionPointcut,并设置其表达式。创建一个 AspectJMethodBeforeAdvice,它将通知方法、切点和实例工厂关联起来。创建一个 DefaultPointcutAdvisor,将切点和通知关联起来。将创建的 Advisor 添加到列表中。

@Before 前置通知会被转换为下面原始的 AspectJMethodBeforeAdvice 形式,该对象包含了如下信息

  • a. 通知代码从哪儿来
  • b. 切点是什么(这里为啥要切点, 后面解释)
  • c. 通知对象如何创建, 本例共用同一个 Aspect 对象

如果是其他通知,转换为类似的形式:

  •  AspectJAroundAdvice (环绕通知)
  • AspectJAfterReturningAdvice
  •  AspectJAfterThrowingAdvice
  •  AspectJAfterAdvice

 

6.静态通知调用

不同通知统一转换为环绕通知

其实无论 ProxyFactory 基于哪种方式创建代理, 最后干活(调用 advice)的是一个 MethodInvocation 对象:

  • 因为 advisor 有多个,且一个套一个调用, 因此需要一个调用链对象,即 MethodInvocation
  • MethodInvocation 要知道 advice 有哪些,还要知道目标

如图:

从上图看出, 环绕通知才适合作为 advice,因此其他 before、afterReturning 都会被转换成环绕通知,统一转换为环绕通知,体现的是设计模式中的适配器模式,对外是为了方便使用要区分 before、afterReturning,对内统一都是环绕通知,统一用 MethodInterceptor 表示。

示例代码:

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.interceptor.ExposeInvocationInterceptor;
import org.springframework.aop.support.DefaultPointcutAdvisor;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

public class Demo {

    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);
        }

    }
}

先简单输出,可以看到对应的四个对象

 

通知统一转换为环绕通知 MethodInterceptor

proxyFactory.addAdvice(ExposeInvocationInterceptor.INSTANCE);是spring给所有切面都要加的,它会将 MethodInvocation 放入当前线程的上下文中。这使得在方法调用链中的其他地方(例如,在业务逻辑代码中)可以访问当前的 MethodInvocation,从而获得有关调用的详细信息

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);
}

下面转换的4个对象都是实现了 MethodInterceptor

此步获取所有执行时需要的 advice (静态)即统一转换为 MethodInterceptor 环绕通知, 这体现在方法名中的 Interceptors 上

适配如下:

  • MethodBeforeAdviceAdapter 将 @Before AspectJMethodBeforeAdvice 适配为 MethodBeforeAdviceInterceptor
  • AfterReturningAdviceAdapter 将 @AfterReturning AspectJAfterReturningAdvice 适配为 AfterReturningAdviceInterceptor

例如:

 

创建并执行调用链 (环绕通知s + 目标)

System.out.println(">>>>>>>>>>>>>>>>>>>>>>>>>>>>>>");
// 3. 创建并执行调用链 (环绕通知s + 目标)
MethodInvocation methodInvocation = new ReflectiveMethodInvocation(
        null, target, Target.class.getMethod("foo"), new Object[0], Target.class, methodInterceptorList
);
// 执行
methodInvocation.proceed();

 

7.动态通知调用

动态通知调用,需要参数绑定,执行时还需要切点对象,复杂程度高,性能比无参数绑定的通知调用低

对应的对象如下:

InterceptorAndDynamicMethodMatcher 里有一个切点对象和通知对象

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 {

    @Aspect
    static 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);
        }
    }

    @Configuration
    static class MyConfig {
        @Bean
        AnnotationAwareAspectJAutoProxyCreator proxyCreator() {
            return new AnnotationAwareAspectJAutoProxyCreator();
        }

        @Bean
        public 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();
    }

    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();
        }
    }
}

 

 

8.参考

黑马程序员:spring

暂无评论

发送评论 编辑评论

|´・ω・)ノ
ヾ(≧∇≦*)ゝ
(☆ω☆)
(╯‵□′)╯︵┴─┴
 ̄﹃ ̄
(/ω\)
∠( ᐛ 」∠)_
(๑•̀ㅁ•́ฅ)
→_→
୧(๑•̀⌄•́๑)૭
٩(ˊᗜˋ*)و
(ノ°ο°)ノ
(´இ皿இ`)
⌇●﹏●⌇
(ฅ´ω`ฅ)
(╯°A°)╯︵○○○
φ( ̄∇ ̄o)
ヾ(´・ ・`。)ノ"
( ง ᵒ̌皿ᵒ̌)ง⁼³₌₃
(ó﹏ò。)
Σ(っ °Д °;)っ
( ,,´・ω・)ノ"(´っω・`。)
╮(╯▽╰)╭
o(*////▽////*)q
>﹏<
( ๑´•ω•) "(ㆆᴗㆆ)
😂
😀
😅
😊
🙂
🙃
😌
😍
😘
😜
😝
😏
😒
🙄
😳
😡
😔
😫
😱
😭
💩
👻
🙌
🖕
👍
👫
👬
👭
🌚
🌝
🙈
💊
😶
🙏
🍦
🍉
😣
Source: github.com/k4yt3x/flowerhd
颜文字
Emoji
小恐龙
花!
上一篇
下一篇