- Are there any decent examples on enabling the above behavior usually done with the first two options using AspectJ LTW?
I have a couple of AspectJ examples on my GitHub account. Both these examples show how to intercept calls within the same target object (self-invocation) and also intercept private methods.
- Spring Boot Source Weaving Example with AspectJ
- Spring Boot Load-Time Weaving Example with AspectJ
Both the examples are similar except the way aspects are woven into target classes.
Please read the examples' READMEs to find out more about each type of weaving and on how to use each of the examples.
- Is there a way to enable AspectJ LTW selectively, e.g. not for @Async and @Transactional but just @Cacheable?
Yes, you can filter based on either of the following:
By the annotation type of the caller method.
@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());
}
By the name of the caller method.
@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());
}
You can find working examples here.
Updated
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).
Spring AOP and CTW/LTW AspectJ weavings are completely orthogonal, i.e., they are independent of each other.
- The compile and LTW modify the actual bytecode, i.e, the lines of code are inserted in the target object's method body.
- AOP is proxy-based, i.e. there is a wrapper around the target object. Any call to the target object gets intercepted by the Spring wrapper object. You can also use Spring AOP with CTW/LTW if the need arises. In this case, Spring AOP will make a proxy of the modified target.
You will need @EnableTransactionManagement
if you want to enable Spring's annotation-driven transaction management capability.
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?
Yes, in CTW you don't need anything special during start-up since the extra bytecode is already injected in the original code by the AspectJ compiler (ajc
) during compile time. For example, here is the original source code:
@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;
}
Here is the same piece of code after compilation by AspectJ compiler, ajc
:
private Book validateRequest(BookRequest request) {
JoinPoint var3 = Factory.makeJP(ajc$tjp_0, this, 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 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;
}
While in LTW, you need the Java Agent since the code gets modified during load-time, i.e., when the classes are being loaded by Java class loaders.