spring aop(一) pointcut 和 advicor

本文详细解析了Spring AOP的实现原理,包括如何通过ProxyFactory创建代理对象、添加Advice和Pointcut的具体过程,并通过一个具体示例展示了不同增强处理的效果。

先看一个使用 Spring AOP 代理的 Demo, 理解最初流程

public class ProxyFactoryDemo {
    public static void main(String[] args) {
        // 1. 构造目标对象
        Cat catTarget = new Cat();
        // 2. 通过 target 对象,构造 ProxyFactory 对象
        ProxyFactory factory = new ProxyFactory(catTarget);
        // 添加一个方法拦截器
        factory.addAdvice(new MyMethodInterceptor());
        // 3. 根据目标对象生成代理对象
        Object proxy = factory.getProxy();
        Animal cat = (Animal) proxy;
        cat.eat();
    }

	// 增强处理逻辑
    public static class MyMethodInterceptor implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            System.out.println("MyMethodInterceptor invoke 调用 before invocation.proceed");

            Object ret = invocation.proceed();

            System.out.println("MyMethodInterceptor invoke 调用 after invocation.proceed");
            return ret;
        }
    }
}

1. 实例化 ProxyFactory 对象 new ProxyFactory(catTarget) 的过程

// ProxyFactory.java
public ProxyFactory(Object target) {
	setTarget(target);  // 设置目标对象
	setInterfaces(ClassUtils.getAllInterfaces(target));   // 设置目标对象实现的接口
}

2. 为 ProxyFactory 对象添加拦截器 factory.addAdvice(new MyMethodInterceptor()); 的过程
首先,我们加入的 Advice 自己实现的 MyMethodInterceptor 对象,该对象继承了 MethodInterceptor 接口 。 MethodInterceptor 接口继承关系如下图:

接下来, 如下代码可以看出, addAdvice() 的动作做法是, 向内部的 List<Advisor> 集合中加入一个 DefaultPointcutAdvisor 对象. 是职责链模式的体现.

// ProxyFactory.java
private List<Advisor> advisors = new ArrayList<>();

public void addAdvice(Advice advice) throws AopConfigException {
	int pos = this.advisors.size();
	addAdvisor(pos, new DefaultPointcutAdvisor(advice));
}
public void addAdvisor(int pos, Advisor advisor) throws AopConfigException {
	this.advisors.add(pos, advisor);
}
  • (1)先来看 DefaultPointcutAdvisor 的继承体系

    • Advisor 接口:持有 Advice 对象的基础接口, 可将二者看做对等借口
    • PointcutAdvisor 接口:包含切入点 的 Advisor,从而可以针对符合 Pointcut 规则的连接点进行增强处理
    • Ordered 接口:用来确定当前 Advisor 在拦截器责任链列表中的位置,主要用在 Aspect 中

上面看到,PointcutAdvisor 持有 PointCut 接口 的引用。PointCut 接口 用来在代理时,判断被代理的对象和被代理的方法是否符合我们的要求, 该接口组合了 ClassFilter 接口和 MethodMatcher 接口。ClassFilter 接口和 MethodMatcher 都有一个返回 bool 类型的 match 方法,用来过滤被代理类和被代理方法

// Pointcut.java
public interface Pointcut {
	ClassFilter getClassFilter();
  	MethodMatcher getMethodMatcher();
}

// ClassFilter.java
public interface ClassFilter {
  boolean matches(Class<?> clazz);
}

// MethodMatcher.java
public interface MethodMatcher {
  /**
   * 对方法进行静态匹配,即不对方法调用的传入的实参进行校验
   */
  boolean matches(Method method, Class<?> targetClass);

  /**
   * 返回当前 MethodMatcher 是否需要进行动态匹配。
   *   如果 isRuntime() 方法返回 true,则表示需要调用 matches(Method, Class, Object[])方法对目标方法进行匹配
   */
  boolean isRuntime();

  /**
   * 对方法进行动态匹配,即对方法调用的传入的实参进行校验
   */
  boolean matches(Method method, Class<?> targetClass, Object... args);

}
  • (2)new DefaultPointcutAdvisor(advice) 做了什么、
