@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;
}
这是我的注释。
答案 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)