Pay attention to wechat public number [Java], more articles and learning materials to help you give up the path of programming!


I. Agent mode

Proxy Pattern is a design Pattern in program design. It is characterized by the same interface between the Proxy class and the delegate class. The Proxy class is mainly responsible for preprocessing messages for the delegate class, filtering messages, forwarding messages to the delegate class, and post-processing messages. There is usually an association between proxy class and delegate class. A proxy class object is associated with a delegate class object (target object). The proxy class object itself does not really realize services, but provides specific services by calling the relevant methods of the delegate class object. That is, access the target object through the proxy object, so that we can enhance the implementation of the target object on the basis of additional operations, that is, extend the function of the target object. Take kweichow Moutai, which is very popular recently, as an example. If we want to buy a box of Moutai, we do not buy it directly from Moutai Company, but through agents (such as Wal-Mart, JINGdong, etc.). Moutai company is a target, it is only responsible for the production of Moutai, and other trivial matters such as how to sell, delivery to agents (agents) to deal with.

Static proxy

The proxy object implements the same interface or inherits the same parent class with the target object. The source code is created by the programmer or automatically generated by a specific tool. That is, the interface, target class, proxy class, and so on are already identified at compile time. The.class file for the proxy class is generated before the program runs. You can simply think of the proxy object as writing down the holding target object.

package com.nobody.staticproxy;

/ * * *@DescriptionLiquor manufacturer *@Author Mr.nobody
 * @Date 2021/2/12
 * @Version1.0 * /
public interface WhileWineCompany {
    // Produce wine (demo only write a method, actually several methods can be proxy)
    void product(a);
}
Copy the code
package com.nobody.staticproxy;

/ * * *@DescriptionCommissioned, Maotai, Guizhou *@Author Mr.nobody
 * @Date 2021/2/12
 * @Version1.0 * /
public class Moutai implements WhileWineCompany {
    public void product(a) {
        System.out.println("Producing Kweichow Moutai..."); }}Copy the code
package com.nobody.staticproxy;

/ * * *@DescriptionAgents, JINGdong agents *@Author Mr.nobody
 * @Date 2021/2/12
 * @Version1.0 * /
public class JDProxy implements WhileWineCompany {

    // The agent is Kweichow Moutai Company
    private Moutai moutai;

    public JDProxy(Moutai moutai) {
        this.moutai = moutai;
    }

    public void product(a) {
        System.out.println("Order from JINGdong Mall");
        // The method that actually calls the target object
        moutai.product();
        System.out.println("Jingdong Mall delivery"); }}Copy the code
package com.nobody.staticproxy;

/ * * *@Description
 * @Author Mr.nobody
 * @Date 2021/2/12
 * @Version1.0 * /
public class Main {
    public static void main(String[] args) {
        // Generate a proxy object and pass it in
        WhileWineCompany proxy = new JDProxy(newMoutai()); proxy.product(); }}// Output the resultJingdong Mall placed an order for the production of Kweichow Moutai... Jingdong Mall deliveryCopy the code

Advantages and disadvantages of static proxy:

  • Advantages: The function of the target object can be extended without modifying its function.
  • Disadvantages: If you have a target class that needs to be enhanced, you also need to add the corresponding proxy class, resulting in many proxy classes to be written manually. Also, once the interface adds methods, both the target object and the proxy object are maintained.

Dynamic proxy

Proxy classes that create proxies while the program is running are called dynamic proxies. In static proxies, the proxy class (JDProxy) is defined by us programmers and compiled before the program runs. The proxy classes in dynamic proxies are not defined in Java code, but are dynamically generated at run time according to our “instructions” in Java code. In Java, there are two dynamic proxy implementations, JDK dynamic proxy and CGLIB dynamic proxy. AOP in Spring relies on dynamic proxies for faceted programming.

3.1 JDK Dynamic Proxy

JDK dynamic proxies are based on reflection, generating an anonymous class that implements the proxy interface and then overriding methods for method enhancement. This is handled by calling the InvokeHandler invoke method before invoking the concrete method.

It is characterized by being fast to generate proxy classes, but slow to call method operations at runtime because it is reflection based and can only be programmed against interfaces, that is, the target object implements the interface. If the target object implements the interface, the JDK’s dynamic proxy is used by default.

To implement JDK dynamic proxies, we need to use the Proxy class in the java.lang.reflect package and the InvocationHandler interface

package com.nobody.jdkproxy;

/ * * *@DescriptionLiquor manufacturer *@Author Mr.nobody
 * @Date 2021/2/12
 * @Version1.0 * /
public interface WhileWineCompany {
    // Produce wine (demo only write a method, actually several methods can be proxy)
    void product(a);
}
Copy the code
package com.nobody.jdkproxy;

/ * * *@DescriptionCommissioned, Maotai, Guizhou *@Author Mr.nobody
 * @Date 2021/2/12
 * @Version1.0 * /
public class Moutai implements WhileWineCompany {
    public void product(a) {
        System.out.println("Producing Kweichow Moutai..."); }}Copy the code
