被AJC编译过的类不能成功Spring AOP的切面?

去年写过Spring AOP原理和源码的文章:
Spring AOP从原理到源码(一)
Spring AOP从原理到源码(二)
Spring AOP从原理到源码(三)
Spring AOP从原理到源码(四)

时至今日,我可能已经不记得具体每一行源码是怎样的,但是Spring AOP的原理和流程还记得清清楚楚。这不,最近写了一个纯AspectJ的项目,打包成了jar包,放到一个webflux工程里发现jar包里定义的切面类压根不起作用。
本着对AOP原理比较熟,而且借助调试技巧十多分钟就定位出问题所在了。

问题原因

先说一下问题定位的结果:因为jar包被AspectJ项目的ajc编译器编译过,所以spring判断这个类是否为一个切面时判了死刑,认为它不能成为一个切面类。大概就是这样一段代码。

// AbstractAspectJAdvisorFactory#isAspect
@Override
public boolean isAspect(Class clazz) {
    // 成为切面的条件,有@AspectJ注解,同时不能是ajc编译出来的类
    return (hasAspectAnnotation(clazz) && !compiledByAjc(clazz));
}

问题定位记录

下面记录一下问题定位过程。

从@EnableAspectJAutoProxy入手

@EnableAspectJAutoProxy本质就是引入了AspectJAutoProxyRegistrar,注入了一个AnnotationAwareAspectJAutoProxyCreator(一个BeanPostProcessor)。
所以跟踪AnnotationAwareAspectJAutoProxyCreator#postProcessAfterInitialization方法即可。

跟到

Object[] specificInterceptors = getAdvicesAndAdvisorsForBean(bean.getClass(), beanName, null);

发现返回来的拦截器没有那个被@AspectJ注解的类,这就找到问题原因了。
继续跟下去。

跟到BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors方法时就发现spring在第一次创建代理对象时就将所有切面类保存起来了。

而判断一个bean是否能成为切面类的代码就在BeanFactoryAspectJAdvisorsBuilder#buildAspectJAdvisors里。

// AbstractAspectJAdvisorFactory#isAspect
@Override
public boolean isAspect(Class clazz) {
    // 成为切面的条件,有@AspectJ注解,同时不能是ajc编译出来的类
    return (hasAspectAnnotation(clazz) && !compiledByAjc(clazz));
}

private boolean compiledByAjc(Class clazz) {
    // The AJTypeSystem goes to great lengths to provide a uniform appearance between code-style and
    // annotation-style aspects. Therefore there is no 'clean' way to tell them apart. Here we rely on
    // an implementation detail of the AspectJ compiler.
    for (Field field : clazz.getDeclaredFields()) {
        // 通过反射拿到类中的属性,里面有ajc$开头的属性就代表被ajc编译器编译过。
        if (field.getName().startsWith(AJC_MAGIC)) {
            return true;
        }
    }
    return false;
}

通过jd-gui查看一下jar中的代码,发现确实属性里有ajc$开头的。


被AJC编译过的类不能成功Spring AOP的切面?_第1张图片
image.png

所以打包的时候别用ajc编译器就好了。

你可能感兴趣的:(被AJC编译过的类不能成功Spring AOP的切面?)