聊聊spring aop中的advisor组件

1.Advisor的组成结构

在spring aop中Advisor是一个接口,代表被拦截方法需要增强的逻辑。
Advisor通常由另两个组件组成——Advice接口和Pointcut接口,其中Advice表示实际增强的逻辑入口,Pointcut表示哪些类或者哪些方法需要被拦截。

1.1 Advice

我们先看下Advice类体系图,下图是实现或者继承了Advice接口的组件
在这里插入图片描述
最上面一层的around、after等,代表我们在@Aspect的类中定义的各种通知方法类型。

1.2 Pointcut

Pointcut也是一个接口,包含如下方法
在这里插入图片描述
ClassFilter接口的作用是判断哪些类需要被拦截,MethodMatcher接口的作用是判断哪些方法需要被拦截,都是通过接口中的matches方法实现的。

2.收集对象的Advisor

在spring创建bean时,如果bean需要生成代理,那么spring会收集符合条件的所有Advisor。

2.1 收集已有的Advisor

下面是收集项目中已有Advisor的方法入口

在这里插入图片描述

第一个红框会收集实现了Advisor接口的类,这些类包括我们项目自定义的、事务advisor、缓存advisor等。
第二个红框会收集注解@Aspect类中定义的around、before等通知方法,将其包装为Advisor。
这些都是已存在的Advisor。

2.2 拓展Advisor

经过上述过程,Advisor集合已经有了元素。之后spring会进入如下红框处的方法 ,在这里插入图片描述
该方法会加入新的advisor——DefaultPointcutAdvisor,并放入集合的最前面。
DefaultPointcutAdvisor的Advice是ExposeInvocationInterceptor类,作用是在调用代理对象方法时,将相关信息存入ThreadLocal,以在多个advisor间进行传递。
在这里插入图片描述

2.3 加入自定义的MethodInterceptor

经过上面两步,spring会开始生成代理对象了,代码如下
在这里插入图片描述红框处会将自定义的MethodInterceptor接口实现类也包装为advisor,并放入advisor集合的最前面。

在这里插入图片描述

自定义MethodInterceptor实战见博文

3.Advisor的排序

收集到多个Advisor之后,spring会进行排序。具体排序逻辑本文不做具体分析,只说结论:

假设集合里有before、after、afterThrowing、afterRetruning、around5种通知,以及上面提到的ExposeInvocationInterceptor,则排序后顺序为
在这里插入图片描述

在调用代理对象方法时,也是按照这个顺序调用advisor。

4.自定义Advisor实战

通过上面的分析,我们来自定义一个Advisor,功能是拦截方法有@Hero注解的类,并生成代理。

4.1 自定义注解

@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Hero {
}

4.2 自定义MethodMatcher

判断方法上是否有@Hero注解,如有则符合pointcut的条件。

public class CustomMethodMatcher implements MethodMatcher {

    @Override
    public boolean matches(Method method, Class<?> aClass) {
        Method specificMethod = AopUtils.getMostSpecificMethod(method, aClass);
        boolean res = specificMethod.isAnnotationPresent(Hero.class);
        if(res){
            return true;
        }
        return false;
    }

    @Override
    public boolean isRuntime() {
        return false;
    }

    @Override
    public boolean matches(Method method, Class<?> aClass, Object... objects) {
        return false;
    }
}

这里我们通过AopUtils.getMostSpecificMethod(method, aClass);先拿到Method,然后再去拿注解信息。
为什么不直接用方法的参数method,通过method.isAnnotationPresent来判断呢?
除了收集advisor时会进入MethodMatcher的方法,在调用JdkDynamicAopProxy的invoke方法时,spring为了排除没必要拦截的方法,也会进入MethodMatcher的方法。
问题在于,此时我们的method是接口的方法,而不是实现类的方法,而接口方法是没有注解的,所以此时method.isAnnotationPresent拿不到,spring就不会用到对应的Advisor。
AopUtils.getMostSpecificMethod(method, aClass);是不管如何都可以拿到注解信息的,可以确保万无一失。

4.3 自定义Pointcut

这里ClassFilter我们直接返回true,表示所有类都匹配

public class CustomPointCut implements Pointcut {
    @Override
    public ClassFilter getClassFilter() {
        return ClassFilter.TRUE;
    }

    @Override
    public MethodMatcher getMethodMatcher() {
        return new CustomMethodMatcher();
    }
}

4.4 自定义Advice

CustomMethodInterceptor 处理实际业务逻辑

public class CustomMethodInterceptor implements MethodInterceptor {
    @Override
    public Object invoke(MethodInvocation methodInvocation) throws Throwable {
        System.out.println("CustomMethodInterceptor");
        return methodInvocation.proceed();
    }
}

4.5 自定义Advisor

拥有advice和pointcut之后,我们就可以创建Advisor了

@Component
public class CustomAdvisor implements PointcutAdvisor {

    @Override
    public Pointcut getPointcut() {
        return new CustomPointCut();
    }

    @Override
    public Advice getAdvice() {
        return new CustomMethodInterceptor();
    }

    @Override
    public boolean isPerInstance() {
        return false;
    }
}

4.6 service接口

public interface TestService {
    void test();
}

@Service
public class TestServiceImpl implements TestService{

    @Override
    @Hero
    public void test() {
        System.out.println("test");
    }
}

项目启动时,spring就会针对有@Hero注解的TestServiceImpl类生成代理,代理中拥有CustomAdvisor。

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值