package com.nobody.jdkproxy;

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

/ * * *@DescriptionJDK dynamic proxy implementation InvocationHandler interface *@Author Mr.nobody
 * @Date 2021/2/12
 * @Version1.0 * /
public class JDKProxy implements InvocationHandler {

    // Target object to be proxied
    private Object target;

    /** * All methods that execute proxy objects are replaced with invoke methods **@paramProxy Proxy object *@paramMethod Specifies the method to be executed@paramArgs The parameters required to execute the method *@returnMethod returns the value * after execution@throws Throwable
     */
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("Order from JINGdong Mall");
        // By reflection, call the target object's method and pass in the parameters
        Object result = method.invoke(target, args);
        System.out.println("Jingdong Mall delivery");
        return result;
    }

    /** * get the proxy object **@paramTarget Proxied object *@returnThe proxy object */
    public Object getJDKProxy(Object target) {
        this.target = target;
        // loader: the ClassLoader object that defines which ClassLoader object is loaded to generate the proxy object
        // interfaces: an array of Interface objects. That is, we give the proxy objects a set of interfaces that the proxy objects implement (polymorphic) so that we can call methods on those interfaces
        // h: :InvocationHandler object. When a dynamic proxy object calls a method, the invoke method of the InvocationHandler object is called
        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
                target.getClass().getInterfaces(), this); }}Copy the code
package com.nobody.jdkproxy;

/ * * *@Description
 * @Author Mr.nobody
 * @Date 2021/2/12
 * @Version1.0 * /
