Spring AOP - Point Cut not getting called - java

I have a SpringBoot Application.
I have defined an Annotation say "Track", and I have annotated few methods in different packages which I want aop to consider.
The annotation has been defined as below :
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Track {
}
I have not missed the #EnableAspectJAutoProxy in the #Configuration class of my package.
I have a Pointcut and an Advice defined in the Aspect like below :
#Aspect
#Component
public class MyAspect {
#Pointcut("execution(#Track * *.*(..))")
void annotatedMethod() {
// No Implementation required
}
#Around("annotatedMethod() && #annotation(methodLevelTrack)")
public void adviseAnnotatedMethods(ProceedingJoinPoint proceedingJoinPoint,
Track methodLevelTrack) throws Throwable {
// do some task
proceedingJoinPoint.proceed();
// do some task after the method is executed.
}
}
My intention is: for any method (annotated with #Track) in any package, with any access modifier, and any number of input arguments, and any return type, to follow the aspect's #Around advice.
Now, the interesting situation is as below :
I have a class say "Engine" which calls other classes and downstream systems to perform a long-running operation. Let's define the class as follows :
public class Engine {
// bunch of other autowired objects
public void processTask() {
<autowired_object_A>.someMethod() // this method has been annotated with #Track
<autowired_object_B>.someMethod() // this method has also been annotated with # Track
.... // bunch of other methods in other autowired objects that have been annotated with # Track
someMethodOfEngineClass(); // Now this has been defined in the Engine class as below, but pointcut doesn't recognize this method!
}
#Track
private void someMethodOfEngineClass() {
// do something
}
}
All the "other" autowired objects' methods are getting recognized by pointcut as expected but the method within this Engine class, that has been annotated with #Track, is not recognized. What's the mystery?
I have tried making "someMethodOfEngineClass" method public, return something instead of void and all those combinations and it doesn't work.
What am I missing?
Is it the pointcut definition expression?
I have defined the aspect in one of the sub packages, is aspect supposed to be defined at the top level in the package structure?
Can you folks please suggest something that can work? I am kinda stuck at this.

When you define aop spring creates proxy around the class,
so when the method is called, actually call is delegated to proxy, sth like
your.package.Engine$$FastClassBySpringCGLIB$$c82923b4.someMethodOfEngineClass()
But this works only when a method is called from outside it's class
If you call class method from the same class you are effectively calling it by this.someMethodOfEngineClass()
here -> http://www.nurkiewicz.com/2011/10/spring-pitfalls-proxying.html
you can find more info about proxying
so proxy is bypassed and aop is not working.

Related

AspectJ on children objects

I'm starting to work with AspectJ and I'm trying to do something that I don't know if it's possible. This is my code:
public abstract class MyAbstractObject<T> {
private T myOtherObject;
public T getMyOtherObject() {
return myOtherObject;
}
}
#Component
public class MyObject extends MyAbstractObject<WhateverObject> {
}
#Aspect
#Component
public class MyAspects {
#Before("execution(* mypackage.MyAbstractObject.getMyOtherObject().set*(*))")
public void beforeExample(JoinPoint joinPoint) {
// DO THINGS
}
}
This code fails, with the error:
Caused by: java.lang.IllegalArgumentException: Pointcut is not well-formed: expecting ')' at character position 58 execution(* mypackage.MyAbstractObject.getMyOtherObject().set*(*))
However, I can intercept MyOtherObject like this, but not it's setters:
#Aspect
#Component
public class MyAspects {
#Before("execution(* mypackage.MyAbstractObject.getMyOtherObject())")
public void beforeExample(JoinPoint joinPoint) {
// DO THINGS
}
}
I don't want to intercept the setters of the object MyOtherObject everywhere, because this object is used in more places in the program where I don't need aspects. However I want to intercept them only when used in a class that extends MyAbstractObject.
I'm using Spring with AspectJ.
Thanks.
The syntax you just invented is illegal, unfortunately. You cannot describe chained method calls the ways you dreamed up.
What you really want is to intercept method executions of type WhateverObject, not of MyAbstractObject or MyObject. I.e., your pointcut should rather bet something like
execution(* mypackage.WhateverObject.set*(*))
I am just guessing, but if you want to limit matching to control flows where the setter method is called (directly or indirectly) from getMyOtherObject(), in AspectJ your would add something like
&& cflow(execution(* mypackage.MyAbstractObject.getMyOtherObject()))
to the first pointcut. But cflow pointcuts are unavailable in Spring AOP, which is what you seem to be using. So
either you switch to native AspectJ, which is easy to integrate into Spring applications, but can also be used outside of the Spring context because it is an idependent product,
or you check if Spring AOP's "lite" version of control flow pointcuts does what you need. I posted a few answers and links mentioning this almost undocumented feature here and there.
As a native AspectJ fan who does not normally use Spring, you can imagine which option I would recommend regarding control flow pointcuts, but I think you can make your own decision.

Is it OK to inject value directly inside Bean function?

I was wondering how to do dependency injection in the most effective way inside my code.
I have this code:
#Configuration
public class SomeName {
#Autowired
private Other other;
#Bean
public void method() {
other.someMethod();
// some code
}
}
Can this code be changed into the following code(other will be used only inside this function)?
#Configuration
public class SomeName {
#Bean
public void method(Other other) {
other.someMethod();
// some code
}
}
You should avoid #Autowired if possible and inject using a constructor or method.
Starting with Java 9 and java modules (project jigsaw) there are some strict rules that make it harder for your framework to change the values of a private field.
What Spring is doing in the first example is essentially that - it breaks encapsulation to change the value of a private value. (There is a way to overcome this with "opens" directive in module-info..)
You are also becoming dependent on the framework you are using and your code becomes harder to test compared to when using a simple setter.
You are also not explicitly declaring that your class depends on another class since I can easily instantiate it and "Other" will be null.
Some resources:
https://docs.spring.io/spring-framework/docs/current/reference/html/core.html#beans-scanning-autodetection (search for jigsaw)
https://blog.marcnuri.com/field-injection-is-not-recommended/
PS: You are probably missing #Configuration on your class

Emulate annotation inheritance for interfaces and methods with AspectJ

Often people ask AspectJ questions like this one, so I want to answer it in a place I can easily link to later.
I have this marker annotation:
package de.scrum_master.app;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Inherited
#Retention(RetentionPolicy.RUNTIME)
public #interface Marker {}
Now I annotate an interface and/or methods like this:
package de.scrum_master.app;
#Marker
public interface MyInterface {
void one();
#Marker void two();
}
Here is a little driver application which also implements the interface:
package de.scrum_master.app;
public class Application implements MyInterface {
#Override
public void one() {}
#Override
public void two() {}
public static void main(String[] args) {
Application application = new Application();
application.one();
application.two();
}
}
Now when I define this aspect, I expect that it gets triggered
for each constructor execution of an annotated class and
for each execution of an annotated method.
package de.scrum_master.aspect;
import de.scrum_master.app.Marker;
public aspect MarkerAnnotationInterceptor {
after() : execution((#Marker *).new(..)) && !within(MarkerAnnotationInterceptor) {
System.out.println(thisJoinPoint);
}
after() : execution(#Marker * *(..)) && !within(MarkerAnnotationInterceptor) {
System.out.println(thisJoinPoint);
}
}
Unfortunately the aspect prints nothing, just as if class Application and method two() did not have any #Marker annotation. Why does AspectJ not intercept them?
The problem here is not AspectJ but the JVM. In Java, annotations on
interfaces,
methods or
other annotations
are never inherited by
implementing classes,
overriding methods or
classes using annotated annotations.
Annotation inheritance only works from classes to subclasses, but only if the annotation type used in the superclass bears the meta annotation #Inherited, see JDK JavaDoc.
AspectJ is a JVM language and thus works within the JVM's limitations. There is no general solution for this problem, but for specific interfaces or methods you wish to emulate annotation inheritance for, you can use a workaround like this:
package de.scrum_master.aspect;
import de.scrum_master.app.Marker;
import de.scrum_master.app.MyInterface;
/**
* It is a known JVM limitation that annotations are never inherited from interface
* to implementing class or from method to overriding method, see explanation in
* JDK API.
* <p>
* Here is a little AspectJ trick which does it manually.
*
*/
public aspect MarkerAnnotationInheritor {
// Implementing classes should inherit marker annotation
declare #type: MyInterface+ : #Marker;
// Overriding methods 'two' should inherit marker annotation
declare #method : void MyInterface+.two() : #Marker;
}
Please note: With this aspect in place, you can remove the (literal) annotations from the interface and from the annotated method because AspectJ's ITD (inter-type definition) mechanics adds them back to the interface plus to all implementing/overriding classes/methods.
Now the console log when running the Application says:
execution(de.scrum_master.app.Application())
execution(void de.scrum_master.app.Application.two())
By the way, you could also embed the aspect right into the interface so as to have everything in one place. Just be careful to rename MyInterface.java to MyInterface.aj in order to help the AspectJ compiler to recognise that it has to do some work here.
package de.scrum_master.app;
public interface MyInterface {
void one();
void two();
// Cannot omit 'static' here due to https://bugs.eclipse.org/bugs/show_bug.cgi?id=571104
public static aspect MarkerAnnotationInheritor {
// Implementing classes should inherit marker annotation
declare #type: MyInterface+ : #Marker;
// Overriding methods 'two' should inherit marker annotation
declare #method : void MyInterface+.two() : #Marker;
}
}
Update 2021-02-11: Someone suggested an edit to the latter solution, saying that the aspect MarkerAnnotationInheritor nested inside interface MyInterface is implicitly public static, so the modifiers in the aspect declaration could be omitted. In principle this is true, because members (methods, nested classes) of interfaces are always public by default and a non-static inner class definition would not make sense inside an interface either (there is no instance to bind it to). I like to be explicit in my sample code, though, because not all Java developers might know these details.
Furthermore, currently the AspectJ compiler in version 1.9.6 throws an error if we omit static. I have just created AspectJ issue #571104 for this problem.

How to add a field to a custom-annotated class using AspectJ

To add a field to some specific class with aspectj we do
package com.test;
public class MyClass {
private String myField;
}
public aspect MyAspect
{
private String MyClass.myHiddenField;
}
How do we add a field to a class that is annotated with some custom annotation?
example usage : if class is annotated with #CustomLoggable add a Logger field and some methods.
or
if method has the #ReadLocked annotation then class will have a ReentrantReadWriteLock field and the appropriate logic injected, etc.
Actually you cannot make inter-type declarations (ITD) on annotation types, i.e. you need to know concrete class names in order to declare static or non-static members or methods directly.
The usual workaround is:
Create an interface with all the methods you need.
Provide implementations for each interface method.
Make each annotated type implement the interface via ITD.
Now if you also want to add a static member such as a logger to all annotated types, again if you do not know the exact class names you need to use a workaround:
Create an aspect holding the desired member(s). Let's call it LoggerHolder in this example.
Make sure that one aspect instance per target class is created instead of the default singleton aspect instance. This is done via pertypewithin.
In order to avoid runtime exceptions you must not initialise the members directly via Logger logger = ... but need to do it lazily, waiting until after the target type's static initialisation phase is finished.
You also need to provide an accessor method like LoggerHolder.getLogger() in the aspect and call it whenever necessary.
In order to hide all the ugly aspect stuff from the end user I recommend to add yet another accessor method LoggableAspect.getLogger() (same method name for convenience) to the ITD interface mentioned above and provide a method implementation extracting the member reference from the aspect instance via LoggerHolder.aspectOf(this.getClass()).getLogger().
Attention: I am using two concepts at once here, mixing them in one application because you asked for both static members and non-static methods added to annotated classes:
Helper interface + implementation added to your core code via ITD
Holder aspect declaring member(s) and associated with target classes via pertypewithin in order to emulate static members
Now here is some sample code:
Annotation:
package de.scrum_master.app;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
#Retention(RetentionPolicy.RUNTIME)
public #interface CustomLoggable {}
Two classes, one bearing the annotation and one not bearing it:
package de.scrum_master.app;
public class OrdinaryClass {
public void doSomething() {
System.out.println("Logging some action directly to console");
}
}
package de.scrum_master.app;
import java.util.logging.Level;
#CustomLoggable
public class AnnotatedClass {
public void doSomething() {
getLogger().log(Level.INFO, "Logging some action via ITD logger");
getLogger().log(Level.INFO, someOtherMethod(11));
}
}
As you can see, the second class uses two methods which have not been declared directly within the class: getLogger() and someOtherMethod(int). Both of them will be declared via ITD further below, the former providing access to the pseudo-static member and the latter being just another method you want declared on each annotated class.
Aspect holding an instance of the pseudo-static member:
package de.scrum_master.aspect;
import java.util.logging.Logger;
import de.scrum_master.app.CustomLoggable;
public aspect LoggerHolder
pertypewithin(#CustomLoggable *)
{
private Logger logger;
after() : staticinitialization(*) {
logger = Logger.getLogger(getWithinTypeName());
}
public Logger getLogger() {
return logger;
}
}
As I said earlier, please note the usage of pertypewithin and staticinitialization. Another convenient thing is to use the aspect's getWithinTypeName() method in order to get the target class name for naming the logger.
Aspect declaring an interface + implementation and applying it to all target types:
package de.scrum_master.aspect;
import java.util.logging.Logger;
import de.scrum_master.app.CustomLoggable;
public aspect LoggableAspect {
public static interface Loggable {
Logger getLogger();
String someOtherMethod(int number);
}
declare parents : (#CustomLoggable *) implements Loggable;
public Logger Loggable.getLogger() {
return LoggerHolder.aspectOf(this.getClass()).getLogger();
}
public String Loggable.someOtherMethod(int number) {
return ((Integer) number).toString();
}
}
For simplicity, I just declared the interface as a static nested type within the aspect. You can also declare the interface separately, but here you see it in its context which for me is preferable.
The key thing here is the declare parents statement making each target class implement the interface. The two method implementations at the end show how to provide "normal" method implementations as well as how to access the logger from the holder aspect via aspectOf.
Driver class with entry point:
Last, but not least, we want to run the code and see if it does what we want.
package de.scrum_master.app;
public class Application {
public static void main(String[] args) {
new OrdinaryClass().doSomething();
new AnnotatedClass().doSomething();
}
}
Console output:
Logging some action directly to console
Mrz 15, 2015 11:46:12 AM de.scrum_master.app.AnnotatedClass doSomething
Information: Logging some action via ITD logger
Mrz 15, 2015 11:46:12 AM de.scrum_master.app.AnnotatedClass doSomething
Information: 11
VoilĂ ! Logging works, the Logger has a nice name de.scrum_master.app.AnnotatedClass and calling the two interface methods works as expected.
Alternative approach:
Since AspectJ 1.8.2 annotation processing is supported, see also this blog post. I.e. you could use APT in order to generate one aspect per annotated type and introduce static members and additional methods directly without any tricks such as per-type instantiation, accessor methods members within holder aspect instances and interfaces. This comes at the cost of an additional build step, but I think it would be a very neat and straightforward way to solve your problem. Let me know if you have any difficulty understanding the examples and need more help.
You can create a pointcut for any type with a particular annotation. See Join Point Matching based on Annotations.

Can't Get Guice Method Interception to Work

I'm trying to print a "Hello, AOP!" message whenever Guice/AOP Alliance intercepts a method marked with a particular (custom) annotation. I have followed the official docs (a PDF that can be found here - AOP method interception stuff on pg. 11) and cannot get it to work, only compile.
First, my annotation:
#Retention(RetentionPolicy.RUNTIME)
#Target({ElementType.METHOD})
#BindingAnnotation
public #interface Validating {
// Do nothing; used by Google Guice to intercept certain methods.
}
Then, my Module implementation:
public class ValidatingModule implements com.google.inject.Module {
public void configure(Binder binder) {
binder.bindInterceptor(Matchers.any(),
Matchers.annotatedWith(Validating.class,
new ValidatingMethodInterceptor()),
}
}
Next, my method interceptor:
public class ValidatingMethodInterceptor implements MethodInterceptor {
public Object invoke(MethodInvocation invocation) throws Throwable {
System.out.println("Hello, AOP!");
}
}
Finally, the driver that attempts to make use of all this AOP stuff:
public class AopTest {
#Validating
public int doSomething() {
// do whatever
}
public static main(String[] args) {
AopTest test = new AopTest();
Injector injector = Guice.createInjector(new ValidatingModule());
System.out.println("About to use AOP...");
test.doSomething();
}
}
When I run this little test driver, the only console output I get is About to use AOP...... the Hello, AOP! never gets executed, which means the #Validating doSomething() method is never being intercepted the way the Guice docs show.
The only thing I can think of is the fact that in my Module implementation I am specifying the MethodInterceptor to bind to (as the 3rd argument to the bindInterceptor method) as being a new ValidatingMethodInterceptor(), whereas in that interceptor, I am only defining a required invoke(MethodInvocation) method.
Perhaps I am not wiring these two together correctly? Perhaps Guice doesn't implicitly know that the invoke method should be ran when an intercept occurs?!?!
Then again, not only have I followed the Guice docs, I have also followed several other tutorials to no avail.
Is there something obvious I am missing here? Thanks in advance!
Edit One other discrepancy between my code and the examples I followed, although small, is the fact that my invoke method (inside the interceptor) is not annotated with #Override. If I try to add this annotation, I get the following compile error:
The method invoke(MethodInvocation) of type ValidatingMethodInterceptor must override a superclass method.
This error makes sense, because org.aopalliance.intercept.MethodInterceptor is an interface (not a class). Then again, every example using Guice/AOP Alliance uses this #Override annotation on the invoke method, so it obviously works/compiles for some people...weird.
If you don't let Guice construct your object, it can't provide you an instance with the interceptor wrapped around. You must not use new AopTest() to get an instance of your object. Instead, you must ask Guice to give you one instance:
Injector injector = Guice.createInjector(new ValidatingModule ());
AopTest test = injector.getInstance(AopTest.class);
See http://code.google.com/p/google-guice/wiki/GettingStarted

Categories