深入理解Spring 之 源码剖析AOP(XML配置方式)

Spring 的两大核心,一是IOC,我们之前已经学习过,并且已经自己动手实现了一个,而令一个则是大名鼎鼎的 AOP,AOP的具体概念我就不介绍了,我们今天重点是要从源码层面去看看 spring 的 AOP 是如何实现的。注意,今天楼主给大家分享的是 XML 配置AOP的方式,不是我们经常使用的注解方式,为什么呢?

有几个原因:
1. Spring AOP 在 2.0 版本之前都是使用的 XML 配置方式,封装的层次相比注解要少,对于我们学习AOP是个很好的例子。
2. 虽然现在是2017年,现在使用SpringBoot 都是使用注解了, 但是底层原理都是一样的,只不过多了几层封装。当然,我们也是要去扒开它的源码的。但不是今天。
3. 楼主也还没有分析注解方式的AOP。-_-|||

我们主要分为几个步骤去理解:
1. 查看源码了解 spring AOP 的接口设计。Advice,PointCut,Advisor。
2. 用一个最简单的代码例子 debug 追踪源码。

那么我们现看第一步:

1. Spring AOP 接口设计

1.1 PointCut (连接点,定义匹配哪些方法)

我们打开 Spring 的源码,查看 PointCut 接口设计:

public interface Pointcut {
   
    ClassFilter getClassFilter();
    MethodMatcher getMethodMatcher();
    Pointcut TRUE = TruePointcut.INSTANCE;
}

该接口定义了2 个方法,一个成员变量。我们先看第一个方法, ClassFilter getClassFilter() ,该方法返回一个类过滤器,由于一个类可能会被多个代理类代理,于是Spring引入了责任链模式,另一个方法则是 MethodMatcher getMethodMatcher() ,表示返回一个方法匹配器,我们知道,AOP 的作用是代理方法,那么,Spirng 怎么知道代理哪些方法呢?必须通过某种方式来匹配方法的名称来决定是否对该方法进行增强,这就是 MethodMatcher 的作用。还有要给默认的 Pointcut 实例,该实例对于任何方法的匹配结果都是返回 true。

我们关注一下 MethodMatcher 接口:

public interface MethodMatcher {
   
    boolean matches(Method method, @Nullable Class<?> targetClass);
    boolean isRuntime();
    boolean matches(Method method, @Nullable Class<?> targetClass, Object... args);
    MethodMatcher TRUE = TrueMethodMatcher.INSTANCE;
}

该接口定义了静态方法匹配器和动态方法匹配器。所谓静态方法匹配器,它仅对方法名签名(包括方法名和入参类型及顺序)进行匹配;而动态方法匹配器,会在运行期检查方法入参的值。静态匹配仅会判别一次,而动态匹配因为每次调用方法的入参都可能不一样,所以每次都必须判断。一般情况下,动态匹配不常用。方法匹配器的类型由isRuntime()返回值决定,返回false表示是静态方法匹配器,返回true表示是动态方法匹配器。

总的来说, PointCut 和 MethodMatcher 是依赖关系,定义了AOP应该匹配什么方法以及如何匹配。

1.2 Advice (通知,定义在链接点做什么)

注意,Advice 接口只是一个标识,什么也没有定义,但是我们常用的几个接口,比如 BeforeAdvice,AfterAdvice,都是继承自它。我们关注一下 AfterAdvice 的子接口 AfterReturningAdvice :

public interface AfterReturningAdvice extends AfterAdvice {
   
    void afterReturning(@Nullable Object returnValue, Method method, Object[] args, @Nullable Object target) throws Throwable;
}

````

该接口定义了一个方法,afterReturning,参数分别是返回值,目标方法,参数,目标方法类,在目标方法执行之后会回调该方法。那么我们就可以在该方法中执行我们的切面逻辑,BeforeAdvice 也是一样的道理。





<div class="se-preview-section-delimiter"></div>

### 1.3 Advisor (通知器,将 Advice 和 PointCut 结合起来)

有了对目标方法的增强接口 Advice 和 如何匹配目标方法接口 PointCut 接口后,那么我们就需要用一个对象将他们结合起来,发挥AOP 的作用,所以Spring 设计了 Advisor(通知器),经过我们刚刚的描述,我们应该知道了,这个 Advisor 肯定依赖了 Advice 和 PointCut,我们看看接口设计:





<div class="se-preview-section-delimiter"></div>

```java
public interface Advisor {
   

    Advice EMPTY_ADVICE = new Advice() {};

    Advice getAdvice();

    boolean isPerInstance();

}

还有他的子接口:

public interface PointcutAdvisor extends Advisor {
   

    Pointcut getPointcut();
}

最重要的两个方法,getAdvice,getPointcut。和我们预想的基本一致。

接下来,我们可以停下来思考一下,现在有了这个东西,我们怎么实现面向切面编程;
1. 首先我们需要告诉AOP在哪里进行切面。比如在某个类的方法前后进行切面。
2. 告诉AOP 切面之后做什么,也就是说,我们知道了在哪里进行切面,那么我们也该让spring知道在切点处做什么。
3. 我们知道,Spring AOP 的底层实现是动态代理(不管是JDK还是Cglib),那么就需要一个代理对象,那么如何生成呢?

接下来,我们将通过代码的方式,解答这三个疑惑。

2. 从一个简单的AOP例子

首先,我们需要实现刚刚我们说的3个接口,还有一个目标类,还要一个配置文件。一个一个来。

2.1. Pointcut 接口实现

package test;

import java.lang.reflect.Method;
import org.springframework.aop.ClassFilter;
import org.springframework.aop.MethodMatcher;
import org.springframework.aop.Pointcut;

public class TestPointcut implements Pointcut {
   

    @Override
    public ClassFilter getClassFilter() {
        return ClassFilter.TRUE;
    }

    @Override
    public MethodMatcher getMethodMatcher() {
        return new MethodMatcher() {

            public boolean matches(Method method, Class<?> targetClass, Object[] args) {
                if (method.getName().equals("test")) {
                    return true;
                }
                return false;
            }

            public boolean matches(Method method, Class<?> targetClass) {
                if (method.getName().equals("test")) {
                    return true;
                }
                return false;
            }

            public boolean isRuntime() {
                return true;
            }
        };
    }

}

我们如何定义匹配?只要方法名称是test则对该方法进行增强或者说拦截。

2.2 AfterAdvice 实现

package test;

import java.lang.reflect.Method;
import org.springframework.aop.AfterReturningAdvice;

public class TestAfterAdvice implements AfterReturningAdvice {
   

    @Override
    public void afterReturning(Object returnValue, Method method,
            Object[] args, Object target) throws Throwable {
        System.out.println(
                "after " + target.getClass().getSimpleName() + "." + method.getName() + "()");
    }

}

我们在方法执行完毕后打印该方法的名称和该目标类的名称。这就是我们做的简单增强。

2.3 Advisor 通知器的实现

package test;

import org.aopalliance.aop.Advice;
import org.springframework.aop.Pointcut;
import org.springfra
  • 4
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值