Extending Spring's pointcut-definig expression language - java

I am currently experimenting with Spring and its AOP features.
What I would like to know is if it is possible to extend the expression language of pointcut definition somehow...
I am quite familiar with the standard behaviour and usage of pointcut designators Spring provides but for my needs I would like to implement my own one.
Say we have a method in our aspect:
#Aspect
public class AspectClass {
#Before("execution(* *.get*(..)) && #annotation(someAnnotation)"
public void doStuff(ProceedingJoinPoint pjp, SomeAnnotation someAnnotation) {
System.out.println("Doing stuff!");
}
}
Now what I would like is something like this:
...
#Before("execution(* *.get*(..)) && #myAnnotation(someAnnotation)"
public void doStuff(ProceedingJoinPoint pjp, SomeAnnotation someAnnotation){
...
Where the myAnnotation(..) token is my custom extension of the expression language.
When I delved into Spring implementation I found where the parser resides and the fact it is hard-coded into AspectJExpressionPointcut implementation. Hence providing a custom implementation of said parser and sticking it someplace in some bean initialization routine seems like a no-no.
Exemplary usage of this extension would be for example matching a method by it's annotation, disregarding proximity of this annotation in the object hierarchy. So it would match a method that is overriden and whose annotation is defined on the parent's implementation.
public abstract class Superclass {
#SomeAnnotation
public abstract String getValue();
}
public class TheClass extends Superclass {
#Override
public String getValue() { // <- this would get advised by Spring using formerly defined aspect
// return some stuff
}
}
I know that I can reflect the method in the advice and check if the annotation is present on some method of some superclass, but I would like to encapsulate this process and offer it for convenient usage.
Have you guys stumbled upon something like this & can you offer me a solution (if it is possibe) / explanation (if it is not)? :-)
Thanks in advance, Janek.

Related

Spring AOP is powerless for classes with interfaces

I understand that Spring AOP is very limited in its abilities (it can only cut into public methods of classes that are Spring beans, and only when those methods are called from outside the class). But now I've discovered another baffling limitation when interfaces are involved.
Normally, if a class is subclassed, Spring AOP has no problem cutting into all of their methods (even overridden ones):
public class A {
public void methodA() { } //OK, can cut in
}
public class B extends A {
#Override
public void methodA() { } //OK, can cut in
public void methodB() { } //OK, can cut in
}
But when we add an interface into the mix, things get pretty bad for Spring AOP:
public interface I {
public void methodA();
}
public class A implements I {
#Override
public void methodA() { } //OK, can cut in
public void methodB() { } //Fail, cannot see or cut into this method
}
public class B extends A {
#Override
public void methodA() { } //Fail, cannot see or cut into this method
public void methodC() { } //Fail, cannot see or cut into this method
}
First of all, Spring AOP can only cut into methods that are in the interface, anything else - it cannot see. Second, it can only cut into the method that directly implements the interface's method - A.methodA(). It cannot cut into the same method overridden by B.
I am using a generic pointcut expression "execution(* method*(..))" to cut into all possible methods, so it's not an expression issue.
Is there any way around this limitation? Or should I just forget about Spring AOP and use a different approach?
UPDATE:
Ok, I have found the real cause of the problem. I was actually relying on Intellij IDEA's AOP plugin to test this. It's supposed to link the pointcut to all affected methods. But it was using the 'old', dynamic JDK proxy strategy instead of the new, CGLIB strategy. So it wasn't linking it to all methods, but when I actually ran my program, it would cut into all methods correctly.
I'm using Spring Boot 2, which uses the 'new' CGLIB strategy. But on SB1 it might still use the 'old' dynamic JDK proxy strategy, so it might still not work there.
Spring will use either dynamic proxy or cglib to implement AOP.
Cglib is picked if there is no interface, then it will effectively create a subclass of the target class, and override all methods in the target class. With this way all methods could be cut in, except final and static ones.
In case the target class is with interface, then Spring might use a dynamic proxy using one of the interface, and apprantly this will only affect the methods declared in the interface.
Before spring-boot 2.0, dynamic proxy is the default strategy. Now Cglib is the default strategy after spring-boot 2.0.
It seems to me spring probably take the dynamic proxy approach in your case. You could add spring.aop.proxy-target-class: true in your application.yaml to force use Cglib.
In case you still have issue, it's better to post more complete code snippet showing how the mothods are invoked.

Why Spring-proxy uses delegate pattern instead of inheritance+super?

