🎓 do your best and listen to fate. The blogger is studying for a master’s degree in Southeast University. She loves fitness and basketball. She is willing to share what she sees and gets related to technology

🎁 This article has been included in “CS-wiki” Gitee official recommendation project, has accumulated 1.5K + STAR, is committed to creating a perfect back-end knowledge system, in the technology on the road less detour, welcome to come to exchange and learn

🍉 If you do not have a good project, you can refer to a project I wrote “open source community system Echo” Gitee official recommendation project, currently has a total of 400+ star, SpringBoot + MyBatis + Redis + Kafka + Elasticsearch + Spring Security +… And provide detailed development documents and supporting tutorials. The public account background reply Echo can get the supporting tutorial, which is still being updated


As mentioned earlier, the dynamic proxy mechanism uses reflection, and AOP in Spring also uses reflection because it uses dynamic proxy. So, what is an agency? What is dynamic proxy? How is reflection used in dynamic proxies? The mind map of the full text is as follows:

1. Conventional coding

Before we learn about agents, let’s review our general coding: All variables of type interface are always transitioned upward and point to an instance.

1) First, define an interface:

public interface SmsService {
    String send(String message);
}
Copy the code

2) Then write its implementation class:

public class SmsServicseImpl implements SmsService {
    public String send(String message) {
        System.out.println("send message:" + message);
        returnmessage; }}Copy the code

3) Finally create an instance of the implementation class, transform it into an interface and call:

SmsService s = new SmsServicseImpl();
s.send("Java");
Copy the code

This is the way we usually write code. The proxy model is very different from this, and see the following.

2. Overview of the proxy pattern

In simple terms, proxy mode is to use proxy objects instead of access to real objects, so that additional operations can be provided to extend the functionality of the target object without modifying the original target object.

There are roughly three roles in the proxy model:

  • Real Subject: Real class, that is, proxy class, delegate class. Used to actually fulfill business service functions;
  • Proxy: Proxy class. The proxy class object does not really implement its business function.
  • Subject: Defines the interface that both the RealSubject and Proxy roles should implement.

In general terms, the main purpose of the proxy mode is to extend the functionality of the target object. For example, you can add additional operations before and after the execution of a method on the target object without modifying the original code of the method. If you have learned Spring AOP, you will be able to understand this sentence.

For example, if you ask Xiao Hong to ask Xiao Green for you, Xiao Hong will be regarded as my Proxy class, while you are the Real Subject, because what Xiao Hong wants to convey is actually what you said. Then the Subject that you and Xiao Hong both need to implement is talking. Since you can both talk, you will look the same to the outside world.

Does this make sense to you why the delegate class and the proxy class both need to implement the same interface?

That’s to keep the behavior consistent, so the visitor sees no difference between the two. In this way, the proxy class as the middle layer can hide and protect the delegate class object well, and can effectively shield the external direct access to the delegate class object. At the same time, you can also add additional operations on the proxy class, such as xiao Hong will dance before speaking, the outside world will think you will dance before speaking, so, this implementation of the delegate class function enhancement.

The proxy mode has two implementation modes: static proxy and dynamic proxy.

3. Static proxy

What is a static proxy

Let’s look at the implementation steps of static proxy:

1) Define an interface (Subject)

2) Create a Real Subject class that implements this interface

3) Create a Proxy class that also implements this interface

4) Inject the delegate class Real Subject into Proxy class Proxy, and call the corresponding method of Real Subject in the methods of Proxy class. This way, we can block access to the target object through the proxy class and do whatever we want before and after the target method executes.

From an implementation and application point of view, in static proxy, we manually enhance each method of the target object, which is very inflexible (for example, when a new method is added to the interface, both the target object and the proxy object have to be modified) and cumbersome (we need to write a separate proxy class for each target class). The actual application scenarios are very, very few, and the use of static proxies is rarely seen in daily development.

At the JVM level, static proxies turn interfaces, delegate classes, and proxy classes into actual.class files at compile time.

Code sample

1) Define the SMS sending interface

public interface SmsService {
    String send(String message);
}
Copy the code

2) Create a Real Subject class that implements this interface

public class SmsServiceImpl implements SmsService {
    public String send(String message) {
        System.out.println("send message:" + message);
        returnmessage; }}Copy the code

