AspectJ: execution order (precedence) for multiple advice within one aspect - java

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.

Related

How to write Pointcut expression for Spring AOP to weave meta-annotation in any level of deepness?

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.

#PostFilter does not work with Spring #Aspect

I am trying to do something like this:
#Component
#Aspect
class CustomAspect {
#Pointcut("within(#com.example.security.Check *)")
public void classAnnotatedWithCheck() {}
#Pointcut("execution(public * *(..))")
public void publicMethod() {}
#Pointcut("publicMethod() && classAnnotatedWithCheck()")
public void publicMethodInsideAClassMarkedWithCheck() {}
#Around(value = "publicMethodInsideAClassMarkedWithCheck()")
public Object execute(ProceedingJoinPoint point) throws Throwable {
return executeWithFilter(point);
}
#PostFilter(value = "hasPermission(filterObject, 'READ')")
private Object executeWithFilter(ProceedingJoinPoint point) throws Throwable {
return point.proceed();
}
}
Aspects work well, but the last method executeWithFilter is done without filtering. At the same time, filtering works if I add #PostFilter(value = "hasPermission(filterObject, 'READ')") to a regular service method. Is it even possible to use #PostFilter in aspects?
Spring Method Security is AOP based.
Also , from the Spring AOP reference documentation : Declaring an Aspect
In Spring AOP, aspects themselves cannot be the targets of advice from
other aspects. The #Aspect annotation on a class marks it as an aspect
and, hence, excludes it from auto-proxying.
Security advices should get applied on a bean for providing #PostFilter method security , which is not possible on an aspect. In short Spring security will not work on an aspect.

Create Pointcut for Spring AOP annotation to put annotation on class and execute advice on every method in class

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)")

AspectJ Control Flow/Multiple Joinpoints

I am trying to learn AspectJ and figuring out how to retrieve specific joinpoints at certain points in a flow. My example is something like this:
I want to run an unit test annotated with JUnit's #Test then any methods called in that test that may be in another class annotated with another annotation, let's say #Example, then I could access basically the entire flow at those certain points so I had the ability to get the class name/method name of the test annotated with #Test and then also get the method information for the method annotated #Example. I've included some example code for clarification:
Test class:
public class ExampleTest {
#Test
public void shouldTestSomething() {
ExampleClass exampleClazz = new ExampleClass();
exampleClazz.something();
// ...
}
POJO:
public class ExampleClass {
#Example
public void something() {
// do something
end
end
So with these classes, I would like to make an aspect that would basically find any kind of #Example called within a #Test so I can then have access to both (or more) join points where I can grab the method/class signatures from the AspectJ JoinPoint object.
I tried something like this:
#Aspect
public class ExampleAspect {
#Pointcut("execution(#org.junit.Test * *(..))
&& !within(ExampleAspect)")
public void testPointCut() {}
#Pointcut("#annotation(com.example.Example)")
public void examplePointCut() {}
#After("cflow(testPointCut) && examplePointCut()")
public void doSomething(JoinPoint joinPoint) {
System.out.println(joinPoint.getSignature());
}
}
But the output looks like this:
void ExampleTest.ExampleClass.something()
The main issue is that it is missing the name of the test method (shouldTestSomething()) in the signature. What would be the best way to retrieve that?
Not sure if I understand correctly, but if you need to access information about the context from where a piece of code under a join point of your interest gets called, then what you need is the thisEnclosingJoinPointStaticPart (in native AspectJ syntax). If you are using AspectJ 5 annotation style aspects, just add a parameter to your advice method with the type JoinPoint.EnclosingStaticPart.
Note that this is not available for execution() style pointcuts, only for call() style pointcuts, otherwise the JoinPoint.EnclosingStaticPart and JoinPoint.StaticPart will be the same.
This means you need to rewrite your aspect in the following way:
#Aspect
public class ExampleAspect {
#Pointcut("execution(#org.junit.Test * *(..)) && !within(ExampleAspect)")
public void testPointCut() {
}
#Pointcut("call(#com.example.Example * *(..))")
public void examplePointCut() {
}
#After("cflow(testPointCut()) && examplePointCut()")
public void doSomething(JoinPoint joinPoint, EnclosingStaticPart enclosingStaticPart) {
System.out.println(joinPoint.getSignature() + " was invoked from "
+ enclosingStaticPart.getSignature());
}
}
The output with your test code would be:
void q35991663.com.example.ExampleClass.something() was invoked from void q35991663.com.example.ExampleTest.shouldTestSomething()
I also rewrote your examplePointCut. The pointcut expression #annotation(com.example.Example) would mean
any join point where the subject has an annotation of type com.example.Example
which would include both execution() and call() type join points. We need only the call() join points in this case, so #annotation() isn't even necessary if you are not planning to bind the value of the annotation to the advice context.

Pointcut matching methods with annotated parameters

I need to create an aspect with a pointcut matching a method if:
Is public
Its class is annotated with #Controller (Finally does not)
One of its parameters (can have many) is annotated with #MyParamAnnotation.
I think the first two conditions are easy, but I don't know if its possible to accomplish the third with Spring. If it is not, maybe I can change it into:
One of its parameters is an instance of type com.me.MyType (or implements some interface)
Do you think it's possible to achieve this? And will performance be good?
Thanks
EDIT: One example of a matching method. As you can see, MyMethod is not annotated (but it can be).
#Controller
public class MyClass {
public void MyMethod (String arg0, #MyParamAnnotation Object arg1, Long arg3) {
...
}
}
EDIT: The solution I finally used, based on #Espen answers. As you can see, I changed my conditions a little: class doesn't actually need to be a #Controller.
#Around("execution(public * * (.., #SessionInject (*), ..))")
public void methodAround(JoinPoint joinPoint) throws Exception {
...
}
It was an interesting problem, so I created a little sample application to solve the case! (And improved it with Sinuhe's feedback afterwards.)
I have created a DemoController class that should work as an example for the aspect:
#Controller
public class DemoController {
public void soSomething(String s, #MyParamAnnotation Double d, Integer i) {
}
public void doSomething(String s, long l, #MyParamAnnotation int i) {
}
public void doSomething(#MyParamAnnotation String s) {
}
public void doSomething(long l) {
}
}
The aspect that will add a join point on the first three methods, but not the last method where the parameter isn't annotated with #MyParamAnnotation:
#Aspect
public class ParameterAspect {
#Pointcut("within(#org.springframework.stereotype.Controller *)")
public void beanAnnotatedWithAtController() {
}
#Pointcut("execution(public * *(.., #aspects.MyParamAnnotation (*), ..))")
public void methodWithAnnotationOnAtLeastOneParameter() {
}
#Before("beanAnnotatedWithAtController() "
+ "&& methodWithAnnotationOnAtLeastOneParameter()")
public void beforeMethod() {
System.out.println("At least one of the parameters are "
+ "annotated with #MyParamAnnotation");
}
}
The first pointcut will create a joinpoint on all methods inside classes marked with #Controller.
The second pointcut will add a joinpoint when the following conditions are met:
public method
first * is a wildcard for every return type.
second * is a wildcard for all methods in all classes.
(.., matches zero to many parameters of any type before the annotated parameter.
#aspects.MyParamAnnotation (*), matches a parameter annotated with the given annotation.
..) matches zero to many parameters of any type after the annotated parameter.
Finally, the #Before advice advises all methods where all conditions in both pointcuts are satisfied.
The pointcut works with both AspectJ and Spring AOP!
When it comes to performance. The overhead is small, especially with AspectJ that does the weaving on compile-time or load-time.

Categories

Resources