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.
Related
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.
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.
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.
Is there a way to use Guice and AspectJ for situation where i have an aspect which have to use some complex-to-instantiate service in its logic?
For example:
#Aspect
public class SomeAspect {
private final ComplexServiceMangedByGuice complexServiceMangedByGuice;
#Inject
public SomeAspect(ComplexServiceMangedByGuice complexServiceMangedByGuice){
this.complexServiceMangedByGuice = complexServiceMangedByGuice;
}
#AfterThrowing(value = "execution(* *(..))", throwing = "e")
public void afterThrowingException(JoinPoint joinPoint, Throwable e){
complexServiceMangedByGuice.doSomething(e);
}
}
If i try having it like in the example (with aspect constructor), my aspect will not be called. If i try injecting field (without aspect constructor defined), aspect will be called but field complexServiceMangedByGuice won't be set.
One solution i have found is to get instance in advice method body, so an aspect would look like this:
#Aspect
public class SomeAspect {
private static ComplexServiceManagedByGuice complexServiceManagedByGuice;
#AfterThrowing(value = "execution(* *(..))", throwing = "e")
public void afterThrowingException(JoinPoint joinPoint, Throwable e){
if(complexServiceManagedByGuice == null){
Injector injector = Guice.createInjector(new ModuleWithComplexService());
complexServiceMangedByGuice = injector.getInstance(ComlexServiceManagedByGuice.class);
}
complexServiceMangedByGuice.doSomething(e);
}
}
But that has some undesirable overhead.
You can annotate fields of your aspect class like so:
#Inject
SomeDependency someDependency
Then ask Guice to inject dependencies into your aspect class by writing this in your Guice module's configure() method:
requestInjection(Aspects.aspectOf(SomeAspect.class));
The docs for requestInjection say:
Upon successful creation, the Injector will inject instance fields and methods of the given object
Source: https://github.com/jponge/guice-aspectj-sample
This is something I have wrestled with, and I don't think there is a good answer.
The two libraries basically work against each other: The AspectJ aspects are essentially static, and Guice abhors making anything injectable be static.
I think your options are:
Use Guice AOP - Clean, but limited compared to AspectJ (can only weave injected classes)
Put the injector into a static "global" reference so the aspect can access it. (Yuck.)
Use some sort of thread-context (ultimately a thread-local) to communicate the injector to the aspect (Also yuck - though maybe less so)
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(..))