It’s really hard to find a 21st internship this year. Dry your tears and keep your head down! By the time you read this, it will have been a long time.
We’ve all used dynamic proxies directly or indirectly, whether it’s the logging framework or the Spring framework, which includes dynamic proxies.
Proxy is a common design pattern whose purpose is to provide other objects with a proxy to control access to an object. There are mainly static proxy and dynamic proxy, static proxy is not described here. Dynamic proxy is a mechanism by which a program dynamically builds proxy objects and invokes proxy methods during runtime.
In this article we will learn how to implement dynamic proxy? What is the difference between JDK Proxy and CGLib?
A dynamic proxy
When it comes to dynamic proxies, I think the first word that comes to mind is reflection, and the common implementation of dynamic proxies is reflection.
Attention! Not all proxies are implemented by reflection. What is reflection mechanism? The ability to access, detect, and modify the state or behavior of a program while it is running. Using reflection, we can call any class object, as well as properties and methods contained in the class object.
More reflection content can be seen in this article => 10,000-word summary of Reflection (Soul of frame)
But there is more to dynamic proxies than reflection. For example, dynamic proxies are well known as CGLib, which is based on ASM (a Java bytecode manipulation framework) rather than reflection. Simply put, dynamic proxy is a mode of behavior, and reflection or ASM is just a means of implementing it.
The differences between JDK Proxy and CGLib are as follows:
- JDKProxy is a Java language with no need to load third-party classes, while CGLib is a third-party tool based on ASM.
- Java provides stable support for JDKProxy and will continue to be upgraded. For example, the JDKProxy performance in Java8 is much improved compared to previous versions.
- JDK Proxy is implemented by interceptor plus reflection;
- JDK Proxy can only delegate classes that inherit interfaces. CGLib does not need an interface. It implements calls by subclasses.
Use of JDK dynamic proxies and CGLib
JDK dynamic proxy implementation
JDK Proxy dynamic Proxy implementation does not need to reference third-party classes, just need to implement the InvocationHandler interface, override the invoke() method, the entire implementation code is as follows:
public interface HelloService {
void sayHello(String name);
void sayHello1(String name);
}
public class HelloServiceImpl implements HelloService {
@Override
public void sayHello(String name) {
System.out.println("Hello" + name);
}
@Override
public void sayHello1(String name) {
System.out.println("Hello"+ name); }}public class HelloServiceInvocationHandler implements InvocationHandler {
private Object target;
public Object bind(Object target){
this.target = target;
return Proxy.newProxyInstance(target.getClass().getClassLoader(),target.getClass().getInterfaces(),this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("############ I am JDK dynamic proxy ##############");
System.out.println("I'm going to say hello.");
Object result = method.invoke(target,args);
System.out.println("I said hello.");
returnresult; }}public class HelloServiceJdkMain {
public static void main(String[] args) {
HelloServiceInvocationHandler helloServiceInvocationHandler = new HelloServiceInvocationHandler();
HelloService proxy = (HelloService) helloServiceInvocationHandler.bind(new HelloServiceImpl());
proxy.sayHello("Zhang");
proxy.sayHello1("Bill"); }}Copy the code
The running results are as follows:The core of JDK Proxy is the Invocation interface, which mainly involves the following two classesInvocationHandler
H andProxy
The Invocation Invocation has only one invoke() method, which looks like this:
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
Copy the code
The first parameter proxy generally refers to the proxy class, method is the proxy method, and args is the parameter array of this method.
Proxy is a dynamic Proxy class
public static Object newProxyInstance(ClassLoader loader, Class
[] interfaces, InvocationHandler h)
Copy the code
Gets a proxy class where loader is the classloader and interfaces are an array of all interfaces owned by the real class. Returns an instance of the proxy class that can be used as the proxied class.
Dynamic proxy steps:
- Create a class that implements the interface InvocationHandler, which must implement the Invoke method
- Create proxied classes and interfaces
- Static method by Proxy
- NewProxyInstance (ClassLoaderloader, Class [] interfaces, InvocationHandler h) to create a proxy
- Invoke methods through proxies
The implementation of additional
To use CGLib, we need to introduce the CGLib framework in our project and add the following configuration to pom.xml:
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>3.2. 5</version>
</dependency>
Copy the code
CGLib implementation code is as follows:
public class HelloServiceMethodInterceptor implements MethodInterceptor {
private Object target;
public HelloServiceMethodInterceptor(Object target) {
this.target = target;
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("I'm a CGLIb dynamic proxy.");
/ * * * here need to pay attention to the difference between the invoke and invokeSuper * suggested to see this article: https://juejin.im/post/5a8f750af265da4e983f2369 * /
methodProxy.invokeSuper(o,objects);
return null; }}public class HelloServiceCglibMain {
public static void main(String[] args) {
// Output the class file generated by cglib
// System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\proxy");
HelloServiceImpl helloService = new HelloServiceImpl();
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(HelloServiceImpl.class);
enhancer.setCallback(new HelloServiceMethodInterceptor(helloService));
helloService = (HelloServiceImpl) enhancer.create();
helloService.sayHello("11"); }}Copy the code
Run screenshot:From the above two simple cases, CGLib and JDK Proxy implementation code is relatively similar, through the implementation of the Proxy interface, and then call a method to complete the dynamic Proxy.
The difference is:
- CGLib initializes the proxied class by setting the proxied object as a subclass of the proxied class through the Enhancer object. Therefore, the proxyed class cannot be modified by the final keyword (the class modified by final cannot be inherited). If it is modified by final, an error will be reported when setting the parent class using Enhancer, and the dynamic proxy will fail to be built.
- JDK dynamic proxy is implemented through the delegate mechanism, that is, the interface of the target class, and then the target class is passed in as a parameter when constructing the dynamic proxy, so that the proxy object holds the target object, and then through the proxy object’s InvocationHandler to achieve the operation of the dynamic proxy.
In general, JDK dynamic proxies use interfaces to implement proxies, and CGLIB dynamic proxies use inheritance to implement proxies.
Dynamic proxy knowledge expansion
What is the difference between dynamic and static proxies? Static proxy is actually a pre-written proxy class, which can be manually written or generated using tools. But its disadvantage is that each business class has to correspond to a proxy class, which is particularly inflexibly and inconvenient, so there is a dynamic proxy. Dynamic proxies are commonly used in RPC framework encapsulation, AOP (aspect oriented programming) implementation (frequent interview questions), JDBC connectivity, etc. The Spring framework uses both JDK Proxy and CGLib as dynamic proxies. Spring uses JDK Proxy when the Bean implements the interface and CGLib when the interface is not implemented.