As we all know, the self-invokation of bean's method is not working in Spring without AspectJ.
See this question for example.
I think this is because the Spring-created proxy calls the target object's methods using delagate pattern. Like this:
class MyClass {
#Autowired
private MyClass self; // actually a MyProxy instance
#Transactional // or any other proxy magic
public void myMethod() {}
public void myOtherMethod() {
this.myMethod(); // or self.myMethod() to avoid self-invokation problem
}
}
class MyProxy extends MyClass { // or implements MyInterface if proxyMode is not TARGET_CLASS and MyClass also implements MyInterface
private final MyClass delegate;
#Override
public void myMethod() {
// some proxy magic: caching, transaction management etc
delegate.myMethod();
// some proxy magic: caching, transaction management etc
}
#Override
public void myOtherMethod() {
delegate.myOtherMethod();
}
}
Am I right?
With this code:
public void myOtherMethod() {
this.myMethod();
}
this.myMethod() will bypass the proxy (so all #Transactional or #Cacheable magic) because it is just internal delegate's call... So we should inject a MyClass bean (which is actually is MyProxy instance) inside MyClass and call self.myMethod() instead. It is understandable.
But why the proxy is implemented this way?
Why it is not just extends the target class, overriding all public methods and calling super instead of delegate?
Like this:
class MyProxy extends MyClass {
// private final MyClass delegate; // no delegate
#Override
public void myMethod() {
// some proxy magic: caching, transaction management etc
super.myMethod();
// some proxy magic: caching, transaction management etc
}
#Override
public void myOtherMethod() {
super.myOtherMethod();
}
}
It should solve the self-invokation problem, where this.myMethod() bypasses the proxy, because in this case this.myMethod(), invoked from MyClass.myOtherMethod() (we remember that MyClass bean actually is MyProxy instance), will invoke overriden child's method (MyProxy.myMethod()).
So, my main question is why it is not implemented this way?
Your assumption that Spring AOP uses delegation for its proxies is correct. This is also documented.
Using CGLIB, you can theoretically use proxy.invokeSuper() in order to achieve the effect you want, i.e. that self-invocation is registered by the aspect implemented by the proxy's method interceptor (I am using Spring's embedded version of CGLIB here, thus the package names):
package spring.aop;
import org.springframework.cglib.proxy.Enhancer;
import org.springframework.cglib.proxy.MethodInterceptor;
import org.springframework.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
class SampleClass {
public void x() {
System.out.println("x");
y();
}
public void y() {
System.out.println("y");
}
public static void main(String[] args) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(SampleClass.class);
enhancer.setCallback(new MethodInterceptor() {
#Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy)
throws Throwable {
if(method.getDeclaringClass() == Object.class)
return proxy.invokeSuper(obj, args);
System.out.println("Before proxy.invokeSuper " + method.getName());
Object result = proxy.invokeSuper(obj, args);
System.out.println("After proxy.invokeSuper " + method.getName());
return result;
}
});
SampleClass proxy = (SampleClass) enhancer.create();
proxy.x();
}
}
Console log:
Before proxy.invokeSuper x
x
Before proxy.invokeSuper y
y
After proxy.invokeSuper y
After proxy.invokeSuper x
This is exactly what you want. The problem starts, however, when you have several aspects: transactions, logging, whatever else. How do you make sure that they all work together?
Option 1: Each aspect gets its own proxy. This obviously will not work unless you nest the proxies into each other according to aspect precedence. But nesting them into each other means inheritance, i.e. one proxy would have to inherit from the other outside-in. Try proxying a CGLIB proxy, it does not work, you get exceptions. Furthermore, CGLIB proxies are quite expensive and use perm-gen memory, see descriptions in this CGLIB primer.
Option 2: Use composition instead of inheritance. Composition is more flexible. Having one proxy to which you can register aspects as needed solves the inheritance problem, but also means delegation: The proxy registers the aspects and calls their methods during runtime in the right order before/after the actual real object's code is executed (or not, if an #Around advice never calls proceed()). See this example from the Spring manual about manually registering aspects to a proxy:
// create a factory that can generate a proxy for the given target object
AspectJProxyFactory factory = new AspectJProxyFactory(targetObject);
// add an aspect, the class must be an #AspectJ aspect
// you can call this as many times as you need with different aspects
factory.addAspect(SecurityManager.class);
// you can also add existing aspect instances, the type of the object supplied must be an #AspectJ aspect
factory.addAspect(usageTracker);
// now get the proxy object...
MyInterfaceType proxy = factory.getProxy();
As to why the Spring developers chose this approach and whether it might have been possible to use the one-proxy approach but still make sure that self-invocation works like in my little CGLIB sample "logging aspect" above, I can only speculate. You can maybe ask them on the developers mailing list or look into the source code. Maybe the reason was that CGLIB proxies should behave similarly to the default Java dynamic proxies so as to make switching between the two for interface types seamless. Maybe the reason is another one.
I did not mean to be rude in my comments, only straightforward, because your question is really not suited to StackOverflow because it is not a technical problem to which someone can find a solution. It is a historical design question and rather philosophic in nature because with AspectJ a solution to your technical problem (self-invocation) beneath the actual question already exists. But maybe you still want to dive into the Spring source code, change the Spring AOP implementation from delegation to proxy.invokeSuper() and file a pull request. I am not sure such a breaking change would be accepted, though.
In addition, you will not able to use Inheritance + super in the following cases:
What about if the RealSubject is final, so the proxy will can NOT extends it
What about if the Proxy needs to extend something other than the RealSubject
What about if you need to hide some functionality (methods) inside the RealSubject
Prefer Composition over Inheritance (recommended by many developers)

