为什么不能从 beanClass 获取注解?

Why can not get annotation from beanClass?

@Transactional
@Component
@EntranceLog
public class TransferServiceImpl implements TransferService {

    xxxx

}

我有一个带有 Transactional 注释和 Component 注释的 class。 EntranceLog是我自定义的aop打印日志的注解。

public class LogProxyCreator extends AbstractAutoProxyCreator implements ApplicationContextAware {

    private static final LogInterceptor LOG = new LogInterceptor();

    private static Logger log = LoggerFactory.getLogger(LogProxyCreator.class);

    @Override
    protected Object[] getAdvicesAndAdvisorsForBean(Class<?> beanClass, String s, TargetSource targetSource) throws BeansException {
        Annotation anno = null;

        for (Annotation annotationTemp : beanClass.getAnnotations()) {
            Log temp = annotationTemp.annotationType().getAnnotation(EntranceLog.class);
            if (temp != null) {
                anno = temp;
                break;
            }
        }

        if (anno == null) {
            return null;
        }
        Object[] additional =  new Object[]{LOG};

        log.error(beanClass.getName() + " has register the fc log.");

        return additional;
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        LOG.setContext(applicationContext);
    }
}

当我的应用程序启动时,bean transferServiceImpl 启动,但是beanClass.getAnnotations() 无法获取任何注解。为什么?

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Log(logName = "entrance")
public @interface EntranceLog {
    @AliasFor(
        annotation = Log.class,
        attribute = "subLogName"
    )
    String logName() default "";

    @AliasFor(
        annotation = Log.class,
        attribute = "openInfoLog"
    )
    boolean openInfoLog() default false;
}

这是我的注释。

在 Spring @Transactional 中已经是一个 AOP 处理的注解,因此添加您自己的注解需要一些额外的工作。让我解释一下 Spring AOP 和 @Transactional 是如何工作的。

Spring 有两种实现 AOP 的方式,如果 class 实现了一个接口,它 可以 使用标准的 JDK 代理,如果class 没有实现接口,它将通过使用 CGLib 在运行时发出字节码来创建一个新的子 class。除非你非常小心,否则你几乎总是会得到一个带有 Spring AOP 的 CGLib 代理。

当Spring遇到@Transactional(class或方法级别)时,它使用CGLib创建一个新的子class,你可以想到这个class 作为装饰器,它将所有调用转发给您的实现 class。之前和之后(在 Advice 周围)它检查 @Transactional 注释属性,并检查线程本地存储以查看事务是否已经存在,如果没有事务它创建一个,并记住它以便以后可以提交它。如果您在 Transactional 方法中设置断点并查看调用堆栈,您将看到对您的实现的调用来自装饰器 class,并且没有它的源代码。

在您的例子中,添加到应用程序上下文的 bean 不是您的 TransferServiceImplbean,而是 Spring 在找到 @Transactional 注释时创建的 CGLib 代理你的 class,它将被命名为 TransferServiceImpl$$FastClassBySpringCGLIB$$<hexstring> - 这个 class 没有 @EntranceLog 注释,这就是你自己的方面不起作用的原因。

我自己从来没有遇到过这个问题,因为我通常试图避免 AOP,或者总是在 class 已经被 Spring 代理的 CGLib 上。除非你想深入研究 Spring 源代码,或者在 Spring 开发团队中找人帮你解决这个问题,否则我建议你创建另一个间接层,这样你就不需要在同一个 class.

中处理两个方面

对于可能不愿意或无法更改其代码结构以避免此问题的任何人,以下内容可能会有所帮助:

正如 Klaus 提到的,Spring 在遇到带有 @Transactional 标记的 class 时会创建装饰器 class。然而,因为这个新的 class 只是一个装饰器,你应该能够在 beanClass 上调用 getSuperclass() 来给你实际的 class Spring 正在装修,像这样:

beanClass.getSuperclass().getAnnotations()

如果您使用自己的注释,请通过注释注释 class 确保它在运行时持续存在:

@Retention(RetentionPolicy.RUNTIME)