3) Create a Proxy class that also implements this interface

4) Inject the delegate class Real Subject into Proxy class Proxy, and call the corresponding method of Real Subject in the methods of Proxy class. This way, we can block access to the target object through the proxy class and do whatever we want before and after the target method executes.

public class SmsProxy implements SmsService {
	
    // Inject the delegate class into the proxy class
    private final SmsService smsService;

    public SmsProxy(SmsService smsService) {
        this.smsService = smsService;
    }

    @Override
    public String send(String message) {
        // Before calling the delegate class method, we can add our own operations
        System.out.println("before method send()");
        // Call the delegate class method
        smsService.send(message); 
        // After calling the delegate method, we can also add our own operations
        System.out.println("after method send()");
        return null; }}Copy the code

So, how do you use this enhanced send method?

public class Main {
    public static void main(String[] args) {
        SmsService smsService = new SmsServiceImpl();
        SmsProxy smsProxy = new SmsProxy(smsService);
        smsProxy.send("Java"); }}Copy the code

After running the above code, the console prints:

before method send()
send message:java
after method send()
Copy the code

As you can see from the output, we have enhanced the send() method of the delegate class SmsServiceImpl.

Of course, as we can see from the above code, static proxies have some drawbacks. Let’s say we now have a new delegate class that implements the SmsService interface. If we want to enhance this delegate class, we need to write a new proxy class and inject the new delegate class, which is not very flexible. A static proxy is a delegate and a proxy class. Is it possible to make the proxy class generic? For this reason, dynamic proxy applications are born.

4. Java bytecode generation framework

Before we get into dynamics, it’s worth talking a little bit more about.class bytecode files. Dynamic proxy mechanisms are closely related to the Java bytecode generation framework.

In reflection above, we mentioned that a Class corresponds to a.class bytecode file, meaning that the bytecode file stores all the information about a Class. Bytecode is actually a binary file containing machine code that only the JVM can recognize.

The JVM reads the.class bytecode file, retrieves the binary data, loads it into memory, parses the information in the bytecode file, and generates the corresponding class object:

Obviously, this process takes place at compile time.

So, since the JVM loads classes from.class bytecode files (that is, binary information), if we follow the Java compilation system to organize the format and structure of.class bytecode files at runtime, generate the corresponding binary data, and then load that binary data into the corresponding class. In this way, we are done creating a class dynamically at run time. The idea is really the idea of dynamic proxies.

Binary data is generated at run time according to the JVM specification’s organization rules for.class bytecode files. There are many open source frameworks that can do this, such as

  • ASM
  • CGLIB
  • Javassist
  • .

It is important to note that CGLIB is based on ASM. Here’s a quick comparison between ASM and Javassist:

  • The Javassist source-level API is much easier to use than the actual bytecode operations in ASM
  • Javassist provides a higher level of abstraction over complex bytecode level operations. The Javassist source-level API requires little or no knowledge of bytecode, so it is easier and faster to implement.
  • Javassist uses a reflection mechanism, which makes it slower than ASM.

ASM is generally much faster and provides better performance than Javassist, but Javassist is relatively easy to use, so each has its advantages.

In the case of Javassist, let’s look at the power of these frameworks to generate.class bytecode files at run time.

Normally, we create a class that looks like this:

package com.samples;

public class Programmer {
	public void code(a){
		System.out.println("I'm a Programmer,Just Coding....."); }}Copy the code

Here’s how to use Javassist to create the same bytecode as above for a Programmer class:

import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;

public class MyGenerator {
	public static void main(String[] args) throws Exception {
		ClassPool pool = ClassPool.getDefault();
  		// Create Programmer class
		CtClass cc= pool.makeClass("com.samples.Programmer");
		// Define the method
		CtMethod method = CtNewMethod.make("public void code(){}", cc);
		// Insert the method code
		method.insertBefore("System.out.println(\"I'm a Programmer,Just Coding..... \ ");");
		cc.addMethod(method);
		// Save the generated bytecode
		cc.writeFile("d://temp"); }}Copy the code

Open Programmer. Class using the decompile tool and you can see the following code:

So terrible!

5. What is dynamic proxy

