I'm trying to create an annotation to log all methods in annotated class, but I have a problem with my pointcut, it's not applied (AspectJ version 1.7.4, aspectj-maven-plugin version 1.7).
(advice defined in com.test.util.log.Logger has not been applied
[Xlint:adviceDidNotMatch]).
Pointcut:
#Pointcut(value = "execution(* (#Loggable *).*(..))"))
Annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target(value = { ElementType.METHOD, ElementType.CONSTRUCTOR, ElementType.TYPE })
public #interface Loggable {
public enum Level {
TRACE, DEBUG, INFO, WARN, ERROR, FATAL
};
boolean entry() default true;
boolean exit() default true;
String prefix() default "";
String suffix() default "";
Level level() default Level.DEBUG;
}
Thank you
I assume that the annotation is not in the unnamed top level package but in a package like com.company.application.subpackage. If this is true you need to use the fully qualified package name in annotation-style #AspectJ. In native syntax that would not be necessary because you could use imports there. So the pointcut should be:
#Pointcut("execution(* (#com.company.application.subpackage.Loggable *).*(..))"))
The way you use the parentheses makes the pointcut only match methods of classes annotated by #Loggable. The annotation's #Target definition says that it can also be applied to methods and constructors. Those will not be matched by your pointcut, you would have to modify it for that purpose. I hope you know that, I am just mentioning it for safety.
[Xlint:adviceDidNotMatch]) means that your point cut was not applied in the compiled project. Most likely you didn't place your annotation on any method.
PS I also recommend not to reinvent the wheel and try
aspect4log
before(): execution(* YourOwnPackage.*.*(..))
{
//packages is com
System.out.println(" TEST");
}
Use this as a start to find your own advice.
Related
How can we enable/disable an aspect using environment variables?
I know it is possible to enable/disable aspectj in spring boot application using following properties
spring:
aop:
auto: true
Or:
spring.aop.auto=true
And removing #EnableAspectJAutoProxy, but this stops all of our other aspects / joins.
This is the one I want to disable, how do I do it
#Aspect
#Component
public class SomeAspect {
#Around("#annotation(someAnnotation)")
public Object doSomething(ProceedingJoinPoint joinPoint, SomeAnnotation sa) throws Throwable {
// ...
}
//others
}
In order to dynamically deactivate a single advice inside an aspect class, you can use an if() pointcut.
If you want to completely disable an aspect (or any other Spring bean or component) based on conditions like e.g. a property in application.config, have a look at #Conditional and its special cases #ConditionalOn*. For example:
#Aspect
#Component
#ConditionalOnProperty(prefix = "org.acme.myapp", name = "aspect_active")
public class SomeAspect {
// ...
}
Something like this in application.config would deactivate the aspect:
org.acme.myapp.aspect_active=false
The aspect would also be inactive if there was no such property in the application config at all. If you rather want to default to an active aspect, just use
#ConditionalOnProperty(prefix = "org.acme.myapp", name = "aspect_active", matchIfMissing = true)
You can further fine-tune the behaviour as described in the javadoc.
See also:
https://www.baeldung.com/spring-conditional-annotations
https://www.baeldung.com/spring-conditionalonproperty
Update:
In order to dynamically deactivate a single advice inside an aspect class, you can use an if() pointcut.
Oops, sorry, I am a native AspectJ user and forgot that Spring AOP does not support the if() pointcut designator. So probably the best you can do is an if expression at the beginning of your advice, depending on a #Value property.
#Value("${org.acme.myapp.advice_active:false}")
private boolean adviceActive;
#Around("#annotation(someAnnotation)")
public Object doSomething(ProceedingJoinPoint joinPoint, SomeAnnotation sa) throws Throwable {
// Skip advice logic if inactive, simply proceed and return original result
if (!adviceActive)
return joinPoint.proceed();
// Regular advice logic if active
System.out.println(joinPoint);
// Either also proceed or do whatever else is the custom advice logic, e.g.
// - manipulate method arguments,
// - manipulate original return value,
// - skip proceeding to the original method altogether and return something else.
return joinPoint.proceed();
}
Of course, you can also use my original solution and just factor out the advice you wish to deactivate into a separate aspect class, if you need that kind of granularity. That would be less hassle, and the advice method code would be more readable.
I have an annotation which is a class level annotation
#Dummy(value = 123)
How I do create an aspect which gets invoked before any method execution of this annotated class. I would like to just print the value of annotation in the aspect's advice.
Following aspect would achieve the same
#Component
#Aspect
public class DummyAspect {
#Before(value = "#target(dummy) && within(com.your.package..*)")
public void before(JoinPoint jp, Dummy dummy) {
System.out.println(dummy.value());
}
}
within() - is a scoping designator to narrow the scope of classes to be advised. Without this designator the run could have undesired outcome as it could target framework classes as well.
Do go through this answer from #kriegaex to understand the designator in detail.
References : https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#aop-pointcuts-designators
I want to create a pointcut expression which is dynamic in nature.
I have three packages -
package1,
package2,
common
common should always be inculded and based on system property i want to load package1 OR package2 at any given time
Something like below
private static final String PACKAGE = System.getProperty("packagePrefix");
#Around("execution(* "+PACKAGE+"..*.*(..)) && execution(* ..common.*(..))")
how can i achieve this?
EDIT:
I have found this which is quite interesting and i guess will solve my requirement but not able to get it working
So this link says to have like below
#Aspect
public abstract class MyAspect {
protected abstract pointcut();
#Before("pointcut()")
public void myAdviceMethod() {
// Advice code goes here
}
}
public class ConcreteAspect extends MyAspect {
#Pointcut("execution(* com.acme.*.*(..))")
protected pointcut() {
)
}
Included below in my Java config
#Bean
public ConcreteAspect myAspect() {
return new ConcreteAspect();
}
But getting below error :
java.lang.IllegalArgumentException: error at ::0 can't find referenced
pointcut pointcut
I guess at run time its not able to find out overriden pointcut method and hence not able to resolve pointcut.
Any idea how can I fix this?
You could use an if() clause to make a check like this:
aspect X {
before(): execution(* Code.*(..)) &&
if(isOfInterest(thisJoinPointStaticPart.getSignature().getDeclaringType())) {
System.out.println("advice running");
}
public static boolean isOfInterest(Class s) {
return s.getPackage().toString().startsWith(packagePrefix);
}
}
I think something similar will work for annotation style syntax. But this approach will include a runtime test at every matched join point.
You can use within, which will scan the given packages
#Pointcut("within(..common..(..)")
public void CommonPackage(){}
#Pointcut("within(..PACKAGE..(..)")
public void specificPackage(){}
And using #Around/#Before or any advice as u require for your situation
#Around("execution(CommonPackage()&&SpecificPackage()")
Writing the pointcut expression for the different packages and binding them together. Hope this helps!
Is it somehow possible to list all Joinpoints that are matching a given Pointcut in Spring's Aspect Oriented Programming.
I guess spring has some kind of registry where all Joinpoints are in at runtime.
e.g. for
#Pointcut("execution(* transfer(..))")
there should be a list somewhere, containing all methods that are called "transfer"
I do not know how Spring exactly handles AOP internally, but I do know it creates dynamic proxies (JDK or CGLIB types) during runtime. So there might not be a complete list unless all target classes/interfaces are already loaded when you generate your report.
But I have an elegant workaround for you: Just for the purpose of generating the report, compile your aspects with pure AspectJ (AspectJ syntax is basically a superset of Spring AOP), using this option for the AspectJ compiler ajc:
-showWeaveInfo display information about weaving
It will list all woven joinpoints. For statically determined joinpoints like execution() this is ideal. Dynamic joinpoints like cflow(), cflowbelow() (both are not supported by Spring AOP anyway) and if() can only be evaluated during runtime, in those cases more joinpoints will be woven than might actually be used later during runtime, just to be on the safe side and not miss any possible joinpoints.
Besides, in Eclipse using AJDT (AspectJ Development Tools) you have nice cross-reference views in which you can list and dynamically navigate from pointcuts to affected joinpoints and vice versa.
Just give it a spin, good luck! But maybe someone knows a pure Spring solution for you. I do not because I never used Spring.
P.S.: Here is a little example in pure AspectJ:
Java classes:
package de.scrum_master.app;
public class Application {
public int transfer() { return 11; }
public void transfer(int number) { }
}
package de.scrum_master.app;
public class Whatever {
public String transfer(int number) { return "foo"; }
public void transfer(String text) { }
}
Aspect:
package de.scrum_master.aspect;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
#Aspect
public class TransferInterceptor {
#Before("execution(* transfer(..))")
public void intercept(JoinPoint thisJoinPoint) {
System.out.println(thisJoinPoint);
}
}
Compiler output with -showWeaveInfo:
Join point 'method-execution(java.lang.String de.scrum_master.app.Whatever.transfer(int))' in Type 'de.scrum_master.app.Whatever' (Whatever.java:4) advised by before advice from 'de.scrum_master.aspect.TransferInterceptor' (TransferInterceptor.aj:10)
Join point 'method-execution(void de.scrum_master.app.Whatever.transfer(java.lang.String))' in Type 'de.scrum_master.app.Whatever' (Whatever.java:5) advised by before advice from 'de.scrum_master.aspect.TransferInterceptor' (TransferInterceptor.aj:10)
Join point 'method-execution(int de.scrum_master.app.Application.transfer())' in Type 'de.scrum_master.app.Application' (Application.java:4) advised by before advice from 'de.scrum_master.aspect.TransferInterceptor' (TransferInterceptor.aj:10)
Join point 'method-execution(void de.scrum_master.app.Application.transfer(int))' in Type 'de.scrum_master.app.Application' (Application.java:5) advised by before advice from 'de.scrum_master.aspect.TransferInterceptor' (TransferInterceptor.aj:10)
I wanted to instrument a large number of classes to use with Spring Insight and instead of adding the #InsightOperation manually to the methods, I wrote an aspect to annotate the methods using point cuts.
However, this is not working. While the manual annotation affects the Spring Insight trace logging, the AspectJ method does not work.
Is there anything I am doing wrong here? (I decompiled the classes after aspectizing and do find the annotation in the class methods)
This is the aspect code snippet:
declare #method :public * com.example.IExample.execute(..) : #InsightOperation;
Spring documentation says this:
Use of the #Insight* annotations are
optional. They make it easy for end
users to define custom operation
frames and end points without needing
to create a plug-in. Because end user
code modification is required to use
the annotations, they are an option
for users who cannot or do not wish to
write aspects.
http://static.springsource.com/projects/tc-server/2.5/devedition/htmlsingle/devedition.html
So looks like the only way is to write a custom plugin
http://static.springsource.com/projects/tc-server/2.5/devedition/htmlsingle/devedition.html#tutorial-plugin
It is possible that the Insight LTW does not pick up your introduced annotations. I'll have to dig deeper on that.
In the meantime, you can try a more low-level annotation:
com.springsource.insight.collection.method.MethodOperationsCollected
If you look at the spring-core plugin, you will see that it does something similar:
public aspect RepositoryMethodOperationCollectionAspect {
declare #type: #Repository * : #MethodOperationsCollected;
}
An easy work around is to call another method from within your aspect method to continue executing the join point. I only tried calling a static method in a static class. See below my code for adding the #InsightOperation to all my JSON serialization.
My aspect:
#Aspect
public class JSONSerializerAspect {
#Around("call(* *.JSONSerializer.serialize(..)) && args(target)")
public Object serialize(ProceedingJoinPoint joinPoint, Object target) throws Throwable {
return JSONSerializationWrapper.serialize(joinPoint, target);
}
}
The static class it is calling:
public class JSONSerializationWrapper {
#InsightOperation(label = "JSON_SERIALIZATION")
public static Object serialize(ProceedingJoinPoint joinPoint, Object target) throws Throwable {
return joinPoint.proceed(new Object[]{target});
}
}
I'm using this myself and tested that it works.