我已经看到了许多与@ Cacheable,@ Transaction,@ Async等相关的Spring功能的例子,每次重复相同的选项:
>除了@Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)之外,通过代理对象进行的自我调用通过ApplicationContext.getBean(MyService.class)或自动装配的MyService.class代理对象获得,
>将目标方法重定位到单独的@Service类,
>使用AspectJ加载时编织.
虽然前两种方法通常都很好,但有时我们需要将上述三种(和其他)注释的功能附加到私有方法,无论是出于代码清晰度,设计还是其他原因.
前两种方法有很多例子,但最后一种方法很少.据我所知,由于AspectJ LTW的性质,默认情况下它与通常的Spring AOP行为相互排斥,可以实现@Cacheable等行为而不会有太多麻烦.我的问题如下:
>使用AspectJ LTW,通常使用前两个选项来启用上述行为是否有任何不错的示例?
>有没有办法有选择地启用AspectJ LTW,例如不是@Async和@Transactional而是@Cacheable?一个示例用例可能是:由于团队的设计,所有可缓存的方法(其中一些可能是私有的)应该位于外观类中,该类调用执行繁重计算的私有方法,但可能更新某些状态(即’last-queried-at:#’)在返回外部调用之前.
这个问题来自spring-boot用户,但我相信它通常适用于Spring AOP和AspectJ LTW.如果在这种情况下需要特殊考虑,请纠正我.
- Are there any decent examples on enabling the above behavior usually done with the first two options using AspectJ LTW?
我的GitHub帐户上有几个AspectJ示例.这两个示例都显示了如何拦截同一目标对象中的调用(自调用)以及拦截私有方法.
> Spring Boot Source Weaving Example with AspectJ
> Spring Boot Load-Time Weaving Example with AspectJ
除了将方面编织到目标类中之外,这两个示例都是相似的.
请阅读示例的自述文件以了解有关每种编织类型以及如何使用每个示例的更多信息.
- Is there a way to enable AspectJ LTW selectively,e.g. not for @Async and @Transactional but just @Cacheable?
是的,您可以根据以下任一情况进行过滤:
@Before("call(* com.basaki.service.UselessService.sayHello(..))" +
" && cflow(@annotation(trx))")
public void inspectMethod(JoinPoint jp,JoinPoint.EnclosingStaticPart esjp,Transactional trx) {
log.info(
"Entering FilterCallerAnnotationAspect.inspectMethod() in class "
+ jp.getSignature().getDeclaringTypeName()
+ " - method: " + jp.getSignature().getName());
}
@Before("call(* com.basaki.service.UselessService.sayHello(..))" +
" && cflow(execution(* com.basaki.service.BookService.read(..)))")
public void inspectMethod(JoinPoint jp,JoinPoint.EnclosingStaticPart esjp) {
log.info(
"Entering FilterCallerMethodAspect.inspectMethod() in class "
+ jp.getSignature().getDeclaringTypeName()
+ " - method: " + jp.getSignature().getName());
}
你可以找到工作实例here.
更新
Q. Do I understand correctly then,that if I wanted to enable compile-time weaving for transactionality,I would: 1. No longer use a TransactionAwareDataSourceProxy anywhere in my DataSource configuration; 2. Add the following to my application: @EnableTransactionManagement(mode=AdviceMode.ASPECTJ).
弹簧AOP和CTW / LTW AspectJ编织是完全正交的,即它们彼此独立.
>编译和LTW修改实际的字节码,即代码行插入目标对象的方法体中.
> AOP是基于代理的,即目标对象周围有一个包装器.对Spring目标对象的任何调用都会被Spring包装器对象拦截.如果需要,您还可以将Spring AOP与CTW / LTW一起使用.在这种情况下,Spring AOP将代表修改后的目标.
如果要启用Spring的注释驱动的事务管理功能,则需要@EnableTransactionManagement.
Q. In your examples,I see that you do not start the application in any special way for CTW. Would this suffice,or have I missed anything?
是的,在CTW中你在启动期间不需要任何特殊的东西,因为在编译期间,AspectJ编译器(ajc)已经在原始代码中注入了额外的字节码.例如,这是原始源代码:
@CustomAnnotation(description = "Validates book request.")
private Book validateRequest(BookRequest request) {
log.info("Validating book request!");
Assert.notNull(request,"Book request cannot be empty!");
Assert.notNull(request.getTitle(),"Book title cannot be missing!");
Assert.notNull(request.getAuthor(),"Book author cannot be missing!");
Book entity = new Book();
entity.setTitle(request.getTitle());
entity.setAuthor(request.getAuthor());
return entity;
}
这是由AspectJ编译器ajc编译后的同一段代码:
private Book validateRequest(BookRequest request) {
JoinPoint var3 = Factory.makeJP(ajc$tjp_0,this,request);
CustomAnnotationAspect var10000 = CustomAnnotationAspect.aspectOf();
Annotation var10002 = ajc$anno$0;
if (ajc$anno$0 == null) {
var10002 = ajc$anno$0 = BookService.class.getDeclaredMethod("validateRequest",BookRequest.class).getAnnotation(CustomAnnotation.class);
}
var10000.inspectMethod(var3,(CustomAnnotation)var10002);
log.info("Validating book request!");
Assert.notNull(request,"Book author cannot be missing!");
Book entity = new Book();
entity.setTitle(request.getTitle());
entity.setAuthor(request.getAuthor());
return entity;
}