总结摘要
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