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.
Related
Let's say I need to enhance the shower() method with a #MusicAround advice to give me some music before and after executing the shower() method.
public class Me {
#MusicAround
public void shower() {
// shower code omitted
}
}
First I created the new annotation #MusicAround.
#Target(ElementType.METHOD)
#Retention(RetentionPolicy.RUNTIME)
public #interface MusicAround {
Then bind it with an aspect MusicAspect.
#Aspect
public class MusicAspect {
#Around("#annotation(MusicAround)")
public Object musicAround(ProceedingJoinPoint joinPoint) throws Throwable {
IPhone iphone = new IPhone();
Iphone.music();
joinPoint.proceed();
iphone.music();
}
}
Configure MusicAspect as a Bean. #EnableAspectJAutoProxy annotation leaves spring to encapsulate the aspect proxy for me.
#Configuration
#EnableAspectJAutoProxy
public class ApplicationConfig {
// ... other beans omitted
#Bean
public MusicAspect musicAspect() {
return new MusicAspect();
}
}
In main method, get Me instance from context, and execute shower() method.
public static void main(String[] args) {
try {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(ApplicationConfig.class);
Me me = context.getBean(Me.class);
me.shower();
context.close();
} catch (ApplicationContextException ace) {
// handle exception
}
}
Now I can enjoin the music during the shower.
<hey jude>
I'm showering
<don't be so serious>
The problem is that in this way MusicAspect class is coupled with IPhone class. I want to decouple them by injecting IPhone object as a parameter as below,
#Aspect
public class MusicAspect {
#Around("#annotation(MusicAround)")
public Object musicAround(ProceedingJoinPoint joinPoint, IPhone iphone) throws Throwable {
iphone.music();
joinPoint.proceed();
iphone.music();
}
}
Of cause a second parameter "iphone" in musicAround() method will not be allowed here. Is there any spring features that I can use to decouple IPhone and MusicAspect in this case?
!Note: thanks #kriegaex for the proofreading.
This is a preliminary answer, because the content is not suitable for a comment.
When looking at the code in your updated question, some things strike me as strange:
I wonder why everyone is so eager to always use aspects in combination with annotations. Why not use a pointcut which directly targets packages, classes or methods of interest? All this annotation pollution is horrible, if not absolutely necessary. Ideally, the application code should be completely agnostic of the existence of aspects.
You use the #annotation pointcut designator incorrectly. Instead of #annotation(#MusicAround) it should be #annotation(MusicAround). But also that only works if the annotation happens to be in the exact same package as the aspect, otherwise you need #annotation(fully.qualified.package.name.MusicAround).
You use a MusicAspect, but then declare a MinstrelAroundAdvice bean. That does not seem to match. Besides, an aspect is an aspect, the method inside it which actually does something is the advice. So the class name *Advice for an aspect is simply wrong. Better use *Aspect instead or something else which properly describes what the aspect does. In this case, MusicAspect seems just fine to me.
Now concerning your actual question, it is still unclear to me. Is it about how to inject (auto-wire) another bean into an aspect instance?
Of course I was not allowed to do so.
Why "of course"? What was not allowed? How did you notice? Did something not work? Did you get an error message? A stack trace? Please explain clearly what you tried, what the expected result is and what happened instead. Make your problem reproducible. Your code snippets do not do that, unfortunately. Just imagine for a minute that someone else would ask you the same question without you seeing the full code and without other context information necessary to understand the problem. Could you answer it? If your helpers do not understand the problem, how can they answer your question? Please be advised to learn what an MCVE is.
Problem solved with the help of #k-wasilewski and #kriegaex. Thanks bro.
The answer is #Autowired annotation.
Define IPhone as a field of MusicAspect class. Add #Autowired tag, which tells spring context to initialize the IPhone instance for us.
#Aspect
public class MusicAspect {
#Autowired
private IPhone iphone;
#Around("#annotation(MusicAround)")
public Object musicAround(ProceedingJoinPoint joinPoint) throws Throwable {
Iphone.music();
joinPoint.proceed();
iphone.music();
}
}
Don't forget to register IPhone bean in ApplicationConfig. The rest part remain the same.
#Configuration
#EnableAspectJAutoProxy
public class ApplicationConfig {
// ... other beans omitted
#Bean
public IPhone iphone() {
return new IPhone();
}
}
The code passed unit-test on my laptop.
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.
I want to inject some code through annotations in java.
The plan is I have two methods beginAction() and endAction(). I want to annotate a method such that before executing the statements in the method the beginAction() will be put and after finishing executing them the endAction() will be put automatically. Is it possible. If yes please suggest me how to do.
#MyAnnotation
public void myMethod(){
// Statement 1;
// Statement 2;
}
At runtime the beginAction() and endAction() should be injected in the method through the annotation. That is it should become like the following at runtime.
public void myMethod{
beginAction();
// Statement 1;
// Statement 2;
endAction();
}
It looks like you need aspects. AspectJ is the most popular library in this case. You can read more about it here: https://eclipse.org/aspectj/docs.php
And here's the example of such aspect in use:
Class with intercepted method:
public class YourClass {
public void yourMethod() {
// Method's code
}
}
Aspect itself:
#Aspect
public class LoggingAspect {
#Around("execution(* your.package.YourClass.yourMethod(..))")
public void logAround(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("Do something before YourClass.yourMethod");
joinPoint.proceed(); //continue on the intercepted method
System.out.println("Do something after YourClass.yourMethod");
}
}
You can't do it using just plain Java. However, there is a Java-like language that does allow this. It's called Xtend. It compiles to Java, not bytecode, so it benefits from all the wonderful things your Java compiler does.
It started life as an Eclipse project, but is now also available for IntelliJ.
One of its many features is a thing called "Active Annotations". They do exactly what you're asking for: they allow you to participate in the code generation process, so you can insert your beginAction() and endAction() methods as you want.
See http://www.eclipse.org/xtend/documentation/204_activeannotations.html for more info on Active Annotations.
I am new to Spring AOP. Based on my understanding, I noticed that both Advisor (for example DefaultPointcutAdvisor) and Aspect (for example the class annotated with #Aspect) can both help to solve the cross-cutting problem by doing something more when a method is invoked.
What is the different between these two term please?
Most aspects are a combination of advice that defines the
aspect’s behavior and a pointcut defining where the aspect should be executed.
Spring recognizes this and offers advisors, which combine advice and pointcuts
into one object.
More specifically, the PointcutAdvisor does this.
public interface PointcutAdvisor {
Pointcut getPointcut();
Advice getAdvice();
}
Most of Spring’s built-in pointcuts also have a corresponding PointcutAdvisor.
This is convenient if you want to define a pointcut and the advice it is managing
in one place.
Read more in Spring in Action, 3rd Edition
Sanpshots
Advisors seem to be an old "AOP lite" type of defining cross-cutting concerns from Spring 1.2 when Java 5 usage was still somewhat uncommon and thus #AspectJ syntax (via Java annotations) not used in Spring. The concept has still survived for lovers of schema-based AOP rather than annotation-based AOP or pure AspectJ syntax, see Spring documentation on advisors.
The concept of "advisors" comes from the AOP support defined in Spring and does not have a direct equivalent in AspectJ. An advisor is like a small self-contained aspect that has a single piece of advice. The advice itself is represented by a bean and must implement one of the advice interfaces described in Advice Types in Spring. Advisors can take advantage of AspectJ pointcut expressions.
In my understanding, Aspect is just Aspect Oriented Programming jargon and Advisor is Spring Framework jargon.
Maybe this simple example will be helpful:
Foo.java
public interface Foo {
void foo();
void baz();
}
FooImpl.java
public class FooImpl implements Foo {
#Override
public void foo() {
System.out.println("Foo!");
}
#Override
public void baz() {
System.out.println("Baz!");
}
}
MethodBeforeAdviceBarImpl.java
import org.springframework.aop.MethodBeforeAdvice;
import java.lang.reflect.Method;
public class MethodBeforeAdviceBarImpl implements MethodBeforeAdvice {
#Override
public void before(Method method, Object[] args, Object target) throws Throwable {
System.out.println("Bar!");
}
}
and finally App.java
import org.springframework.aop.MethodBeforeAdvice;
import org.springframework.aop.framework.ProxyFactory;
import org.springframework.aop.support.NameMatchMethodPointcutAdvisor;
public class App {
public static void main(String[] args) {
final MethodBeforeAdvice advice = new MethodBeforeAdviceBarImpl();
final NameMatchMethodPointcutAdvisor nameMatchMethodPointcutAdvisor = new NameMatchMethodPointcutAdvisor();
nameMatchMethodPointcutAdvisor.setMappedName("foo");
nameMatchMethodPointcutAdvisor.setAdvice(advice);
final ProxyFactory proxyFactory = new ProxyFactory();
proxyFactory.addAdvisor(nameMatchMethodPointcutAdvisor);
final Foo foo = new FooImpl();
proxyFactory.setTarget(foo);
final Foo fooProxy = (Foo) proxyFactory.getProxy();
fooProxy.foo();
fooProxy.baz();
}
}
Running the main method in App.java will output:
Bar!
Foo!
Baz!
So as you see here NameMatchMethodPointcutAdvisor is an Advisor and it consists of a mappedName, which is the pointcut and the Advice itself, which in this case is MethodBeforeAdvice.
And in Aspect Oriented Programming Jargon, an Aspect is Advice + Pointcut, so there you go.. An Advisor seems to be an Aspect after all..
Advice is the way you take an action on your Pointcut. You can use before, after or even around advice to apply any action you defined. Talking about Spring Aspect, it is only a class which is a high level and merge two concepts : joinpoint and advice. It can be done through XML based blueprint, or programmatically. Also you should specify your point where you want to plug in an aspect, it is done by using Joinpoint.
Also Spring Aspects and Advice aren't substitute for each other, because Aspects is only merger for joinpoint and advice.
I am familiar with Spring AOP, although not having much hands on experience on that.
My question is, if I want to have some AOP functionality for some methods of a class, not all, then can it be possible with single pointcut. Say, I have four methods save1,save2,get1 and get2 in my class and I want to apply AOP on save1 and save2 only, then in that case how can I create a single pointcut for that? How will my pointcut expression looks like? OR Is it even possible?
There are many ways to do it(with wildcard expression, with aspectJ annotation, ..)
i will give an example with aspectJ
class MyClass{
#MyPoint
public void save1(){
}
#MyPoint
public void save2(){
}
public void save3(){
}
public void save4(){
}
}
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface MyPoint {
}
#Aspect
#Component
public class MyAspect {
#Before("#annotation(com.xyz.MyPoint)")
public void before(JoinPoint joinPoint) throws Throwable {
//do here what u want
}
}
So you are all set, as long as you marked #Mypoint annotation, spring will call before aspect for this method, make sure spring is managing this method and object, not you. include aspectJ in your classpath
You need to specify a pointcut expression to select the methods with to apply your advice.
See 7.2.3 Declaring a pointcut in the Spring Documentation and use the execution joinpoint designator to select the methods.
Having a pointcut expression like so should do the trick
**execution(* save*(..))**
See here for more information
You can use or and and with pointcut expressions:
execution(* my.Class.myMethod(..)) or execution(* my.Class.myOtherMethod(..))