OK, now that you understand the Java bytecode generation framework, you’re ready to learn about Dynamic Proxy.

To review the static proxy, we abstracted the execution process of the static proxy as follows:

As you can see, the proxy class does nothing more than add operations before and after calling the delegate class method. Different delegate classes lead to different proxy classes.

So in order to make a generic proxy class, we take the action of calling the method of the delegate class and encapsulate it into a generic handler class, so we have the InvocationHandler role in the dynamic proxy.

Thus, there is an additional role between the agent class and the delegate class. This role is to make unified calls to the agent class’s invocation of the delegate class method. In other words, the InvocationHandler is responsible for the unified handling of the agent class’s invocation of the delegate class method. See below:

From the JVM’s perspective, dynamic proxies generate.class bytecode files dynamically at run time and load them into the JVM. This is already mentioned in the Java Bytecode generation framework.

Although dynamic proxies are used relatively little in our daily development, they are almost a required technique in frameworks. Learning about dynamic proxies also helps us understand and learn the principles of various frameworks, such as Spring AOP and RPC, that rely on dynamic proxies.

In Java, dynamic proxies can be implemented in a variety of ways, such as:

  • JDK Dynamic proxy
  • CGLIB dynamic proxy
  • Javassit dynamic proxy
  • .

These three dynamic proxy mechanisms are described in detail below.

6. JDK dynamic proxy mechanism

Using the step

Let’s take a look at how to use the JDK dynamic proxy mechanism:

1) Define an interface (Subject)

2) Create a Real Subject class that implements this interface

3) Create a handler class and implement the InvocationHandler interface, override its Invoke method (invoke the method of the delegate class using reflection mechanism, and customize some processing logic), and inject the delegate class into the handler class

This method takes the following three arguments:

  • Proxy: Proxy class object (see next step)

  • Method: Remember we talked about Method.invoke in reflection? This is the one that we can use to call methods on our delegate class (reflection)

  • Args: the list of arguments passed to the delegate class method

4) Create Proxy object: create Proxy object of Proxy class object by proxy.newProxyInstance ()

This method takes three parameters:

  • ClassLoader
  • An array of interfaces implemented by the delegate class. At least one interface needs to be passed in
  • Of the callInvocationHandlerThe instance handling interface method (that is, the instance of the class we created in step 3)

In other words: The Proxy object we create with newProxyInstance() of the Proxy class actually calls the Invoke () method of the handler class that implements the InvocationHandler interface. You can customize the processing logic in the Invoke () method, such as what to do before and after method execution.

Code sample

1) Define an interface (Subject)

public interface SmsService {
    String send(String message);
}
Copy the code

2) Create a Real Subject class that implements this interface

public class SmsServiceImpl implements SmsService {
    public String send(String message) {
        System.out.println("send message:" + message);
        returnmessage; }}Copy the code

3) Create a handler class and implement the InvocationHandler interface, override its Invoke method (invoke the method of the delegate class using reflection mechanism, and customize some processing logic), and inject the delegate class into the handler class

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

public class DebugInvocationHandler implements InvocationHandler {
    
    // Inject the delegate class into the handler class.
    private final Object target;

    public DebugInvocationHandler(Object target) {
        this.target = target;
    }
	
    // Override the invoke method
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws InvocationTargetException, IllegalAccessException {
        // Before calling the method, we can add our own operations
        System.out.println("before method " + method.getName());
        Object result = method.invoke(target, args);
        // After calling the method, we can also add our own operations
        System.out.println("after method " + method.getName());
        returnresult; }}Copy the code

4) Define a factory class for creating Proxy objects: create Proxy objects with proxy.newProxyInstance ()

public class JdkProxyFactory {
    public static Object getProxy(Object target) {
        return Proxy.newProxyInstance(
                target.getClass().getClassLoader(),
                target.getClass().getInterfaces(),
                newDebugInvocationHandler(target) ); }}Copy the code

5) Actual use

SmsService smsService = (SmsService) JdkProxyFactory.getProxy(new SmsServiceImpl());
smsService.send("Java");
Copy the code

After running the above code, the console prints:

before method send
send message:Java
after method send
Copy the code

7. CGLIB dynamic proxy mechanism

Using the step

