Please explain, why self invocation on proxy performed on target but not proxy? If that made on purpose, then why? If proxies created by subclassing, it's possible to have some code executed before each method call, even on self invocation. I tried, and I have proxy on self invocation
public class DummyPrinter {
public void print1() {
System.out.println("print1");
}
public void print2() {
System.out.println("print2");
}
public void printBoth() {
print1();
print2();
}
}
public class PrinterProxy extends DummyPrinter {
@Override
public void print1() {
System.out.println("Before print1");
super.print1();
}
@Override
public void print2() {
System.out.println("Before print2");
super.print2();
}
@Override
public void printBoth() {
System.out.println("Before print both");
super.printBoth();
}
}
public class Main {
public static void main(String[] args) {
DummyPrinter p = new PrinterProxy();
p.printBoth();
}
}
Output:
Before print both
Before print1
print1
Before print2
print2
Here each method called on proxy. Why in documentation mentioned that AspectJ should be used in case of self invocation?
Please read this chapter in the Spring manual, then you will understand. Even the term "self-invocation" is used there. If you still do not understand, feel free to ask follow-up questions, as long as they are in context.
Update: Okay, now after we have established that you really read that chapter and after re-reading your question and analysing your code I see that the question is actually quite profound (I even upvoted it) and worth answering in more detail.
Your misunderstanding is about how dynamic proxies work because they do not work as in your sample code. Let me add the object ID (hash code) to the log output for illustration to your own code:
package de.scrum_master.app;
public class DummyPrinter {
public void print1() {
System.out.println(this + " print1");
}
public void print2() {
System.out.println(this + " print2");
}
public void printBoth() {
print1();
print2();
}
}
package de.scrum_master.app;
public class PseudoPrinterProxy extends DummyPrinter {
@Override
public void print1() {
System.out.println(this + " Before print1");
super.print1();
}
@Override
public void print2() {
System.out.println(this + " Before print2");
super.print2();
}
@Override
public void printBoth() {
System.out.println(this + " Before print both");
super.printBoth();
}
public static void main(String[] args) {
new PseudoPrinterProxy().printBoth();
}
}
Console log:
de.scrum_master.app.PseudoPrinterProxy@59f95c5d Before print both
de.scrum_master.app.PseudoPrinterProxy@59f95c5d Before print1
de.scrum_master.app.PseudoPrinterProxy@59f95c5d print1
de.scrum_master.app.PseudoPrinterProxy@59f95c5d Before print2
de.scrum_master.app.PseudoPrinterProxy@59f95c5d print2
See? There is always the same object ID, which is no surprise. Self-invocation for your "proxy" (which is not really a proxy but a statically compiled subclass) works due to polymorphism. This is taken care of by the Java compiler.
Now please remember we are talking about dynamic proxies here, i.e. subclasses and objects created during runtime:
I want to illustrate it like this:
package de.scrum_master.app;
public class DelegatingPrinterProxy extends DummyPrinter {
DummyPrinter delegate;
public DelegatingPrinterProxy(DummyPrinter delegate) {
this.delegate = delegate;
}
@Override
public void print1() {
System.out.println(this + " Before print1");
delegate.print1();
}
@Override
public void print2() {
System.out.println(this + " Before print2");
delegate.print2();
}
@Override
public void printBoth() {
System.out.println(this + " Before print both");
delegate.printBoth();
}
public static void main(String[] args) {
new DelegatingPrinterProxy(new DummyPrinter()).printBoth();
}
}
See the difference? Consequently the console log changes to:
de.scrum_master.app.DelegatingPrinterProxy@59f95c5d Before print both
de.scrum_master.app.DummyPrinter@5c8da962 print1
de.scrum_master.app.DummyPrinter@5c8da962 print2
This is the behaviour you see with Spring AOP or other parts of Spring using dynamic proxies or even non-Spring applications using JDK or CGLIB proxies in general.
Is this a feature or a limitation? I as an AspectJ (not Spring AOP) user think it is a limitation. Maybe someone else might think it is a feature because due to the way proxy usage is implemented in Spring you can in principle (un-)register aspect advices or interceptors dynamically during runtime, i.e. you have one proxy per original object (delegate), but for each proxy there is a dynamic list of interceptors called before and/or after calling the delegate's original method. This can be a nice thing in very dynamic environments. I have no idea how often you might want to use that. But in AspectJ you also have the if()
pointcut designator with which you can determine during runtime whether to apply certain advices (AOP language for interceptors) or not.
What you can do in order to solve the problem is:
Switch to native AspectJ, using load-time weaving as described in the Spring manual. Alternatively, you can also use compile-time weaving, e.g. via AspectJ Maven plugin.
If you want to stick with Spring AOP, you need to make your bean proxy-aware, i.e. indirectly also AOP-aware, which is less than ideal from a design point of view. I do not recommend it, but it is easy enough to implement: Simply self-inject a reference to the component, e.g. @Autowired MyComponent INSTANCE
and then always call methods using that bean instance: INSTANCE.internalMethod()
. This way, all calls will go through proxies and Spring AOP aspects get triggered.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With