@Transactional @Async and other annotations do not work
Before, many people have encountered some situations where the annotation does not work when using @Transactional, @Async and other annotations in Spring.
Why do these situations occur? Because the functions of these annotations are actually implemented by Spring AOP, and their implementation principles are implemented through proxy.
JDK dynamic proxy
Let’s understand the basic principles of JDK dynamic proxy with a simple example:
//Target class interface public interface JDKProxyTestService { void run();}//Target class public class JDKProxyTestServiceImpl implements JDKProxyTestService { public void run(){ System.out.println("do something..."); }}//Proxy class public class TestJDKProxy implements InvocationHandler { private Object targetObject; //Proxy target object//Construct the proxy object public Object newProxy(Object targetObject) { this.targetObject = targetObject; return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(), targetObject.getClass().getInterfaces(), this); } //Use reflection to perform logical enhancement on the original logic public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //Simulate transaction start assumeBeginTransaction(); //Original execution logic Object ret = method.invoke(targetObject, args); //Mock transaction submission assumeCommitTransaction(); return return; } private void assumeBeginTransaction() { System.out.println("Mock transaction start..."); } private void assumeCommitTransaction() { System.out.println("Mock transaction submission..."); }}//Test public class Test { public static void main(String[] args) { TestJDKProxy jdkProxy = new TestJDKProxy(); JDKProxyTestService proxy = (JDKProxyTestService) jdkProxy.newProxy(new JDKProxyTestServiceImpl()); proxy.run(); }}The above example should be able to clearly explain the principle of JDK dynamic proxy. It uses the reflection mechanism to generate an anonymous class that implements the proxy interface, and calls InvokeHandler to handle it before calling the specific method. When we call a method through a proxy class object, we will actually call its invoke method first, and then call the original method. In this way, we can add processing logic uniformly before and after the original method logic.
Spring also has a dynamic proxy method that is CGLIB dynamic proxy. It loads the class file of the proxy object class and processes it by modifying its bytecode to generate subclasses. Although the handling methods are different, the agency's ideas are consistent.
If the target object being proxyed implements an interface, Spring will use JDK dynamic proxy by default. All interfaces implemented by this target type will be proxyed. If the target object does not implement any interface, a CGLIB proxy is created.
Spring AOP annotation failure and resolution
Based on the above analysis of the dynamic proxy principle, let’s look at the following two common problems:
In the same class, method A calls method B (annotated on method B), and the annotation is invalid.
For all Spring AOP annotations, if Spring finds such annotations when scanning beans, it will dynamically construct a proxy object.
This annotation is valid if you want to directly call the A method with annotation through an object of class X. Because at this time, Spring will determine that there is an AOP annotation on the method you are about to call, and then it will use the proxy object of class X to call method A.
But suppose that method A in class X will call method B with annotation, and you still want to call method A through object of class X, then the annotation on method B is invalid. Because Spring determines that the A you call has no annotation, the original object is still used rather than the proxy object. When A calls B next, the annotation of method B in the original object is of course invalid.
Solution:
The easiest way is of course to make methods A and B have no dependencies and can directly call method B through object of class X.
But many times, our logic may not be well written in this way, so there is another method: find a way to manually get the proxy object.
The AopContext class has a currentProxy() method that can directly get the proxy object of the current class. Then the above example can be solved like this:
// Call method B inside method A// 1. Call B directly, the annotation is invalid. B()// 2. Get the proxy class object and call B. ((X)AopContext.currentProxy()).B()
The @Autowired object is null in the AOP annotation method
In previous use, in the annotation method, when using other injected objects, it was found that the object was not injected, and it was null.
Finally, it was found that the reason for this was because the method was private. Because Spring uses JDK dynamic proxy or CGLIB dynamic proxy, one is a class that implements the interface and the other is implemented through subclasses. Neither the interface nor the parent class, the private method can be present, otherwise neither the subclass nor the implementation class can be overridden.
If the method is private, then this method cannot be found in the proxy process, causing problems in the creation of proxy objects and causing some objects not to be injected.
So if the method needs to use AOP annotations, set it to a non-private method.
The above is all the content of this article. I hope it will be helpful to everyone's learning and I hope everyone will support Wulin.com more.