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.
Related
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)")
uppose ther is a class A :
public class A {
public B b;
public void justDoIt1(){
b.getB();
}
#SomeAnnotation
public void justDoIt2(){
b.getB();
}
}
and class B:
public class B{
public void getB(){
System.out.println("get");
}
}
How do we create pointcut to execution of B.getB() that is called from within a method annotated with #SomeAnnotation?
Here is what I've tried
#Aspect
public class LocalizationAspect {
#Before(value = "#within(Localize) && execution(* B.getB())")
public void aspectStuff() {
System.out.println("aspect");
}
}
just to make my point clear : expected output will be when calling justDoIt2();
aspect
get
but when calling justDoIt1();
get
Note: i'm using SpringAOP (maybe it has some restrictions on this)
Any help on this?
If I were using plain AspectJ I would do this:
execution(* B.getB()) && cflow(#withincode(SomeAnnotation))
"execution of getB() in the control flow of a method annotated with SomeAnnotation. But this does mean it will get caught if deeper in the flow, which may not be what you want. e.g. if the method annotated with SomeAnnotation calls something which calls something else which then calls getB() - that will get caught by this advice.
I don't know how it'll behave under Spring AOP though.
EDIT: On further thinking, the pointcut above is perhaps not optimal as #withincode() might create more byte code than absolutely necessary. A more optimal version is probably:
execution(* B.getB()) && cflow(execution(#SomeAnnotation * *(..)))
#withincode(SomeAnnotation) will advise all join points within a method marked #SomeAnnotation, but you are probably only interested in the execution join point.
Pointcut declaration:
#Pointcut(value="com.someapp.someservice.someOperation() && args(t,req)",argNames="t,req")
private void logOperationArg(final String t,final String req)
{
}
Advice Declaration not compiling:
#Before(value="logOperationArg(t,req)")
public void logBeforeOperationAdvice(JoinPoint jp, final String t, final String req){
...
}
When compiling Aspect with Aspectj-maven-plugin (1.5 version), have error "can not build thisJoinPoint lazily for this advice since it has no suitable guard [Xlint:noGuardForLazyTjp]"
But the same advice compiles without JoinPoint argument.
Advice Declaration compiling:
#Before(value="logOperationArg(t,req)")
public void logBeforeOperationAdvice(final String t, final String req){
...
}
Spring AOP only supports method join points because it is based on dynamic proxies which creates proxied object if it is needed (for example if you are using ApplicationContext, it will be created after beans are loaded from BeanFactory)
Use execution() statement to match join points which are methods execution.
For example:
class LogAspect {
#Before("execution(* com.test.work.Working(..))")
public void beginBefore(JoinPoint join){
System.out.println("This will be displayed before Working() method will be executed");
}
And now how to declare your BO:
//.. declare interface
then:
class BoModel implements SomeBoInterface {
public void Working(){
System.out.println("It will works after aspect");
}
}
execution() statement is a PointCut expression to tell where your advice should be applied.
If you like to use #PointCut, you can do something like this:
class LogAspect {
//define a pointcut
#PointCut(
"execution(* com.test.work.SomeInferface.someInterfaceMethod(..))")
public void PointCutLoc() {
}
#Before("PointCutLoc()")
public void getBefore(){
System.out.println("This will be executed before someInterfaceMethod()");
}
}
Part2:
Also,the Error shows that you haven't put a guard on your advice. Technically guard makes your code faster, because you do not need construct thisJoinPoint everytime you execute it. So, if it does not make sense you can try to ignore it
canNotImplementLazyTjp = ignore
multipleAdviceStoppingLazyTjp=ignore
noGuardForLazyTjp=ignore
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.
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.