I've got an AspectJ weaving annotation that works for public methods, but private method are being ignored.
The purpose of this method is to simply log the time it took to run the function.
#Aspect
#Slf4j
public class TimedLogAspect {
#Pointcut("#annotation(timedLogVar)")
public void annotationPointCutDefinition(TimedLog timedLogVar) {}
#Pointcut("execution(* *(..))")
public void atExecution() {}
#Around(value = "annotationPointCutDefinition(timedLogVar) && atExecution()", argNames = "joinPoint,timedLogVar")
public Object around(ProceedingJoinPoint joinPoint, TimedLog timedLogVar) throws Throwable {
Stopwatch stopwatch = Stopwatch.createStarted();
Object returnValue = joinPoint.proceed();
stopwatch.stop();
MessageBuilder messageBuilder = new MessageBuilder(joinPoint.toShortString(), stopwatch.elapsed(TimeUnit.MILLISECONDS))
.attachMessage(timedLogVar.message())
.attachMethodArgs(timedLogVar.shouldAttachMethodArgs(), Stream.of(joinPoint.getArgs()).collect(Collectors.toList()))
.attachReturnValue(timedLogVar.shouldAttachReturnValue(), returnValue);
log.info(messageBuilder.build(), messageBuilder.getArgs().toArray());
return returnValue;
}
}
with this being the actual interface:
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface TimedLog {
boolean shouldAttachMethodArgs() default false;
boolean shouldAttachReturnValue() default false;
String message() default "";
}
I've seen a lot of answer, being adding private before the first * in the execution portion, I've seen privileged which isn't supported for annotations, and I'm using AspectJ with no SpringAOP.
any ideas?
The answer is simple: use native AspectJ syntax. You mentioned it by yourself. You even said that you use full AspectJ, not Spring AOP. So switching should not be a problem.
You have lots of advantages:
more power (some features are unavailable in annotation syntax, as you noticed),
syntax highlighting and code completion in IDEs,
more expressive syntax (less verbose and more elegant, i.e. no need to bind thisJoinPoint in advices and easier use of if(), no need for fully-qualified class names because you can just import them).
The annotation-based syntax IMO is just terribly difficult to read, everything is in a single string inside an annotation parameter. I only use it if I have no other choice or when answering questions about it here.
Related
If I have Annotation:
#Retention(RUNTIME)
#Target({TYPE, METHOD})
public #interface Loggable {
enum LogLevel {
ERROR, WARN, INFO, DEBUG, TRACE;
}
LogLevel value() default DEBUG;
}
and trying to use it as target marker of Spring AOP aspect joinpoint marker, it works well for some aspect, weaved by that composite pointcut:
#Aspect
class LoggableAspect {
#Pointcut("within(#Loggable *)")
void aClass() {}
//#Pointcut("#annotation(Loggable)") - in straight-forward case it works too!
#Pointcut("execution(#Loggable * *(..))")
void method() {}
#Around("method() || aClass()")
Object aroundAdvice(ProceedingJoinPoint pjp) {...}
}
In other words, it works well as "straight-forward" annotation usage, when I write code like this:
#Component
public class Abc {
#Loggable
public String method(int x) {
return "string: " + x;
}
}
But in case of META-annotation...:
#Loggable(INFO)
#Retention(RUNTIME)
public #interface Log {
}
...it doesn't work for example, in that code:
#Component
public class Abc {
#Log // doesn't work! :(
public String method(int x) {
return "string: " + x;
}
}
Certainly, I can write yet another pointcut for that particular case of 2-level deepness:
//...
#Pointcut("execution(#(#Loggable *) * *(..))")
void metaMethod() {}
#Around("method() || metaMethod() || aClass()")
Object aroundAdvice(ProceedingJoinPoint pjp) {...}
and it will work, but I want universal solution, working on any level of deepness - 3, 4, 5... Is it possible for that style of AOP?
P.S. That issue:
execution(public * ((#Transactional *)+).*(..))
looks like exactly right solution, but unfortunately, it doesn't work in my case. I think, it's possible only as AspectJ solution (in *.aj files) - not for Spring AOP. Am I right?..
The correct answer to your question, even though you might not like it, is: Neither Spring AOP as a syntactical subset of AspectJ nor native AspectJ provide a dedicated syntax to achieve what you wish to do. The best you can actually do is to use a nested syntax like you suggested yourself and use a reasonable number of nesting levels.
In my answer here you find solutions for both class- and method-level meta annotations.
Update: You misunderstood what
execution(public * ((#Transactional *)+).*(..))
means, even though the answer you linked to explains it:
Matches the execution of any public method in a type with the Transactional annotation, or any subtype of a type with the Transactional annotation.
So this syntax is about class inheritance, not about meta annotation nesting. It means that if you have #Transational class Parent, it would match class Child extends Parent and class Grandchild extends Child etc. The syntax should also work in Spring AOP, but that does not solve your problem.
I want to have annotation on class level that will execute advice on every method in annotated class.
Is that even possible.
Example: I would like to annotate OmniDemoService with #DoSomethingForMe and I want both method1 and method2 to log "look at me" before execution
This example is not working and I don't know why. When I transform Pointcut to Around and just use it with annotation (also change annotation ElementType to method) everything is working on method level.
So I think it is wrong defined Pointcut.
Annotation:
#Target(ElementType.TYPE)
#Retention(RetentionPolicy.RUNTIME)
public #interface DoSomethingForMe {
}
Advice:
#Aspect
#Component
public class DoSomethingForMeAdvice {
private static final Logger logger = LoggerFactory.getLogger(DoSomethingForMeAdvice.class);
#Pointcut("execution(public * *(..)) && #annotation(DoSomethingForMe)")
public void anyAnnotatedMethod() {
}
#Before("anyAnnotatedMethod()")
public void acquireExecution() {
logger.info("look at me");
}
}
Usage:
#Service
#DoSomethingForMe
public class OmniDemoService {
private static final Logger logger = LoggerFactory.getLogger(OmniDemoService.class);
public void method1() {
logger.info("---1---");
}
public void method2() {
logger.info("---2---");
}
}
Your issue is that you are confusing pointcut definition with advices.
Pointcut is aiming, advice performs the actual WhateverYouWantToBeExecuted. Like for example
#Pointcut("#annotation(com.omnidemo.advice.DoSomethingForMe)")
public void anyAnnotatedMethod() {
}
#Before("anyAnnotatedMethod()")
public void logMethodCall(JoinPoint jp) {
String methodName = jp.getSignature().toShortString();
logger.info("Executing: " + methodName);
}
Solution for the problem is to use within for pointcut
#Pointcut("#within(DoSomethingForMe)")
public void anyAnnotatedMethod() {
}
#Before("anyAnnotatedMethod()")
public void acquireExecution() {
logger.info("look at me");
}
Solution provided by #J Asgarov in the comments
Check out what the AspectJ quick reference says about #annotation():
any join point where the subject has an annotation of type
SomeAnnotation
You used #annotation(DoSomethingForMe) but the "subject" of a method execution is a method. So that would mean any method annotated #DoSomethingForMe.
Use #this(DoSomethingForMe) or #target(DoSomethingForMe).
Thanks to kriegaex for pointing out that #this and #target must be evaluated at runtime, which would pollute the codebase a lot (i.e. check in every method). So the next approach is better:
If you check the AspectJ manual section about type patterns you will see that you can annotate the type directly. Please also remember to use use fully qualified class names. So that would be:
execution(public * (#com.path.DoSomethingForMe *).*(..))
Also, if you have such a simple pointcut and you don't need to reuse it, I think you can drop the additional method and just use it in the advice directly:
#Before("execution(public * (#com.path.DoSomethingForMe *).*(..))")
which says: "before the execution of any public method of a type annotated with #com.path.DoSomethingForMe", where "before the execution of a method" means "inside the method, at the beginning".
Alternatively, if this pointcut looks a bit too complicated for you, you can separate annotation matching and method matching like this, as suggested by J Asgarov in his comment:
#Before("execution(public * *(..)) && #within(com.path.DoSomethingForMe)")
I am using aspectj and have two almost identical aspects defined, the first one works just fine whether the second fails during compilation as below:
[WARNING] advice defined in com...RolesAspect has not been applied [Xlint:adviceDidNotMatch]
First aspect:
#Aspect
public class RoleAspect {
#Before("requireRolePointcut(requireRole)")
public void requireRole(JoinPoint joinPoint, RequireRole requireRole) throws Throwable {
//TODO
}
#Pointcut("#annotation(requireRole)")
public void requireRolePointcut(RequireRole requireRole) {
}
}
Second aspect:
#Aspect
public class RolesAspect {
#Before("requireRolesPointcut(requireRoles)")
public void requireRoles(JoinPoint joinPoint, RequireRoles requireRoles) throws Throwable {
//TODO
}
#Pointcut("#annotation(requireRoles)")
public void requireRolesPointcut(RequireRoles requireRoles) {
}
}
Annotation RequireRole:
#Retention(RetentionPolicy.RUNTIME)
#Target(value={ElementType.METHOD})
public #interface RequireRole {
String value();
}
Annotation RequireRoles:
#Retention(RetentionPolicy.RUNTIME)
#Target(value={ElementType.METHOD})
public #interface RequireRoles {
String[] value();
}
Any help is really appreciated. I tried many different annotations but the second always fails so there must be something maybe with it.
The aspect does not fail, what you see is just an AspectJ compiler warning telling you that the pointcut does not match.
Unfortunately you are not showing
your full aspect code
and also no target class(es) with package names(!).
Same goes for your annotations: no package names.
I.e. you have omitted the information needed to analyse your problem.
Thus, for now I can only tell you to fix your pointcut in order to make it match what you want to intercept - or edit your question in order to show it to me and notify me with a comment. Then I can take a second look and provide more concrete hints.
Below is my code snippet:
ServiceImpl.java
#Service
public class ServiceImpl implements Service {
private Response worker(Audit send) throws ArgumentException {
System.out.println("STEP_1");
worker(send.getRequest(), send.getId());
}
private Response worker(Request request, String id) throws ArgumentException {
System.out.println("STEP_2");
try {
//throwing some exception
} catch (Exception e) {
System.out.println("STEP_3");
}
}
}
Now, what I want is whenever NullPointerException is being thrown from method worker(Request request, String id) as shown above I want to perform some specific task. For that I have written an Aspect class which is following:
MyAspect.java
#Aspect
#Component
public class MyAspect{
#Pointcut("com.xyz.myapp.ServiceImpl.worker() && args(request,..)")
private void someOperation(Request request) {}
#Before("someOperation(request)")
public void process(Request request) {
System.out.println("SUCCESS");
}
#AfterThrowing("com.xyz.myapp.ServiceImpl.worker() && args(request,..)")
public void doRecoveryActions() {
System.out.println("EXCEPTION_SUCCESS");
}
}
Current Output:
STEP_1
STEP_2
STEP_3
Desired Output:
STEP_1
STEP_2
STEP_3
SUCCESS
EXCEPTION_SUCCESS
As you can see MyAspect.java is not getting triggered hence NOT printing values.
What can be the reason for this?
Note:
I tried making worker as public classes too but it didn't work.
Also tried changing the name of the methods to eliminate any overloading issue that too didn't work.
Tried various other pointcut expressions all in vain as of now.
In my application there are other aspect classes working absolutely fine.
You made a typical Spring AOP beginner's mistake: You assume that it works for private methods, but as the documentation clearly says, it does not. Spring AOP is based on dynamic proxies, and those only work for public methods when implementing interfaces via JDK proxies and additionally for protected and package-scoped methods when using CGLIB proxies.
You should make the worker() method public if you want to intercept it from an aspect.
P.S.: Full-fledged AspectJ also works for private methods, but to switch to another AOP framework would be overkill here.
Update: You also have other problems in your code:
The first worker method, even if you make it public, does not return anything. The last statement should be return worker(send.getRequest(), send.getId());, not just worker(send.getRequest(), send.getId());.
Your pointcut com.xyz.myapp.ServiceImpl.worker() will never match because it has an empty argument list, but your method has arguments. The args() does not help you here.
The syntax of your pointcut is also wrong because it does not specify a return type for the method, not even *. Furthermore, the method name itself is not enough, it should be enclosed in an actual pointcut type such as execution(). I.e. you want to write something like:
#Pointcut("execution(* com.xyz.myapp.ServiceImpl.worker(..)) && args(request, ..)")
private void someOperation(Request request) {}
To intercept a method that throws an exception you can use this code (works only if methods are public):
#AfterThrowing(pointcut="com.xyz.myapp.SystemArchitecture.dataAccessOperation()",throwing="ex")
public void doRecoveryActions(NullPointerException ex) {
// ...
}
Source: Spring AOP
Classes use compile time weaving.
Imagine I have the aspect class:
#Aspect
public class SecurityInterceptor {
#Pointcut("within(#org.springframework.stereotype.Controller *)")
public void beanAnnotatedWithController() {}
#Pointcut("execution(public * *(..)) && args(*,httpReq)")
public void publicMethods(HttpServletRequest httpReq) {}
#Pointcut("beanAnnotatedWithController() && publicMethods(httpReq)")
public void controllerMethods(HttpServletRequest httpReq) {}
#Pointcut("execution(public * *(..)) && args(httpReq)")
public void publicMethodsRequestOnly(HttpServletRequest httpReq) {}
#Pointcut("beanAnnotatedWithController() && publicMethodsRequestOnly(httpReq)")
public void controllerMethodsOneArg(HttpServletRequest httpReq) {}
#Around(value = "controllerMethods(httpReq)")
public Object populateSecurityContext(final ProceedingJoinPoint joinPoint, HttpServletRequest httpReq) throws Throwable {
return popSecContext(joinPoint, httpReq);
}
#Around(value = "controllerMethodsOneArg(httpReq)")
public Object populateSecurityContextOneArg(final ProceedingJoinPoint joinPoint, HttpServletRequest httpReq) throws Throwable {
return popSecContext(joinPoint, httpReq);
}
}
What is the correct way to use #DeclarePrecedence to determine the execution order?
Please read paragraph "Advice precedence" in the language semantics section of the AspectJ documentation.
Precedence of aspects can be declared explicitly, precedence of advice within a single aspect is determined by rules described in the document and cannot be changed, AFAIK. So #DeclarePrecedence will not help you in this case, only changing the order of advice within the aspect file.
If you're are looking for the the order of multiple aspects, you can create an aspect like:
#Aspect
#DeclarePrecedence("AuthorizationAspect, MySpecialAspect, LastAspect")
public class CoordinationAspect {
// empty
}
This will indeed work over multiple aspects. Inside a single aspect is another matter and can not be changed AFAIK, but I don't see why this would be an issue.
When two pieces of advice defined in the same aspect both need to run at the same join point, the ordering is undefined (since there is no way to retrieve the declaration order via reflection for javac-compiled classes). Consider collapsing such advice methods into one advice method per joinpoint in each aspect class, or refactor the pieces of advice into separate aspect classes - which can be ordered at the aspect level.
from Spring AOP documentation here (section 6.2.4.7. Advice ordering) https://docs.spring.io/spring/docs/2.0.x/reference/aop.html
If it helps in case you came looking for this here.