I'm currently migrating one of my projects form "self configured spring" to spring boot. while most of the stuff is already working I have a problem with a #Transactional method where when it is called the context is not present as set before due to a call to the "target" instance instead of the "proxy" instance (I'll try to elaborate below).
First a stripped down view of my class hierarchy:
#Entity
public class Config {
// fields and stuff
}
public interface Exporter {
int startExport() throws ExporterException;
void setConfig(Config config);
}
public abstract class ExporterImpl implements Exporter {
protected Config config;
#Override
public final void setConfig(Config config) {
this.config = config;
// this.config is a valid config instance here
}
#Override
#Transactional(readOnly = true)
public int startExport() throws ExporterException {
// this.config is NULL here
}
// other methods including abstract one for subclass
}
#Scope("prototype")
#Service("cars2Exporter")
public class Cars2ExporterImpl extends ExporterImpl {
// override abstract methods and some other
// not touching startExport()
}
// there are other implementations of ExporterImpl too
// in all implementations the problem occurs
the calling code is like this:
#Inject
private Provider<Exporter> cars2Exporter;
public void scheduleExport(Config config) {
Exporter exporter = cars2Exporter.get();
exporter.setConfig(config);
exporter.startExport();
// actually I'm wrapping it here in a class implementing runnable
// and put it in the queue of a `TaskExecutor` but the issue happens
// on direct call too. :(
}
What exactly is the issue?
In the call to startExport() the field config of ExporterImpl is null although it has been set right before.
What I found so far:
With a breakpoint at exporter.startExport(); I checked the id of the exporter instance shown by eclipse debugger. In the debbug round while writing this post it is 16585. Continuing execution into the call/first line of startExport() where i checked the id again (of this this time) expecting it to be the same but realizing that it is not. It is 16606 here... so the call to startExport() is done on another instance of the class... in a previous debug round i checked to wich instance/id the call to setConfig() is going... to the first on (16585 in this case). This explains why the config field is null in the 16606 instance.
To understand what happens between the line where i call exporter.startExport(); and the actuall first line of startExport() i clicked into the steps between those both lines in eclipse debugger.
There i came to line 655 in CglibAopProxy that looks like this:
retVal = new CglibMethodInvocation(proxy, target, method, args, targetClass, chain, methodProxy).proceed();
checking the arguments here i found that proxy is the instance with id 16585 and target the one with 16606.
unfortunately I'm not that deep into springs aop stuff to know if that is how it should be...
I just wonder why there are two instances that get called on diffrent methods. the call to setConfig() goes to the proxy instance and the call do startExport() reaches the target instance and thus does not have access to the config previously set...
As mentioned the project has been migrated to spring boot but we where before already using the Athens-RELEASE version of spring platform bom. From what i can tell there where no special AOP configurations before the migration and no explicitly set values after the migration.
To get this problem fixed (or at least somehow working) i already tried multiple things:
remove #Scope from the sub class
move #Transactional from method level to class
override startExport() in subclass and put #Transactional here
add #EnableAspectJAutoProxy to application class (i wasn't even able to log in - no error message)
set spring.aop.proxy-target-class to true
the above in diffrent combinations...
Currently I'm out of clues on how to get this back working...
Thanks in advance
*hopes someone can help*
Spring Boot tries to create a cglib proxy, which is a class based proxy, before you probably had an interface based (JDK Dynamic Proxy).
Due to this a subclass of your Cars2ExporterImpl is created and all methods are overridden and the advices will be applied. However as your setConfig method is final that cannot be overridden and as a result that method will be actually called on the proxy instead on the proxied instance.
So either remove the final keyword so that CgLib proxy can be created or explicitly disable class based proxies for transactions. Add #EnableTransationManagement(proxy-target-class=false) should also do the trick. Unless there is something else triggering class based proxies that is.
Related
I've been blown away by something and my sleep is nowhere after finding out that my code is working from last 2 years when clearly it shouldn't!
So it's a Spring boot application. The application has some controllers. One of the classes I'm using extends WebServiceGatewaySupport, so requires that I #Autowire this. The class name is AClient. Now, this class needs 4 properties to work. Three of them are coming from properties file, e.g.
#Value("${a.userName}")String userName;
So three properties are obviously picked from application.properties file. Now the fourth property had me blown away. It is NOT being fetched as rest three, but is being copied from constructor parameter. So we have only one constructor in the class:
public AClient(String d){
this.d=d;
}
Now this property is being used in one of the methods this class has
public String getSomeData(){
// This method gets Data based on property d
}
And interestingly and surprisingly, this property's value is present everytime this bean is accessed! The bean is #Autowired at one place only, inside the Controller class. I haven't marked the property value to be fetched from application.properties file. I haven't provided this class any clue where to get the value of d from. I haven't provided any public methods for other classes to set this value. Yet the code is working and I can even place a debug pointer in method getSomeData() and see that the value of d is present!
Now I understand I might be missing something obvious, but what? Is there a way I can get into Spring container when #Autowired objects are being instantiated and debug from that point to see where this value is coming from? I've checked the code multiple times. Run hundreds of query on Google to find something like Spring boot does some magic stuff to map the missing String properties. But the variable name in properties file is different from what it is in AClient class. So how can it even map? This is truly killing me now!
Addition:
Less relevant, but the code is being accessed in a standard way:
#Autowired
AClient aClient;
public someOtherMethod(){
aClient.getSomeData();
}
So when I place a debugger on first line of someOtherMethod() and hover over aClient, it shows variable d value populated, same as in application.properties file!
Edit:Here's what I missed:
#Configuration
public class someConfig{
#Bean
public AClient aClient(){
// Someone else fetched property from application.properties file, created an object of AClient class using argument constructor and returned that object here. So now Spring is using #Autowire reference for this object I guess
}
}
So basically, your #Configuration class looks similar to this?
#Configuration
public class SomeConfig {
#Value("${a-client.important-value-d}")
private String importantValueDFromApplicationProperties;
#Bean
public AClient aClient() {
return new AClient(importantValueDFromApplicationProperties);
}
}
If yes, then Spring will the aClient for every #Autowired requesting it. Therefore the value of importantValueDFromApplicationProperties will be present in this certain instance.
Another note:
I would recommend using Spring Boot's #ConfigurationProperties instead of #Value. Take a look.
The problem is simple
#Around("execution(* package.*Repository.save(..))")
public Object saveInterupt(ProceedingJoinPoint joinPoint) throws Throwable {
// This gets called whenever repository save is called
}
#Around("execution(* package.*Repository.findAll(..))")
public Object findInterupt(ProceedingJoinPoint joinPoint) throws Throwable {
// This IS NOT GETTING called whenever repository findAll is called
}
Breaking head here!
Edit: A small break through. I printed the target , it returns SimpleJpaRepository instead of the Actual repository.
Assuming the repository is of the following construct
public interface JpaEmployeeRepository extends CrudRepository<JpaEmployee, Long> {..}
following pointcut works on both the cases
#Around("execution(* org..*Repository.save(..))")
and
#Around("execution(* org..*Repository.findAll(..))")
If I understand the question correctly the requirement is to intercept on the execution of a particular method within a specific package.If yes, More details on the same can be read here.
#AspectJ pointcut for all methods inside package
Are you using Spring-BOOT ? Aspecting in Spring-BOOT only calls the advice on a single method in the class the first time it is called. If you want Spring-BOOT to respect your #Around advice in all cases for multiple methods in any given class -- you need to access the class as a bean (SB's #Bean)...
Spring-BOOT's AOP is not 100% the same as aspecting using AspectJ -- the main difference is AspectJ modifies the byte-code where Spring uses a dynamic proxy.
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)
This is the code I have:
#Cacheable(value = "configurationCache", key = "#myFile.lastModified()")
private Object foo(File myFile) throws IOException {
System.out.println(myFile.lastModified());
try {
Thread.sleep(6000);
} catch (InterruptedException ignored) {
}
final Object foo = new SomeObjectFromFile(myFile);
return foo;
}
I call this method twice passing file objects that have the same lastmodified value but caching does not work, the method will wait for 6 seconds.
Here is the output I am getting:
1456298573000
1456298573000
What am I doing wrong?
key = "#myFile.lastModified"
did not work either..
I am sure my configuration with ehcache is fine.
Juliens answer is probably the right one assuming you do not use aspectj. its not alone invoking a public method, but invoking a public method of an object where spring had the chance to wrap it's proxies around. So make sure you are injecting the service that you want to have enhanced with cacheable support.
For example
#Service
public SomeService {
#Autowired
private CacheEnhancedService css;
public void doSomething() {
css.getConfig(new File("./file"));
}
}
#Service
public CacheEnhancedService {
#Cacheable(value = "configurationCache", key = "#myFile.lastModified()")
public Object getConfig(File myFile) {
...
}
}
}
The issue lies with the fact that your method is private.
As mentioned in the documentation of the Spring Framework:
Method visibility and cache annotations
When using proxies, you should apply the cache annotations only to
methods with public visibility. If you do annotate protected, private
or package-visible methods with these annotations, no error is raised,
but the annotated method does not exhibit the configured caching
settings. Consider the use of AspectJ (see below) if you need to
annotate non-public methods as it changes the bytecode itself.
[...]
In proxy mode (which is the default), only external method calls
coming in through the proxy are intercepted. This means that
self-invocation, in effect, a method within the target object calling
another method of the target object, will not lead to an actual
caching at runtime even if the invoked method is marked with
#Cacheable - considering using the aspectj mode in this case. Also,
the proxy must be fully initialized to provide the expected behaviour
so you should not rely on this feature in your initialization code,
i.e. #PostConstruct.
You should either switch to a public method and make and external call or user AspectJ.
When I call fooMethod, I want to process first class annotation (with First.class - in my project this checks if user is logged) and then method annotation (with Second.class - in my project this checks if uses has desired rights to access this specific method. So I need to ensure user is logged first). Is there a way to do that?
#With(First.class)
public class Foo{
#With(Second.class)
public static void fooMethod(){
}
}
Also I wonder why custum action ignores annotation. Code below doesn't process anotation #With(First.class).
public class Foo2 extends Action<CustomAnnotation> {
#Override
#With(First.class)
public Promise<Result> call(Http.Context context) throws Throwable {
return delegate.call(context);
}
}
}
Similar unanswered question: Java + Play Framework 2 with nested action compositions in the same class
In Play 2.4 there appears to be an option for this from the docs here:
Note: If you want the action composition annotation(s) put on a
Controller class to be executed before the one(s) put on action
methods set play.http.actionComposition.controllerAnnotationsFirst =
true in application.conf. However, be aware that if you use a third
party module in your project it may rely on a certain execution order
of its annotations.
The note is only present in the 2.4 docs, so presumably it doesn't work in previous versions.
It looks like at least in Java 8 you have an order in annotation of the same kind: Java Annotations Reflection Ordering.