设为首页 加入收藏

TOP

接口方法上的注解无法被 @Aspect 声明的切面拦截的原因分析(六)
2018-05-21 15:48:41 】 浏览:1682
Tags:接口 方法 注解 无法 @Aspect 声明 拦截 原因分析
ingClass()); if (opDef != null && ClassUtils.isUserLevelMethod(method)) { return opDef; } }

看似不受影响,其实是做了兼容。

可以参考后面的内容,有提到Spring相关的issue

解决方案

如何解决这个问题呢?答案是在Spring Boot 1.X中没有解决方案。。因为这个类太基础了,除非切换版本。

使用其他Aspect表达式也可以解决此问题,使用注解方式在1.X版本是无解的。

表达式参考如下链接:

  1. Spring 之AOP AspectJ切入点语法详解(最全面、最详细。)
  2. 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
首页 上一页 3 4 5 6 下一页 尾页 6/6/6
】【打印繁体】【投稿】【收藏】 【推荐】【举报】【评论】 【关闭】 【返回顶部
上一篇用 Maven 实现一个 protobuf 的 J.. 下一篇直播一次问题排查过程

最新文章

热门文章

Hot 文章

Python

C 语言

C++基础

大数据基础

linux编程基础

C/C++面试题目