ingClass());
if (opDef != null && ClassUtils.isUserLevelMethod(method)) {
return opDef;
}
}
看似不受影响,其实是做了兼容。
可以参考后面的内容,有提到Spring相关的issue
解决方案
如何解决这个问题呢?答案是在Spring Boot 1.X中没有解决方案。。因为这个类太基础了,除非切换版本。
使用其他Aspect表达式也可以解决此问题,使用注解方式在1.X版本是无解的。
表达式参考如下链接:
- Spring 之AOP AspectJ切入点语法详解(最全面、最详细。)
- Spring Aspect的Execution表达式
本来以为在注解Demo中加入@Inherited可解决的,结果发现不行,因为这个@Inherited只在类注解有效,在接口中或者方法上,都是不能被子类或者实现类继承的,看这个@Inherited上面的注释
/**
* Indicates that an annotation type is automatically inherited. If
* an Inherited meta-annotation is present on an annotation type
* declaration, and the user queries the annotation type on a class
* declaration, and the class declaration has no annotation for this type,
* then the class's superclass will automatically be queried for the
* annotation type. This process will be repeated until an annotation for this
* type is found, or the top of the class hierarchy (Object)
* is reached. If no superclass has an annotation for this type, then
* the query will indicate that the class in question has no such annotation.
*
* <p>Note that this meta-annotation type has no effect if the annotated
* type is used to annotate anything other than a class. Note also
* that this meta-annotation only causes annotations to be inherited
* from superclasses; annotations on implemented interfaces have no
* effect.
* 上面这句话说明了只在父类上的注解可被继承,接口上的都是无效的
*
* @author Joshua Bloch
* @since 1.5
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Inherited {
}
扩展阅读
问题及可能的影响范围已经详细分析完了,下面我们好奇一下,这个核心问题类AopUtils.java的提交记录中,作者有写什么吗
AopUtils.java类GitHub页面
查看这个类的历史记录,注意Commits on Apr 3, 2018这个日期的提交,其中提到:
Consistent treatment of proxy classes and interfaces for introspection
Issue: SPR-16675
Issue: SPR-16677
针对proxy classes做了内省配置,相关issue是SPR-16677,我们看下这个issue。
Spring Framework/SPR-16677
这个issue详细描述了这次提交的原因及目的。
读者感兴趣的话可以详细的阅读。
注意AopUtils.java的最新提交,又做了一些优化,可以研究一下。
扩展知识
上面的示例代码依赖于数据库,现做一个模拟Mapper类的改进,可以直接无任何依赖的重现该问题:
已知Mybatis的Mapper接口是通过JDK动态代理生成的逻辑,而Mapper接口相关的Bean生成,是通过AutoConfiguredMapperScannerRegistrar自动注册到BeanFactory中的,注册进去的是MapperFactoryBean这个工厂Bean类型。
而MapperFactoryBean的getObject方法,则是通过getSqlSession().getMapper(this.mapperInterface)生成的,mapperInterfact是mapper接口。
底层是通过Configuration.getMapper生成的,再底层是mapperRegistry.getMapper方法,代码如下
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
}
try {
// 调用下面的方法生成代理实例
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception e) {
throw new BindingException("Error getting mapper instance. Cause: " + e, e);
}
}
public T newInstance(SqlSession sqlSession) {
// 创建MapperProxy这个InvocationHandler实例
final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
return newInstance(mapperProxy);
}
protected T newInstance(MapperProxy<T> mapperProxy) {
// 调用jdk动态代理生成实例,代理的InvocationHandler是MapperProxy
return (T) Proxy.n