One of the most fatal problems with dynamic proxy in the JDK is that it can only proxy implementation classes that implement an interface, and the proxy classes can only proxy methods implemented in the interface. If the implementation class has its own private method that the interface does not have, the method cannot be called by the proxy.

To solve this problem, we can use the CGLIB dynamic proxy mechanism.

As mentioned above, CGLIB (Code Generation Library) is an ASM based Java bytecode Generation framework that allows us to modify and dynamically generate bytecode at run time. The idea is to generate a subclass using bytecode technology, and in the subclass intercept calls to methods of the parent class, weaving in additional business logic. Have you noticed the keyword, intercept! CGLIB introduces a new role, MethodInterceptor. Similar to the InvocationHandler class in the JDK, it is used to implement the uniform invocation of methods. See below:

In addition, because CGLIB uses inheritance, proxied classes cannot be final modified.

CGLIB is used by many well-known open source frameworks, such as the AOP module in Spring: if the target object implements the interface, the JDK dynamic proxy is used by default, otherwise the CGLIB dynamic proxy is used.

Look at the steps for using CGLIB dynamic proxies:

1) Create a Real Subject class

2) create a MethodInterceptor that implements the MethodInterceptor interface and overwrites the intercept method. The intercept method is used to intercept and enhance the delegate class (similar to the INVOKE method in the JDK dynamic proxy InvocationHandler).

This method takes four parameters:

  • Object VAR1: delegate class Object

  • Method VAR2: Intercepted Method (Method in delegate class that needs enhancement)

  • Object[] var3: method input parameter

  • MethodProxy var4: Used to invoke the original Method of the delegate class (the underlying mechanism is also reflection, but instead of method. invoke, use the MethodProxy. InvokeSuper Method)

3) Create Proxy object: Create the Proxy object of the delegate class object by enhancer.create ()

In other words: The intercept() method of the handler class that implements the MethodInterceptor interface is called by the proxy object we create using the Enhancer class create(). You can customize the processing logic in the intercept() method, such as what to do before and after the method is executed.

The CGLIB dynamic proxy mechanism follows the same steps as the JDK dynamic proxy mechanism. The core of the CGLIB dynamic proxy is the MethodInterceptor and Enhancer. The core of the JDK dynamic Proxy is the handling classes InvocationHandler and Proxy.

Code sample

Unlike the JDK, dynamic proxies do not require additional dependencies. CGLIB is an open source project, so you need to add dependencies manually if you want to use it.

<dependency>
  <groupId>cglib</groupId>
  <artifactId>cglib</artifactId>
  <version>3.3.0</version>
</dependency>
Copy the code

1) Create a Real Subject class

public class AliSmsService {
    public String send(String message) {
        System.out.println("send message:" + message);
        returnmessage; }}Copy the code

2) create a MethodInterceptor that implements the MethodInterceptor interface and overwrites the intercept method

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;

public class DebugMethodInterceptor implements MethodInterceptor {

    @Override
    public Object intercept(Object o, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        // Before calling the method, we can add our own operations
        System.out.println("before method " + method.getName());
        // Call the methods of the delegate class through reflection
        Object object = methodProxy.invokeSuper(o, args);
        // After calling the method, we can also add our own operations
        System.out.println("after method " + method.getName());
        returnobject; }}Copy the code

3) Create Proxy object: Create the Proxy object of the delegate class object by enhancer.create ()

import net.sf.cglib.proxy.Enhancer;

public class CglibProxyFactory {
    public static Object getProxy(Class
        clazz) {
        // Create a dynamic proxy enhancement class
        Enhancer enhancer = new Enhancer();
        // Set the class loader
        enhancer.setClassLoader(clazz.getClassLoader());
        // Set the delegate class (set the parent class)
        enhancer.setSuperclass(clazz);
        // Set method interceptor
        enhancer.setCallback(new DebugMethodInterceptor());
        // Create a proxy class
        returnenhancer.create(); }}Copy the code

We can see from setSuperclass why CGLIB is based on inheritance.

4) Actual use

AliSmsService aliSmsService = 
    (AliSmsService) CglibProxyFactory.getProxy(AliSmsService.class);
aliSmsService.send("Java");
Copy the code

After running the above code, the console prints:

before method send
send message:Java
after method send
Copy the code

Comparison between JDK dynamic proxy and CGLIB dynamic proxy

1) JDK dynamic proxy is based on a delegate class that implements an interface. The CGLIB dynamic proxy is based on the subclass inheriting the delegate class, and realizes the proxy through the subclass.

2) JDK dynamic proxy can only proxy classes that implement interfaces, and can only enhance existing methods in interfaces; CGLIB, on the other hand, can proxy classes that do not implement any interfaces.

3) In terms of efficiency, most of the time the JDK dynamic proxy is more efficient, and this advantage becomes more obvious as the JDK version is upgraded.

There is also the Javassist dynamic proxy mechanism. Like CGLIB, as a Java bytecode generation framework, Javassist comes with the inherent ability to dynamically create a class at runtime, not least by implementing dynamic proxies. By default, Dubbo uses Javassit for dynamic proxy.

8. When to use dynamic proxies

1) One of the design principles in the design pattern is the open closed principle, that is, close to modification, open to extension, we sometimes take over a lot of previous code, the code logic is confusing, it is difficult to start to modify the code, then we can enhance the class through agents.

2) When we use the RPC framework, the framework itself can not know in advance which interfaces and which methods of each business side to call. In this case, a middleman can be established for the client to use through dynamic proxy, which is also convenient for the framework to build logic. To some extent, it is also a manifestation of the loose coupling between the client code and the framework.

3) Spring’s AOP mechanism also uses dynamic proxy, which will not be discussed in detail here.

9. Comparison between static proxy and dynamic proxy

1) Flexibility: Dynamic proxy is more flexible. It does not have to implement interfaces. It can directly proxy implementation classes, and it does not need to create a proxy class for each target class. In addition, in static proxy, once a new method is added to the interface, both the target object and the proxy object have to be modified, which is very troublesome

2) JVM level: Static proxies compile interfaces, implementation classes, and proxy classes into actual.class bytecode files. Dynamic proxies, on the other hand, generate class bytecode dynamically at runtime and load it into the JVM.

10. Summary

It’s a lot to get through, and I feel like ONCE I understand whether bytecode is generated at compile time or at run time, I’m pretty close to getting a handle on static and dynamic proxies. To summarize the roles in static and dynamic proxies:

Static proxy:

  • Subject: Public interface
  • Real Subject: Delegate class
  • Proxy: Proxy class

JDK dynamic proxy:

  • Subject: Public interface
  • Real Subject: Delegate class
  • Proxy: Proxy class
  • InvocationHandler: Handles classes and calls methods uniformly

CGLIB dynamic proxy:

  • Subject: Public interface
  • Real Subject: Delegate class
  • Proxy: Proxy class
  • MethodInterceptor: a MethodInterceptor that calls methods uniformly

The resources

  • Java Core Technologies – Volume 1 Basics – 10th edition
  • “Thinking In Java – 4th edition”
  • JavaGuide: snailclimb. Gitee. IO/JavaGuide
  • Also mountain – Java dynamic proxy mechanism explanation (JDK and additional, Javassist, ASM) : blog.csdn.net/luanlouis/a…

| flying veal 🎉 pay close attention to the public, get updates immediately

  • The blogger, who is studying for a master’s degree at Southeast University, runs an official account “Flying Veal” in his spare time, which opened on December 29, 2020/12/29. Focus on sharing computer foundation (data structure + algorithm + computer network + database + operating system + Linux), Java foundation and interview guide related original technology good article. The purpose of this public account is to let you can quickly grasp the key knowledge, targeted. I hope you can support me and grow with veal 😃
  • And recommend personal maintenance of open source tutorial projects: CS-Wiki (Gitee recommended projects, now accumulated 1.5K + STAR), is committed to create a perfect back-end knowledge system, less detour on the road of technology, welcome to come to exchange learning partners ~ 😊
  • If you do not have a good project in spring and autumn, you can refer to a project I wrote “open source community system Echo” Gitee official recommendation project, has accumulated 400+ star, SpringBoot + MyBatis + Redis + Kafka + Elasticsearch + Spring Security +… And provide detailed development documents and supporting tutorials. The public account background reply Echo can get the supporting tutorial, which is still being updated.