This question already has answers here:
Why does self-invocation not work for Spring proxies (e.g. with AOP)?
(2 answers)
Closed 2 years ago.
Suppose we have following class
#Service
class MyClass {
public void testA() {
testB();
}
#Transactional
public void testB() { ... }
}
Now, if we invoke myClass.testA(); in test, then #Transactional on testB will not take effect. The reason I think is following.
Cglib will create a proxy bean for MyClass, like this:
Class Cglib$MyClass extends MyClass {
#Override
public void testB() {
// ...do transactional things
super.testB();
}
}
Now we invoke myClass.testA(), which will invoke MyClass.testB() instead of Cglib$MyClass.testB(). So #Transactional is not effective. (Am I right?)
I tried to add #Transactional for both methods (i.e. testA() and testB()). The proxy class should like this.
Class Cglib$MyClass extends MyClass {
#Override
public void testA() {
// ...do transactional things
super.testA();
}
#Override
public void testB() {
// ...do transactional things
super.testB();
}
}
In this case, although we successfully invoke Cglib$MyClass.testA(), it will still goes to MyClass.testB().
So my conclusion is, two methods in same class invoking each other will make aop annotation fail to take effect, unless we use AopContext.currentProxy().
Am I right on above guess? Thanks very much for advice!
It is a well-known and documented (please search for the term "self-invocation") fact that Spring AOP, due to its proxy-based nature, does not and cannot capture internal method calls like this.someMethod(..).
So as you said, you either need to explicitly refer to the exposed proxy object or alternatively switch from Spring AOP to full AspectJ via load-time weaving.
You almost have it right. The proxy looks something more like this:
class Cglib$MyClass extends MyClass {
MyClass delegate;
#Override
public void testB() {
// ...do transactional things
delegate.testB();
}
}
Any call is forwarded by Spring which is why your nested annotations are not activated.
Also, if a virtual method like testA was overridden, Spring could not avoid to invoke the overridden method.
Related
Consider the following scenario: A class called EventHandlerImpl implements an interface called AsyncEventHandler, with a single handle() method marked with the Spring #Async annotation. EventHandlerImpl also extends an abstract superclass called AbstractEventHandler. This abstract superclass implements the plain EventHandler interface which also contains a handle() method, providing an implementation of the method which in turn calls upon another abstract method designed to be implemented by child classes.
public interface EventHandler {
void handle();
}
public interface AsyncEventHandler {
#Async
void handle();
}
#Component
public class EventHandlerImpl extends AbstractEventHandler implements AsyncEventHandler {
#Override
public void wrappedMethod() {
longRunningMethodWhichMayThrowException();
System.out.println("Handled long-running task!");
}
}
public class AbstractEventHandler implements EventHandler {
protected abstract void wrappedMethod();
#Override
public void handle() {
try {
wrappedMethod();
} catch (Exception e) {
System.out.println(e.getMessage());
}
}
}
Then, the required bean is injected in a constructor:
#Autowired
public DependentComponent(AsyncEventHandler asyncEventHandler) {
this.asyncEventHandler = asyncEventHandler;
}
The expected behaviour for the injected bean is asynchronous execution of its handle method as per the AsyncEventHandler interface (this is the entire reason for the existence of this interface).
Example:
System.out.println("Before long-running task execution");
asyncEventHandler.handle();
System.out.println("After long-running task execution");
Expected output:
Before long-running task execution
After long-running task execution
Handled long-running task!
The behaviour seen in reality does not match this. The handle() method is not executed asynchronously by Spring. I've found that having the AsyncEventHandler interface declared as such:
#Async
public interface AsyncEventHandler {
void handle();
}
(annotation on the interface level rather than the method level) makes the bean behave as expected. Digging into the Spring source code responsible for attaching and executing advisors such as the async advisor confirms that Spring is able to find the #Async annotation on the interface, but is unable to do so when it exists on the interface method in this particular scenario.
This raises the question: How is Spring meant to handle searching for annotations when the "same" method signature exists in multiple places in the inheritance hierarchy (with potentially different annotations)?
Is the discrepancy between annotating the method vs the interface a defined behaviour for Spring?
I am trying to use #Async annotation provided by spring. Going through some of the blogs I found there are the following constraints for using it:
It must be applied to public methods only
Self-invocation – calling the async method from within the same class – won’t work
I have a method which is getting called from the same class which I want to mark #Async. Is there any way of achieving it from the same class?
In Spring v4.3+ you can use self injection, and call the method on the self injected reference.
So for example:
#Component
public class SomeClass {
#Autowired
private SomeClass selfInjected;
public void someMethod() {
selfInjected.someOtherMethod();
}
#Async
public void someOtherMethod(){
...;
}
}
Updated as OP is using version before 4.3:
This will work for you.
#Component
public class SomeClass {
#Autowired
private ApplicationContext applicationContext;
private SomeClass selfInjected;
#PostConstruct
private void init() {
selfInjected = applicationContext.getBean(SomeClass.class);
}
}
Or
The other option is to extract the method to separate class and autowire it. I would personally explore this option before doing the above method.
I am using spring aspect -around advice;
For super.method() or inter class method call, I am unable to call my aspect code;
This is my code sample.
//My aspect Class
#Aspect
public class MyAspect {
#Around("execution(* in.test.project.service.myservice.create(..))")
public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
// .
//my logic
// .
}
public interface Myservice{
void create(Myclass entity);
void createObject(Myclass entity);
}
class myserviceImpl implements Myservice{
public void create(Myclass entity) {
// TODO Auto-generated method stub
}
void createObject(Myclass entity){
create(entity);
}
}
class Mycontroller {
void test(Myclass entity) {
Myservice myservice = new myserviceImplimplements();
// calls MyAspect code
myservice.create(entity);
// MyAspect not called
myservice.createObject(entity);
}
}
This a limitation of using Spring AOP without having your code know that it is using Spring AOP (you can view Spring's documentation on how you could do this as well as why you shouldn't on their "Understanding AOP proxies" page). Spring AOP is a proxy-based system and differentiates between the proxy object itself (bound to this) and the target object behind the proxy (bound to target), so you are also limited in what kinds of pointcuts you can apply.
Since the call first goes through the proxy and then on the object, calls made inside of the object on that object (like the call you have above that calls a method inside) are called on the Object itself and not the proxy. The calls that go through the proxy can delegate to relevant interceptors, while calls that are made by the Object itself do not proxy, and thus can not be advised by Spring AOP like this. The link I provided above shows to expose the AopContext, but I would highly recommend against doing that.
Case1
#Transactional
public class UserServiceImpl implements UserService {
...................
public void method1(){
try{
method2();
}catch(Exception e){
}
}
public void method2(){
}
}
Case2
public class UserServiceImpl implements UserService {
...................
public void method1(){
try{
method2();
}catch(Exception e){
}
}
#Transactional
public void method2(){
}
}
In case1 if any exception occurs it rollback is working, but in case 2 it's not working. Is there any performance issues if I follow the case1?
In case 1 #Transactional is applied to every public individual method. Private and Protected methods are Ignored by Spring.
Spring applies the class-level annotation to all public methods of
this class that we did not annotate with #Transactional. However, if
we put the annotation on a private or protected method, Spring will
ignore it without an error.
In case 2 #Transactional is only applied to method2(), not on method1()
Case 1:
- Invoking method1() -> a transaction is started. When method1() calls method2() no new transaction is started, because there is already one
Case 2:
- Invoking method1() -> no transaction is started. When method1() calls method2() NO new transaction is started. This is because #Transactional does not work when calling a method from within the same class. It would work if you would call method2() from another class.
From the spring reference manual:
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 transaction at runtime even if the invoked method is marked with #Transactional. 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.
#Transactionalon a class applies to each method on the service. It is a shortcut. Typically, you can set #Transactional(readOnly = true) on a service class, if you know that all methods will access the repository layer. You can then override the behavior with #Transactional on methods performing changes in your model. Performance issues between 1) and 2) are not known.
Suppose you have the following class:
#Transactional(readOnly = true)
public class DefaultFooService implements FooService {
public Foo getFoo(String fooName) {
// do something
}
// these settings have precedence for this method
#Transactional(readOnly = false, propagation = Propagation.REQUIRES_NEW)
public void updateFoo(Foo foo) {
// do something
}
}
The #Transactional annotation on the class level will be applied to every method in the class.
However, when a method is annotated with #Transactional (like, updateFoo(Foo foo)) this will take precedence over the transactional settings defined at the class level.
More info:
Transaction management in Spring
Quoting from here
The Spring team's recommendation is that you only annotate concrete classes with the #Transactional annotation, as opposed to annotating interfaces.
Since this mechanism is based on proxies, only 'external' method calls coming in through the proxy will be intercepted. This means that 'self-invocation', i.e. a method within the target object calling some other method of the target object, won't lead to an actual transaction at runtime even if the invoked method is marked with #Transactional!
I stumbled upon a case where the AOP proxy created by using #Cacheable breaks the dependency injection in Spring 3.1.1. Here is my scenario:
I have an interface and a class implementing this interface using #Cacheable at the implemented method.
Example interface:
public interface ImgService {
public byte[] getImage(String name);
}
Example implementation:
public class ImgServiceImpl implements ImgService {
#Cacheable(cacheName = "someCache")
public byte[] getImage(String name){//TODO};
protected String someOtherMethod(){//};
}
I also have to JUnit test classes - one which injects the interface and one the implementation:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath*:META-INF/spring.xml" })
public class ImgServiceTest {
#Inject
private ImgService;
}
and
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath*:META-INF/spring.xml" })
public class ImgServiceImplTest {
#Inject
private ImgServiceImpl;
}
Dependency injection for the interface works fine. However, when I get to injecting the implementation in the second test class I get an "Injection of autowired dependencies failed". I was able to debug it and it appears that ClassUtils.isAssignableValue() wrongly compares the desired type to the proxy class. It is called by DefaultListableBeanFactory. What is even stranger is that if I remove the #Cacheable annotation from the implemented method and add it to some other protected/private method, dependency injection works fine again. Is this a bug and what would be the correct approach to handle this situation?
It's not a bug, it's an expected side-effect of using JDK dynamic proxies for AOP implementation.
Since all calls to the cacheable method of ImgServiceImpl should go through the dynamic proxy of type ImgService, there is no way to inject this dependency into a field of type ImgServiceImpl.
When you move #Cacheable to private or protected method, injection works because #Cacheable doesn't take effect in this case - only public methods can be adviced using proxy-based AOP.
So, you should either declare fields to be injected as ImgService, or configure Spring to use target class-based proxies instead using proxy-target-class = "true".
Yet another option is to configure Spring to use AspectJ-based AOP implementation (requires compile-time or load-time weaving).
It's applicable to all AOP-based features provided by Spring (transactions, security, async execution, cache, custom aspects, etc).
See also:
7.6 Proxying mechanisms
OK, so here is the solution I came up finally. I implemented a simple method that attempts to extract the target object from the proxy based on its implementation of the org.springframework.aop.framework.Advised class:
#SuppressWarnings({"unchecked"})
public static <T> T getTargetObject(Object proxy, Class<T> targetClass) {
if (AopUtils.isJdkDynamicProxy(proxy)) {
try {
return (T) ((Advised)proxy).getTargetSource().getTarget();
} catch (Exception e) {
return null;
}
} else {
return (T) proxy;
}
}
My implementation test class now looks like this:
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { "classpath*:META-INF/spring.xml" })
public class ImgServiceImplTest {
#Inject
private ImgService imgService;
private ImgServiceImpl imgServiceImpl;
#PostConstruct
public void setUp() {
imgServiceImpl = getTargetObject(imgService, ImgServiceImpl.class);
}
}