为什么无法从beanClass获取注释?

时间:2017-02-23 09:27:27

标签: java spring annotations aop

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

    xxxx

}

我有一个Transactional注释和Component注释的课程。 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;
}

这是我的注释。

2 个答案:

答案 0 :(得分:1)

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

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

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

在你的情况下,添加到Application Context的bean不是你的TransferServiceImpl bean,而是Spring在你的类中找到@Transactional注释时创建的CGLib代理,它将是命名为TransferServiceImpl$$FastClassBySpringCGLIB$$<hexstring>的东西 - 这个类没有@EntranceLog注释,这就是你自己的方面不起作用的原因。

我自己从未遇到过这个问题,因为我试图避免AOP,或者总是在已经被Spring代理的CGLib的类中。除非你想深入挖掘Spring源代码,或者在Spring开发团队中找到一个人来帮助你,我建议你创建另一个间接层,这样你就不需要在同一个类中处理两个方面。

答案 1 :(得分:0)

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

正如克劳斯(Klaus)所述,Spring在遇到标记为@Transactional的类时会创建一个装饰器类。但是,由于这个新类就是装饰器,因此您应该可以在getSuperclass()上调用beanClass来为Spring提供实际的装饰类,如下所示:

beanClass.getSuperclass().getAnnotations()

如果您使用自己的Annotation,请通过以下方法对Annotation类进行注释,以确保其在运行时中也持续存在:

@Retention(RetentionPolicy.RUNTIME)