设为首页 加入收藏

TOP

接口方法上的注解无法被 @Aspect 声明的切面拦截的原因分析(五)
2018-05-21 15:48:41 】 浏览:1681
Tags:接口 方法 注解 无法 @Aspect 声明 拦截 原因分析
od targetMethod = AopUtils.getMostSpecificMethod(method, targetClass); ShadowMatch shadowMatch = getShadowMatch(targetMethod, method); // Special handling for this, target, @this, @target, @annotation // in Spring - we can optimize since we know we have exactly this class, // and there will never be matching subclass at runtime. if (shadowMatch.alwaysMatches()) { return true; } else if (shadowMatch.neverMatches()) { return false; } else { // the maybe case if (beanHasIntroductions) { return true; } // A match test returned maybe - if there are any subtype sensitive variables // involved in the test (this, target, at_this, at_target, at_annotation) then // we say this is not a match as in Spring there will never be a different // runtime subtype. RuntimeTestWalker walker = getRuntimeTestWalker(shadowMatch); return (!walker.testsSubtypeSensitiveVars() || (targetClass != null && walker.testTargetInstanceOfResidue(targetClass))); } }

这段代码在Spring Boot 1.X和2.X中基本是相同的,但是在AopUtils.getMostSpecificMethod(method, targetClass);这一句的执行结果上,两者是不同的,1.X返回的是动态代理生成的Class中重写的接口中的方法,2.X返回的是原始接口中的方法。

而在动态代理生成的Class中重写的接口方法里,是不会包含接口中的注解信息的,所以Aspect中条件使用注解在这里是拿不到匹配信息的,所以返回了false。

而在2.X中,因为返回的是原始接口的方法,故可以成功匹配。

问题就在于AopUtils.getMostSpecificMethod(method, targetClass)的逻辑:

// 1.X
public static Method getMostSpecificMethod(Method method, Class<?> targetClass) {
    // 这里返回了targetClass上的重写的method方法。
	Method resolvedMethod = ClassUtils.getMostSpecificMethod(method, targetClass);
	// If we are dealing with method with generic parameters, find the original method.
	return BridgeMethodResolver.findBridgedMethod(resolvedMethod);
}

// 2.X
public static Method getMostSpecificMethod(Method method, @Nullable Class<?> targetClass) {
    // 比1.X多了个逻辑判断,如果是JDK的Proxy,则specificTargetClass为null,否则取被代理的Class。
	Class<?> specificTargetClass = (targetClass != null && !Proxy.isProxyClass(targetClass) ?
			ClassUtils.getUserClass(targetClass) : null);
	// 如果specificTargetClass为空,直接返回原始method。
	// 如果不为空,返回被代理的Class上的方法
	Method resolvedMethod = ClassUtils.getMostSpecificMethod(method, specificTargetClass);
	// If we are dealing with method with generic parameters, find the original method.
	// 获取真实桥接的方法,泛型支持
	return BridgeMethodResolver.findBridgedMethod(resolvedMethod);
}

至此原因已经完全明了,Spring在AOP的5.X版本修复了这个问题。

影响范围

原因已经查明,那么根据原因我们推算一下影响范围

  1. Bean是接口动态代理对象时,且该动态代理对象不是Spring体系生成的,接口中的切面注解无法被拦截
  2. Bean是CGLIB动态代理对象时,该动态代理对象不是Spring体系生成的,原始类方法上的切面注解无法被拦截。
  3. 可能也影响基于类名和方法名的拦截体系,因为生成的动态代理类路径和类名是不同的。

如果是Spring体系生成的,之前拿到的都是真实类或者接口,只有在生成动态代理后,才是新的类。所以在创建动态代理时,获取的是真实的类。

接口动态代理多见于ORM框架的Mapper、RPC框架的SPI等,所以在这两种情况下使用注解要尤为小心。

有些同学比较关心@Cacheable注解,放在Mapper中是否生效。答案是生效,因为@Cacheable注解中使用的不是@Aspect的PointCut,而是CacheOperationSourcePointcut,其中虽然也使用了getMostSpecificMethod来获取method,但是最终其实又从原始方法上尝试获取了注解:

// AbstractFallbackCacheOperationSource.computeCacheOperations
if (specificMethod != method) {
	//  Fallback is to look at the original method
	opDef = findCacheOperations(method);
	if (opDef != null) {
		return opDef;
	}
	// Last fallback is the class of the original method.
	opDef = findCacheOperations(method.getDeclar
首页 上一页 2 3 4 5 6 下一页 尾页 5/6/6
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇用 Maven 实现一个 protobuf 的 J.. 下一篇直播一次问题排查过程

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目