Annotation like #depreciated

I'd like to implement an annotation for methods which will let me know where those annotated methods called, just like official #deprecated annotation.
How can I get the list of all calling methods for a given annotated method?
I think this question may help you:
To find this annotated method (from Arthur Ronald's answer):
use
org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider
API
A component provider that scans the classpath from a base package. It then applies exclude and include filters to the resulting classes
to find candidates.
ClassPathScanningCandidateComponentProvider scanner =
new ClassPathScanningCandidateComponentProvider(<DO_YOU_WANT_TO_USE_DEFALT_FILTER>);
scanner.addIncludeFilter(new AnnotationTypeFilter(<TYPE_YOUR_ANNOTATION_HERE>.class));
for (BeanDefinition bd : scanner.findCandidateComponents(<TYPE_YOUR_BASE_PACKAGE_HERE>))
System.out.println(bd.getBeanClassName());
Or (from Jonathan's answer):
Google reflections:
https://github.com/ronmamo/reflections
Quick review:
Spring solution is the way to go if you're using Spring. Otherwise it's a big dependency.
Using ASM directly is a bit cumbersome.
Using Java Assist directly is clunky too.
Annovention is super lightweight and convenient. No maven integration yet.
Google reflections pulls in Google collections. Indexes everything and then is super fast.
Update: If want to calling methods for a given annotated method, you should use AOP (Aspect Oriented Programming) and add # Around or # Before, for example something like this (I don't check this code):
public class Foo {
#YourAnnotation
public int power(int x, int p) {
return Math.pow(x, p);
}
}
#Aspect
public class MethodLogger {
#Around("execution(* *(..)) && #annotation(YourAnnotation)")
public Object around(ProceedingJoinPoint point) {
Logger.info(
"call by method" + MethodSignature.class.cast(point.getSignature()).getMethod().getName()
);
Object result = point.proceed();
return result;
}
}

How to get annotations of interface or abstract class methods in Java

I have an interface like this:
public interface IFoo{
#AnnotationTest(param="test")
String invoke();
}
and I implement this like this:
public class Foo implements IFoo{
#Override
public String invoke(){
Method method = new Object() {
}.getClass().getEnclosingMethod();
AnnotationTest ann = method.getAnnotation(AnnotationTest.class);
if(ann == null){
System.out.printl("Parent method's annotation is unreachable...")
}
}
}
If it is possible to reach parent's annotation, I want to learn the way of it.
Any help or idea will be appreciated.
You can use Spring AnnotationUtils.findAnnotation to read annotations from interfaces.
Example :
Interface I.java
public interface I {
#SomeAnnotation
void theMethod();
}
Implementing class A.java
public class A implements I {
public void theMethod() {
Method method = new Object() {}.getClass().getEnclosingMethod();
SomeAnnotation ann = AnnotationUtils.findAnnotation(method, AnnotationTest.class);
}
}
It obviously requires to include in your project (and import) Spring framework classes.
There is no direct way to get it. If you really need, you have to manually loop over getInterfaces() to find if any implemented interface has the annotation. If you want to search for (eventually abstract) superclasses and the annotation is not #Inherited, you can again iterate the superclass chain until finding Object (*).
But beware, as following post states, there are good reasons for this not to be directly implemented in Java : Why java classes do not inherit annotations from implemented interfaces?
(*) If the annotation is #Inherited it is automatically searched on superclasses, but not on interfaces.
you can't inherit annotations.
But a framework that uses an annotation can check to see if annotation is present on superclass

Java Inheritance and Wrapping

I have a generated object that I want to:
Preserve existing functionality of without injecting into the constructor and rewriting every method to call injectedObject.sameMethod().
Add additional functionality to that generated object without modifying the generated object.
add additional functionality to.
For example:
public class GeneratedObject {
public String getThis() { ... }
public String getThat() { ... }
}
public interface ObjectWrapper {
String doThisWithThat();
}
public class ObjectWrapperImpl extends GeneratedObject implements ObjectWrapper {
String doThisWithThat() { ... }
}
However, downcasting is not allowed, what is the proper implementation without rewriting a bunch of redundant code just to wrap the object?
I think decorator pattern may help you: "The decorator pattern can be used to extend (decorate) the functionality of a certain object at run-time, independently of other instances of the same class"
Have you tried aspectj? http://www.eclipse.org/aspectj/doc/next/progguide/semantics-declare.html It's a bit complicated but so is your request.
If you can extract an interface from GeneratedObject, then it would be possible to do this using a dynamic proxy. You would make a proxy which implemented the extracted interface and ObjectWrapper, with an invocation handler which passed all calls to methods in the GeneratedObject interface through to the delegate, and sent the doThisWithThat() calls elsewhere.
Proxies aren't pretty, but the ugliness is at least well-localised.

Categories

Resources