Scan the QR code at the end of the article or search the wechat public account Xiao Li Budu, you can follow the wechat public account, get more Java related content.
1. Learn with questions
Interviews often ask what are the two proxy approaches to Spring? JDK dynamic proxy and CGLIB dynamic proxy.
What is the difference between these two agents? The JDK dynamic proxy classes are implemented through interfaces, and the CGLIB dynamic proxy is implemented through subclasses.
How much do you know about JDK dynamic proxies? Have you looked at the class file of the proxy object? Can you answer the following two questions about the JDK dynamic proxy?
- Question 1: Why are JDK dynamic proxies implemented based on interfaces? Rather than implement it based on inheritance, right?
- Question 2: In the JDK dynamic proxy, does a target object call another method of its own through the proxy object?
Xiao Li takes you to a more in-depth understanding of the JDK dynamic proxy.
2. JDK dynamic proxy writing
- JDK dynamic proxies require these parts: interfaces, implementation classes, and proxy objects.
- Proxy objects need to inherit from InvocationHandler, and the Invoke method of InvocationHandler is called when a proxy class calls a method.
- Proxy is the parent of all Proxy classes and provides a static method, newProxyInstance, to dynamically create Proxy objects.
public interface IBuyService {
void buyItem(int userId);
void refund(int nums);
}
Copy the code
@Service
public class BuyServiceImpl implements IBuyService {
@Override
public void buyItem(int userId) {
System.out.println("Xiao Li is not bald to buy things! Xiao Li budu's ID is:" + userId);
}
@Override
public void refund(int nums) {
System.out.println("The product is past the expiration date and needs a refund. Amount of refund:"+ nums); }}Copy the code
public class JdkProxy implements InvocationHandler {
private Object target;
public JdkProxy(Object target) {
this.target = target;
}
// Method enhancement
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before(args);
Object result = method.invoke(target,args);
after(args);
return result;
}
private void after(Object result) { System.out.println("Execute !!!! after calling method" ); }
private void before(Object[] args) { System.out.println("Execute !!!! before calling method" ); }
// Get the proxy object
public <T> T getProxy(a){
return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(),
target.getClass().getInterfaces(),this); }}Copy the code
public class JdkProxyMain {
public static void main(String[] args) {
// Indicate that the target target is BuyServiceImpl
JdkProxy proxy = new JdkProxy(new BuyServiceImpl());
// Get the proxy object instance
IBuyService buyItem = proxy.getProxy();
// Call the method
buyItem.buyItem(12345); }}Copy the code
Viewing the Running result
Execute !!!! before calling the method Xiao Li is not bald to buy things! Xiao Li budu's ID is: 12345 after calling the method, execute !!!!Copy the code
We completed the enhancements to the target method and began a more comprehensive analysis of the proxy object.
3. Analyze the proxy object and answer questions
The premise of profiling proxy objects is that there are proxy objects, dynamic proxy objects are created at run time, we can not analyze through the way of breaking points. But we can analyze it by decompiling.class files. How do I get the.class file?
By adding the: in the code System. The getProperties () put (” sun. Misc. ProxyGenerator. SaveGeneratedFiles “, “true”), can realize dynamic proxy object class files written to the disk. The code is as follows:
public class JdkProxyMain {
public static void main(String[] args) {
// Write the class file of the proxy object to disk
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles"."true");
// Indicate that the target target is BuyServiceImpl
JdkProxy proxy = new JdkProxy(new BuyServiceImpl());
// Get the proxy object instance
IBuyService buyItem = proxy.getProxy();
// Call the method
buyItem.buyItem(12345); }}Copy the code
There is an additional $proxy0.class file in the root directory of the project
Take a look at the contents of this file
public final class $Proxy0 extends Proxy implements IBuyService {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m4;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
try {
return (Boolean)super.h.invoke(this, m1, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw newUndeclaredThrowableException(var4); }}public final void buyItem(int var1) throws {
try {
super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw newUndeclaredThrowableException(var4); }}public final String toString(a) throws {
try {
return (String)super.h.invoke(this, m2, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw newUndeclaredThrowableException(var3); }}public final void refund(int var1) throws {
try {
super.h.invoke(this, m4, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw newUndeclaredThrowableException(var4); }}public final int hashCode(a) throws {
try {
return (Integer)super.h.invoke(this, m0, (Object[])null);
} catch (RuntimeException | Error var2) {
throw var2;
} catch (Throwable var3) {
throw newUndeclaredThrowableException(var3); }}static {
try {
m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
m3 = Class.forName("com.example.springtest.service.IBuyService").getMethod("buyItem", Integer.TYPE);
m2 = Class.forName("java.lang.Object").getMethod("toString");
m4 = Class.forName("com.example.springtest.service.IBuyService").getMethod("refund", Integer.TYPE);
m0 = Class.forName("java.lang.Object").getMethod("hashCode");
} catch (NoSuchMethodException var2) {
throw new NoSuchMethodError(var2.getMessage());
} catch (ClassNotFoundException var3) {
throw newNoClassDefFoundError(var3.getMessage()); }}}Copy the code
The dynamic Proxy object $Proxy0 inherits the Proxy class and implements the IBuyService interface. This provides the answer to question 1: Dynamic Proxy objects inherit from Proxy objects by default, and Java does not support multiple inheritance, so JDK dynamic proxies are implemented based on interfaces.
$Proxy0 overrides the methods of the IBuyService interface, as well as the methods of Object. In the overridden method, the super.h.ivoke method is called uniformly. Super stands for Proxy, h stands for InvocationHandler, in this case JdkProxy. So the INVOKE method of JdkProxy is invoked here.
So each time the buyItem method is called, !!!! is printed before the method is called .
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
before(args);
// Call the method through reflection
Object result = method.invoke(target,args);
after(args);
return result;
}
private void after(Object result) { System.out.println("Execute !!!! after calling method" ); }
private void before(Object[] args) { System.out.println("Execute !!!! before calling method" ); }
Copy the code
We haven’t solved problem two yet, so let’s move on
@Service
public class BuyServiceImpl implements IBuyService {
@Override
public void buyItem(int userId) {
System.out.println("Xiao Li is not bald to buy things! Xiao Li budu's ID is:" + userId);
refund(100);
}
@Override
public void refund(int nums) {
System.out.println("The product is past the expiration date and needs a refund. Amount of refund:"+ nums); }}Copy the code
In this code, we call the refund method inside of buyItem. Does this internal call go through the proxy object? Take a look at the result:
Execute !!!! before calling the method Xiao Li is not bald to buy things! Xiao Li Budu's ID is:12345The product has expired the shelf life and needs a refund. The refund quantity:100After invoking the method, execute !!!!Copy the code
It is true that there is no proxy object, in fact we expect the following result
Execute !!!! before calling the method Xiao Li is not bald to buy things! Xiao Li budu's ID is: 12345 execute !!!! before calling the method The product has expired the warranty period and needs a refund. The amount of refund: 100 Execute !!!! after calling the method After invoking the method, execute !!!!Copy the code
So why the difference?
Because an internal call to the refund method is equivalent to this.refund (100), and this refers to a BuyServiceImpl object, not a proxy object, the refund method is not enhanced.
4. Summarize and extend
-
This article understands the use of JDK dynamic proxy, by analyzing the JDK dynamic proxy generated object class file, to solve two problems:
- Question 1: Why are JDK dynamic proxies implemented based on interfaces? Rather than implement it based on inheritance, right?
- answer: Because objects generated by JDK dynamic proxies are inherited by default
Proxy
Java does not support multiple inheritance, so JDK dynamic proxies are implemented based on interfaces. - Question 2: In the JDK dynamic proxy, does a target object call another method of its own through the proxy object?
- Solution: The object used by an internally called method is the target object itself, and the called method does not pass through the proxy object.
-
We know that JDK dynamic proxy internal calls do not use proxy objects. Is it clear why annotations such as @transactional and @async don’t work?
- Since annotations such as @Transactional and @async are implemented using Spring AOP, if a dynamic proxy uses JDK dynamic proxies, then other methods with the annotations are called inside the method. The annotations are invalid because they are not called ona dynamic proxy object.
-
These are the disadvantages of JDK dynamic proxies. How can Spring avoid them? Is another dynamic proxy: the CGLIB dynamic proxy, which I will examine in the next article.
Reference 5.
- Juejin. Cn/post / 684490…
- Blog.csdn.net/varyall/art…
6. I guess you like it
-
JSON learning and using
-
This is enough for learning reflexes
-
Concurrent programming learning (I) Java memory model
Scan the qr code below to follow the wechat public account Xiao Li Budu, together with efficient learning Java.