// TruePointcut 对象组合了永远返回 true 的 ClassFilter 对象和 MethodMatcher 对象
// 表示匹配所有类的所有方法,即对所有方法进行增强处理
private Pointcut pointcut = Pointcut.TRUE;

public DefaultPointcutAdvisor(Advice advice) {
  this(Pointcut.TRUE, advice);
}

// 该方法用于自定义 Pointcut,实现 ClassFilter 和 MethodMatcher
public DefaultPointcutAdvisor(Pointcut pointcut, Advice advice) {
  this.pointcut = pointcut;
  setAdvice(advice);
}

3. 经过以上分析, PointcutAdvice(Advisor) 以及 PointcutAdvisor 的整体架构脉络我们就都清楚了, 下面我们自己实现切入点,体会加切入点和不加切入点的输出对比

  • PointcutAdvisor
    public class MyPointcutAdvisor implements PointcutAdvisor {
    
        private final Pointcut pointcut;
    
        private final Advice advice;
    
        public MyPointcutAdvisor(Advice advice,Pointcut pointcut) {
            this.advice = advice;
            this.pointcut = pointcut;
        }
    
        @Override
        public Pointcut getPointcut() {
            return this.pointcut;
        }
    
        @Override
        public Advice getAdvice() {
            return this.advice;
        }
    
        /**
         * 此方法暂时忽略,不需要理会
         */
        @Override
        public boolean isPerInstance() {
            return false;
        }
    }
    
    
    • PointCut
    public class MyPointcut implements Pointcut {
        @Override
        public ClassFilter getClassFilter() {
            return new ClassFilter() {
                @Override
                public boolean matches(Class<?> clazz) {
                    // 匹配所有的类
                    return true;
                }
            };
        }
    
        @Override
        public MethodMatcher getMethodMatcher() {
            // 继承 StaticMethodMatcher,忽略方法实参,只对方法进行动态匹配。
            return new StaticMethodMatcher() {
                @Override
                public boolean matches(Method method, Class<?> targetClass) {
                    // 如果方法名称是 go,则匹配,否则不匹配
                    if (method.getName().equals("go")) {
                        return true;
                    }
                    return false;
                }
            };
        }
    }
    
    • Advice (MethodInterceptor)
    public static class MyMethodInterceptor implements MethodInterceptor {
        @Override
        public Object invoke(MethodInvocation invocation) throws Throwable {
            System.out.println("MyMethodInterceptor invoke 调用 before invocation.proceed");
    
            Object ret = invocation.proceed();
    
            System.out.println("MyMethodInterceptor invoke 调用 after invocation.proceed");
            return ret;
        }
    }
    

输出会看到, eat() 被代理两次,go()被代理一次

public class TestPointAdvisor {
    public static void main(String[] args) {
        // 设置此系统属性,让 JVM 生成的 Proxy 类写入文件. 保存路径为项目的根目录
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

        // 1. 构造目标对象
        Animal catTarget = new Cat();

        // 2. 通过目标对象,构造 ProxyFactory 对象
        ProxyFactory factory = new ProxyFactory(catTarget);

        // 添加一个 Advice (DefaultPointcutAdvisor)
        factory.addAdvice(new MyMethodInterceptor());

        // 新增代码:添加一个 PointcutAdvisor
        MyPointcutAdvisor myPointcutAdvisor = new MyPointcutAdvisor(
                new MyMethodInterceptor(),  // Advice
                new MyPointcut()            // PointCut
        );
        factory.addAdvisor(myPointcutAdvisor);

        // 3. 根据目标对象生成代理对象
        Animal cat = (Animal) factory.getProxy();
        System.out.println(cat.getClass());
        cat.eat();

        System.out.println("---------------------------------------");

        cat.go();
    }
}

输出:

class com.sun.proxy.$Proxy0
MyMethodInterceptor invoke 调用 before invocation.proceed
猫吃鱼
MyMethodInterceptor invoke 调用 after invocation.proceed
---------------------------------------
MyMethodInterceptor invoke 调用 before invocation.proceed
MyMethodInterceptor invoke 调用 before invocation.proceed
猫在跑
MyMethodInterceptor invoke 调用 after invocation.proceed
MyMethodInterceptor invoke 调用 after invocation.proceed

参考代码

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值