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。