public class Main {
    public static void main(String[] args) {
        JDKProxy jdkProxy = new JDKProxy();
        // Obtain the agent of Kweichow Moutai
        WhileWineCompany proxy = (WhileWineCompany) jdkProxy.getJDKProxy(new Moutai());
        // Execute methodproxy.product(); }}// Output the resultJingdong mall orders kweichow Moutai... Jingdong Mall deliveryCopy the code

If we add a target object to the proxy, we just need to define a delegate class to implement the interface.

package com.nobody.jdkproxy;

/ * * *@DescriptionConsignment, alcoholic *@Author Mr.nobody
 * @Date 2021/2/12
 * @Version1.0 * /
public class JiuGuiJiu implements WhileWineCompany {

    public void product(a) {
        System.out.println("Producing alcoholic liquor..."); }}Copy the code
package com.nobody.jdkproxy;

/ * * *@Description
 * @Author Mr.nobody
 * @Date 2021/2/12
 * @Version1.0 * /
public class Main {
    public static void main(String[] args) {
        JDKProxy jdkProxy = new JDKProxy();
        // Obtain the agent of Kweichow Moutai
        WhileWineCompany proxy = (WhileWineCompany) jdkProxy.getJDKProxy(new Moutai());
        // Execute method
        proxy.product();

        System.out.println("-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --");

        // Obtain a proxy for alcoholic wine
        proxy = (WhileWineCompany) jdkProxy.getJDKProxy(new JiuGuiJiu());
        // Execute methodproxy.product(); }}// Output the resultJingdong mall orders kweichow Moutai... Jingdong mall express ---------------------- Jingdong Mall orders to produce alcoholic wine... Jingdong Mall deliveryCopy the code

Dynamic proxies make it easy to treat the methods of proxy classes uniformly without having to modify the methods in each proxy class. Because all methods executed by the proxy end up calling the Invoke method in The InvocationHandler, we can uniformly enhance processing in the Invoke method.

How does a proxy object complete the dynamic proxy process by calling the Invoke method of InvocationHandler without seeing the actual proxy class? According to JDK source code analysis, the newProxyInstance method of Proxy class dynamically generates bytecode generation Proxy class (cached in Java virtual machine memory) at runtime, thus creating a dynamic Proxy object.

We print the generated proxy class to view locally by:

byte[] classFile =
        ProxyGenerator.generateProxyClass("$Proxy0", Moutai.class.getInterfaces());
String path = "D:/$Proxy0.class";
try (FileOutputStream fos = new FileOutputStream(path)) {
    fos.write(classFile);
    fos.flush();
} catch (Exception e) {
    e.printStackTrace();
}
Copy the code

Finally, the generated class file is decomcompiled as follows, which shows that the Proxy class inherits from Proxy and implements the same WhileWineCompany interface as the target object. It is easy to understand why a proxy object generated by the newProxyInstance method can be assigned to the WhileWineCompany interface. It can also be seen that the product method of the proxy class actually calls the Invoke method of InvocationHandler, which ultimately implements the method proxy on the target object.

The proxy object ($Proxy0) holds the InvocationHandler object (the JDKProxy we define), the InvocationHandler object holds the target object (target), The Invoke method in InvocationHandler enhances the methods of the target object. The proxy object calls the Invoke method of InvocationHandler, which in turn calls the target object’s method.

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

import com.nobody.staticproxy.WhileWineCompany;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements WhileWineCompany {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    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 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 product(a) throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw newUndeclaredThrowableException(var3); }}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"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.nobody.staticproxy.WhileWineCompany").getMethod("product");
            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 JDK generates a proxy class called $Proxy0 (0 is the number, and multiple proxy classes are incrementing). This class file information is stored in memory. When we create the proxy object, we get the constructor of this class by reflection, and then create the proxy instance.

Proxy classes inherit from Proxy classes because they are single-inherited in Java, which is why in JDK dynamic proxies, the target object must implement the interface.

3.1 Cglib dynamic proxy

JDK dynamic proxies require target objects to implement interfaces. What if a class doesn’t implement interfaces? You can use Cglib to implement dynamic proxies. Cglib (Code Generation Library) is a powerful, high performance, high quality Code Generation Library, it is open source. Dynamic proxy uses asm open source package to load the class file of the target object class, and then modify its bytecode to generate a new subclass for extension processing. That is, you can extend Java classes and implement Java interfaces at run time. It is widely used by many AOP frameworks, such as Spring AOP, which provide methods for interception. The bottom line of the Cglib package is to transform bytecode and generate new classes by using ASM, a small and fast bytecode processing framework. Using ASM directly is not recommended unless you are familiar with the JVM’s internal structure, including the class file format and instruction set. To summarize, cglib inherits propped classes, overrides methods, weavers notifications, dynamically generates bytecode and runs it, requiring propped classes not to be modified with final modifiers.

<! -- Introducing cglib dependencies -->
<dependency>
    <groupId>cglib</groupId>
    <artifactId>cglib</artifactId>
    <version>3.3.0</version>
</dependency>
Copy the code
package com.nobody.cglib;

/ * * *@DescriptionCommissioned, Maotai, Guizhou *@Author Mr.nobody
 * @Date 2021/2/12
 * @Version1.0 * /
public class Moutai {

    public void product(a) {
        System.out.println("Producing Kweichow Moutai..."); }}Copy the code
package com.nobody.cglib;

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/ * * *@DescriptionDynamic proxy class that implements the MethodInterceptor MethodInterceptor interface *@Author Mr.nobody
 * @Date 2021/2/12
 * @Version1.0 * /
public class CglibProxy implements MethodInterceptor {

    // Target object to be proxied
    private Object target;

    // Dynamically generates a new class, using the parent class's no-argument constructor to create a proxy instance that specifies a particular callback

    /** * dynamically generates a new class **@param target
     * @return* /
    public Object getProxyObject(Object target) {
        this.target = target;
        // dynamic code generator
        Enhancer enhancer = new Enhancer();
        // Callback method
        enhancer.setCallback(this);
        // Sets the parent type of the generated proxy class
        enhancer.setSuperclass(target.getClass());
        // Generate bytecode dynamically and return proxy object
        return enhancer.create();
    }

    /** * intercepting method **@paramO Dynamically generated proxy class instance of CGLib *@paramMethod Refers to the proxied method called by the entity class above@paramObjects List of method parameter values *@paramThe proxy class generated by methodProxy has proxy references to methods *@returnProxy object@throws Throwable
     */
    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy)
            throws Throwable {
        System.out.println("Order from JINGdong Mall");
        Object result = methodProxy.invoke(target, objects);
        System.out.println("Jingdong Mall delivery");
        returnresult; }}Copy the code
package com.nobody.cglib;

/ * * *@Description
 * @Author Mr.nobody
 * @Date 2021/2/12
 * @Version1.0 * /
public class Main {
    public static void main(String[] args) {
        // Target object
        Moutai moutai = new Moutai();
        // Proxy object
        Moutai proxy = (Moutai) new CglibProxy().getProxyObject(moutai);
        // Execute the proxy object methodproxy.product(); }}// Output the resultJingdong mall orders kweichow Moutai... Jingdong Mall deliveryCopy the code

Cglib dynamic proxy:

  1. Proxyed classes cannot be final.
  2. Methods that need to be extended cannot have final or static keywords; otherwise, they will not be intercepted. That is, executing methods only execute the methods of the target object, not the content of the method extension.

Four, two kinds of dynamic proxy difference

The JDK dynamic proxy is based on reflection and generates an anonymous class that implements the proxy interface. Cglib dynamic proxy is based on inheritance mechanism, which inherits proxy class. The bottom layer is based on asm third-party framework to load the class file of proxy object class, and then modify its bytecode to generate subclasses to deal with it. JDK dynamic proxy is fast to generate classes and slow to execute class methods. Cglib dynamic proxies are slow to generate classes and fast to perform subsequent class methods. The JDK can only program for interfaces, whereas Cglib can program for classes and interfaces. In the Springboot project, adding spring.aop.proxy-target-class=true to the configuration file forces the use of Cglib dynamic proxies to implement AOP. If the target object implements an interface, the default is to implement AOP dynamically using the JDK; if the target object does not implement an interface, the CGLIB library must be used to implement AOP dynamically.

This demo project has been uploaded to Github, if you need to download, welcome Star. Github.com/LucioChn/dy…

Pay attention to wechat public number [Java], more articles and learning materials to help you give up the path of programming!