Spring源码-Aspect-扩展知识

总结摘要
Spring AOP Aspect 扩展知识

AopUtils#canApply 方法匹配分析

问题描述

既然ReflectionUtils.getAllDeclaredMethods(Class<?> clazz)可以获取父类和接口继承的方法,是否有必要先调用ClassUtils.getAllInterfacesForClassAsSet(Class<?> clazz)可以获取定类(clazz)及其所有父类(包括超类)实现的所有接口,再次遍历接口获取方法?

相关代码如下

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
// org.springframework.aop.support.AopUtils#canApply(org.springframework.aop.Pointcut, java.lang.Class<?>, boolean)
public static boolean canApply(Pointcut pc, Class<?> targetClass, boolean hasIntroductions) {
    // ...    
    classes.addAll(ClassUtils.getAllInterfacesForClassAsSet(targetClass));

    for (Class<?> clazz : classes) {
        Method[] methods = ReflectionUtils.getAllDeclaredMethods(clazz);
        for (Method method : methods) {
            if (introductionAwareMethodMatcher != null ?
                    introductionAwareMethodMatcher.matches(method, targetClass, hasIntroductions) :
                    methodMatcher.matches(method, targetClass)) {
                return true;
            }
        }
    }
}

结论

有必要。

否则,存在匹配遗漏的场景。如果类实现的接口继承自其他接口,而 Pointcut 匹配的是父接口的方法,不扫描接口会导致继承的接口方法被遗漏。

验证

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
public interface AspectOperationJdkProxy {
    void serviceInterface();
}

@Component
public class AspectOperationJdkProxyImpl implements AspectOperationJdkProxy {
    @Override
    public void serviceInterface() {
        System.out.println("[SpringAOP][ProxyType] serviceInterface from " + this.getClass().getName());
    }

    public void serviceMethod() {
        System.out.println("[SpringAOP][ProxyType] serviceMethod from " + this.getClass().getName());
    }
}
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
// 打印内容如下:
// interfaceSet: [interface tech.gdev.springbasicexplore.aop.springaop.AspectOperationJdkProxy]
// methodsFromInterface: [public abstract void tech.gdev.springbasicexplore.aop.springaop.AspectOperationJdkProxy.serviceInterface()]
// methodsFromImpl: [public void tech.gdev.springbasicexplore.aop.springaop.AspectOperationJdkProxyImpl.serviceInterface(), public void tech.gdev.springbasicexplore.aop.springaop.AspectOperationJdkProxyImpl.serviceMethod()]
Set<Class<?>> interfaceSet = ClassUtils.getAllInterfacesForClassAsSet(AspectOperationJdkProxyImpl.class);
System.out.println("interfaceSet: " + interfaceSet);
Method[] methodsFromInterface = ReflectionUtils.getAllDeclaredMethods(interfaceSet.iterator().next());
System.out.println("methodsFromInterface: " + Arrays.deepToString(methodsFromInterface));
Method[] methodsFromImpl = ReflectionUtils.getAllDeclaredMethods(AspectOperationJdkProxyImpl.class);
methodsFromImpl = Arrays.stream(methodsFromImpl)
        .filter(item -> item.toString().contains("tech.gdev."))
        .collect(Collectors.toList()).toArray(new Method[0]);
System.out.println("methodsFromImpl: " + Arrays.deepToString(methodsFromImpl));

详情

ClassUtils.getAllInterfacesForClassAsSet(Class<?> clazz)

作用:获取指定类(clazz)及其所有父类(包括超类)实现的所有接口,并以 Set<Class<?>> 形式返回。

1
2
3
// org.springframework.util.ReflectionUtils#getAllDeclaredMethods
// Get all declared methods on the leaf class and all superclasses. Leaf class methods are included first.
// 获取叶类及其所有父类中声明的所有方法叶类中的方法会优先包含

ReflectionUtils.getAllDeclaredMethods(Class<?> clazz)

作用:获取指定类(clazz)中声明的所有方法(包括公共/保护/私有方法),以及从父类和接口继承的方法(覆盖的方法仅出现一次),返回 Method[] 数组。

END