spring aop(一) pointcut 和 advicor

先看一个使用 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

参考代码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值