This article has participated in the “Digitalstar Project” and won a creative gift package to challenge the creative incentive money.

1 From static proxy to dynamic proxy

For example, some people are urged to get married by their parents when they are of marriageable age. Now, under all kinds of pressure, many people are choosing to marry and have children later. So anxious parents began to set up blind dates for their children, more anxious than their children themselves. Let’s look at the code implementation. The code to create the top-level interface, IPerson, is as follows.


public interface IPerson {

    void findLove(a);

}

Copy the code

Son ZhangSan wants to find an object, implement ZhangSan class.


public class ZhangSan implements IPerson {

    public void findLove(a) {
        System.out.println("His son John asked for it."); }}Copy the code

Father ZhangLaosan is going to help his son zhang SAN make a blind date to realize ZhangLaosan.


public class ZhangLaosan implements IPerson {

    private ZhangSan zhangsan;

    public ZhangLaosan(ZhangSan zhangsan) {
        this.zhangsan = zhangsan;
    }

    public void findLove(a) {
        System.out.println("Chang Lao SAN begins his search.");
        zhangsan.findLove();
        System.out.println("Start dating."); }}Copy the code

Take a look at the client test code.


public class Test {
    public static void main(String[] args) {
        ZhangLaosan zhangLaosan = new ZhangLaosan(newZhangSan()); zhangLaosan.findLove(); }}Copy the code

The running result is shown in the figure below.

However, there is a drawback to the above scenario, that is, their father will only help their children to find a partner, other children are not in charge. But the business has grown into an industry, with matchmakers, matchmaking agencies and all sorts of bespoke packages. Using static proxies is too expensive and requires a more generic solution that meets the needs of any single person looking for a partner. This is an upgrade from static to dynamic proxies. Using dynamic agents basically as long as an IPerson can provide matchmaking services. The underlying implementation of dynamic proxies is generally not something we need to implement ourselves, and there are plenty of apis out there. In the Java ecosystem, JDK proxies and CGLib libraries are commonly used today. Start by updating the code based on the JDK’s dynamic proxy support. First create matchmaker (matchmaking agency) class JdkMeipo.


public class JdkMeipo implements InvocationHandler {
    private IPerson target;
    public IPerson getInstance(IPerson target){
        this.target = target; Class<? > clazz = target.getClass();return (IPerson) Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result = method.invoke(this.target,args);
        after();
        return result;
    }

    private void after(a) {
        System.out.println("We agreed to begin our relationship.");
    }

    private void before(a) {
        System.out.println("I'm a matchmaker. I've gathered your needs and started looking."); }}Copy the code

Then create a class ZhaoLiu.


public class ZhaoLiu implements IPerson {

    public void findLove(a) {
        System.out.println("Meet the requirements of Zhao Vi.");
    }

    public void buyInsure(a) {}}Copy the code

The final client test code is as follows.


    public static void main(String[] args) {
        JdkMeipo jdkMeipo = new JdkMeipo();

        IPerson zhaoliu = jdkMeipo.getInstance(new ZhaoLiu());
        zhaoliu.findLove();

    }
		
Copy the code

The running result is shown in the figure below.

2 Static proxy in three-tier architecture

For those of you who still don’t know how to apply the proxy pattern to a business scenario, let’s look at a real business scenario. In distributed business scenarios, database is usually divided into database and table. After that, multiple data sources may need to be configured when Java operations are performed. We dynamically switch data sources by setting data source routes. Start by creating the Order class.


public class Order {
    private Object orderInfo;
    private Long createTime;
    private String id;

    public Object getOrderInfo(a) {
        return orderInfo;
    }
    public void setOrderInfo(Object orderInfo) {
        this.orderInfo = orderInfo;
    }
    public Long getCreateTime(a) {
        return createTime;
    }
    public void setCreateTime(Long createTime) {
        this.createTime = createTime;
    }
    public String getId(a) {
        return id;
    }
    public void setId(String id) {
        this.id = id; }}Copy the code

Create the OrderDao persistence layer operation class.


public class OrderDao {
    public int insert(Order order){
        System.out.println("OrderDao created Order successfully!");
        return 1; }}Copy the code

Create the IOrderService interface.


public interface IOrderService {
    int createOrder(Order order);
}

Copy the code

Create the OrderService implementation class.



public class OrderService implements IOrderService {
    private OrderDao orderDao;

    public OrderService(a){
        // If you use Spring, it should be automatically injected
        // We initialize the orderDao directly in the constructor for ease of use
        orderDao = new OrderDao();
    }

    @Override
    public int createOrder(Order order) {
        System.out.println("OrderService calls orderDao to create the order");
        returnorderDao.insert(order); }}Copy the code

Then use the static proxy, the main function to complete is: according to the order creation time automatically by year repository. According to the open close principle, we modify the original written code logic, through the proxy object to complete. Create a data source routing object and implement the DynamicDataSourceEntry class using a singleton of ThreadLocal.


// Dynamically switch data sources
public class DynamicDataSourceEntry {
   
    // Default data source
    public final static String DEFAULT_SOURCE = null;  
  
    private final static ThreadLocal<String> local = new ThreadLocal<String>();  

    private DynamicDataSourceEntry(a){}

    // Empty the data source
    public static void clear(a) {
        local.remove();
    }  
    
    // Get the name of the data source currently in use
    public static String get(a) {
         return local.get();  
    }  

    // Restore the currently switched data source
    public static void restore(a) {
        local.set(DEFAULT_SOURCE);
    }  
  
    // Set a data source with a known name
    public static void set(String source) {
        local.set(source); 
    }

    // Dynamically set the data source based on the year
    public static void set(int year) {
        local.set("DB_"+ year); }}Copy the code

Create a proxy class OrderServiceSaticProxy to switch data sources.


public class OrderServiceStaticProxy implements IOrderService {

    private SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy");

    private IOrderService orderService;
    public OrderServiceStaticProxy(IOrderService orderService){
        this.orderService = orderService;
    }

    public int createOrder(Order order) {
        before();
        Long time = order.getCreateTime();
        Integer dbRouter = Integer.valueOf(yearFormat.format(new Date(time)));
        System.out.println("Static proxy classes are automatically assigned to DB_" + dbRouter + "] Data source processes data");
        DynamicDataSourceEntry.set(dbRouter);
        orderService.createOrder(order);
        after();
        return 0;
    }

    private void before(a){
        System.out.println("Proxy before method.");
    }

    private void after(a){
        System.out.println("Proxy after method."); }}Copy the code

Take a look at the client test code.


    public static void main(String[] args) {

        try {

            Order order = new Order();
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
            Date date = sdf.parse("2017/02/01");
            order.setCreateTime(date.getTime());

            IOrderService orderService = new OrderServiceStaticProxy(new OrderService());
            orderService.createOrder(order);
        }catch(Exception e){ e.printStackTrace();; }}Copy the code

The running result is shown in the figure below.

As can be seen from the figure above, the results are in line with expectations. Review the class diagram again to see if it matches the one we drew first, as shown below.

Dynamic proxies and static proxies have the same basic idea, but dynamic proxies are more powerful and more adaptable as services expand.

3. Use dynamic proxy to switch data source without perception

After understanding the above case, we will look at the data source dynamic routing service to help our friends deepen their impression of dynamic proxy. Create the dynamic proxy class OrderServiceDynamicProxy as follows.


public class OrderServiceDynamicProxy implements InvocationHandler {

    private SimpleDateFormat yearFormat = new SimpleDateFormat("yyyy");
    private Object target;

    public Object getInstance(Object target){
        this.target = target; Class<? > clazz = target.getClass();return Proxy.newProxyInstance(clazz.getClassLoader(),clazz.getInterfaces(),this);
    }


    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before(args[0]);
        Object object = method.invoke(target,args);
        after();
        return object;
    }

    private void before(Object target){
        try {
            System.out.println("Proxy before method.");
            Long time = (Long) target.getClass().getMethod("getCreateTime").invoke(target);
            Integer dbRouter = Integer.valueOf(yearFormat.format(new Date(time)));
            System.out.println("Static proxy classes are automatically assigned to DB_" + dbRouter + "] Data source processes data");
            DynamicDataSourceEntry.set(dbRouter);
        }catch(Exception e){ e.printStackTrace(); }}private void after(a){
        System.out.println("Proxy after method."); }}Copy the code

Write the client test code as follows.


public static void main(String[] args) {

    try {

        Order order = new Order();

        SimpleDateFormat sdf = new SimpleDateFormat("yyyy/MM/dd");
        Date date = sdf.parse("2018/02/01");
        order.setCreateTime(date.getTime());

        IOrderService orderService = (IOrderService)new OrderServiceDynamicProxy(). 
        getInstance(new OrderService());
        orderService.createOrder(order);
    }catch(Exception e){ e.printStackTrace(); }}Copy the code

As you can see from the above code, the same results can still be achieved. However, with a dynamic proxy implementation, you can implement dynamic routing of data sources not only for Order, but also for any other class. Of course, one of the more important conventions is that the getCreateTime() method must be implemented, because routing rules are based on time. The purpose of the constraint can be achieved through the interface specification, which will not be illustrated here.

4 Handwritten JDK dynamic proxy core principle

Not just the why, but the why. Given that the JDK dynamic proxy is so powerful, how is it implemented? Now let’s explore the principle and create your own dynamic proxy that mimics JDK dynamic proxies. We all know that JDK dynamic proxies use byte reorganization to regenerate objects instead of the original objects to achieve dynamic proxies. The steps for JDK dynamic proxies to generate objects are as follows. (1) get the reference of the proxied object, and get all its interfaces, reflection get. (2) The JDK dynamic proxy class generates a new class, and the new class implements all the interfaces implemented by the proxy class. (3) Dynamically generate Java code, and the newly added business logic method is called by certain logic code (reflected in the code). (4) Compile the newly generated Java code. Class file. (5) Reload into the JVM to run. This process is called bytecode recombination. There is a specification in the JDK that will automatically generate.class files starting with $in the ClassPath. So is there a way to see the “real face” of the replacement object? One such test is to stream the in-memory object bytecode to a new.class file, and then use a decompile tool to view the source code.


    public static void main(String[] args) {
        try {
            IPerson obj = (IPerson)new JdkMeipo().getInstance(new Zhangsan());
            obj.findLove();

            // Use the decompile tool to view the source code
            byte [] bytes = ProxyGenerator.generateProxyClass("$Proxy0".new Class[]{IPerson.class});
            FileOutputStream os = new FileOutputStream("E://$Proxy0.class");
            os.write(bytes);
            os.close();
        } catch(Exception e) { e.printStackTrace(); }}Copy the code

Run the above code to find a proxy0.class file on disk E. Proxy0. Class file is obtained by Jad decomcompiling. Proxy0. Class file is obtained by Jad decomcompiling. Proxy0.jad is decompiled using Jad. Open the proxy0.jad file and see the following content.


import com.tom.pattern.proxy.dynamicproxy.jdkproxy.IPerson;
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 IPerson {
    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})).booleanValue();
        } catch (RuntimeException | Error var3) {
            throw var3;
        } catch (Throwable var4) {
            throw newUndeclaredThrowableException(var4); }}public final void findLove(a) throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw newUndeclaredThrowableException(var3); }}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 buyInsure(a) throws  {
        try {
            super.h.invoke(this, m4, (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)).intValue();
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw newUndeclaredThrowableException(var3); }}static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals".new Class[]{Class.forName("java.lang.Object")});
            m3 = Class.forName("com.tom.pattern.proxy.dynamicproxy.jdkproxy.IPerson")
					.getMethod("findLove".new Class[0]);
            m2 = Class.forName("java.lang.Object").getMethod("toString".new Class[0]);
            m4 = Class.forName("com.tom.pattern.proxy.dynamicproxy.jdkproxy.IPerson")
					.getMethod("buyInsure".new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode".new Class[0]);
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw newNoClassDefFoundError(var3.getMessage()); }}}Copy the code

We found that $Proxy0 inherits the Proxy class, implements the Person interface, and overrides methods like findLove(). Reflection is used to find all methods of the target object in a static code block, and all method references are saved. The overridden method calls the target object’s methods with reflection. You may be wondering: Where did this code come from? This is actually automatically generated by the JDK. Instead of relying on the JDK, we now generate the source code dynamically, compile it dynamically, and then replace the target object and execute it. Create the GPInvocationHandler interface.


public interface GPInvocationHandler {
    public Object invoke(Object proxy, Method method, Object[] args)
            throws Throwable;
}

Copy the code

Create the GPProxy class.


/** * Created by Tom. */
public class GPProxy {

    public static final String ln = "\r\n";

    public static Object newProxyInstance(GPClassLoader classLoader, Class
        [] interfaces, GPInvocationHandler h){
       try {
           //1. Dynamically generate source code. Java file
           String src = generateSrc(interfaces);
           //2.Java file output disk
           String filePath = GPProxy.class.getResource("").getPath();

           File f = new File(filePath + "$Proxy0.java");
           FileWriter fw = new FileWriter(f);
           fw.write(src);
           fw.flush();
           fw.close();

           //3. Compile the generated. Java file into a. Class file
           JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
           StandardJavaFileManager manage = compiler.getStandardFileManager(null.null.null);
           Iterable iterable = manage.getJavaFileObjects(f);

          JavaCompiler.CompilationTask task = compiler.getTask(null,manage,null.null.null,iterable);
          task.call();
          manage.close();

           //4. Compile the generated. Class file and load it into the JVM
          Class proxyClass =  classLoader.findClass("$Proxy0");
          Constructor c = proxyClass.getConstructor(GPInvocationHandler.class);
          f.delete();

           //5. Returns the new proxy object after the bytecode reorganization
           return c.newInstance(h);
       }catch (Exception e){
           e.printStackTrace();
       }
        return null;
    }

    private static String generateSrc(Class
       [] interfaces){
            StringBuffer sb = new StringBuffer();
            sb.append(GPProxy.class.getPackage() + ";" + ln);
            sb.append("import " + interfaces[0].getName() + ";" + ln);
            sb.append("import java.lang.reflect.*;" + ln);
            sb.append("public class $Proxy0 implements " + interfaces[0].getName() + "{" + ln);
                sb.append("GPInvocationHandler h;" + ln);
                sb.append("public $Proxy0(GPInvocationHandler h) { " + ln);
                    sb.append("this.h = h;");
                sb.append("}" + ln);
                for (Method m : interfaces[0].getMethods()){ Class<? >[] params = m.getParameterTypes(); StringBuffer paramNames =new StringBuffer();
                    StringBuffer paramValues = new StringBuffer();
                    StringBuffer paramClasses = new StringBuffer();

                    for (int i = 0; i < params.length; i++) {
                        Class clazz = params[i];
                        String type = clazz.getName();
                        String paramName = toLowerFirstCase(clazz.getSimpleName());
                        paramNames.append(type + "" +  paramName);
                        paramValues.append(paramName);
                        paramClasses.append(clazz.getName() + ".class");
                        if(i > 0 && i < params.length-1){
                            paramNames.append(",");
                            paramClasses.append(",");
                            paramValues.append(",");
                        }
                    }

                    sb.append("public " + m.getReturnType().getName() + "" + m.getName() + "(" 							  + paramNames.toString() + "{" + ln);
                        sb.append("try{" + ln);
                            sb.append("Method m = " + interfaces[0].getName() + ".class. getMethod(\"" + m.getName() + "\",new Class[]{" + paramClasses.toString() + "});" + ln);
                            sb.append((hasReturnValue(m.getReturnType()) ? "return " : "") + getCaseCode("this.h.invoke(this,m,new Object[]{" + paramValues + "})",m.getReturnType()) + ";" + ln);
                        sb.append("}catch(Error _ex) { }");
                        sb.append("catch(Throwable e){" + ln);
                        sb.append("throw new UndeclaredThrowableException(e);" + ln);
                        sb.append("}");
                        sb.append(getReturnEmptyCode(m.getReturnType()));
                    sb.append("}");
                }
            sb.append("}" + ln);
            return sb.toString();
    }

    private static Map<Class,Class> mappings = new HashMap<Class, Class>();
    static {
        mappings.put(int.class,Integer.class);
    }

    private static String getReturnEmptyCode(Class
        returnClass){
        if(mappings.containsKey(returnClass)){
            return "return 0;";
        }else if(returnClass == void.class){
            return "";
        }else {
            return "return null;"; }}private static String getCaseCode(String code,Class
        returnClass){
        if(mappings.containsKey(returnClass)){
            return "(" + mappings.get(returnClass).getName() +  ")" + code + ")." + 							returnClass.getSimpleName() + "Value()";
        }
        return code;
    }

    private static boolean hasReturnValue(Class
        clazz){
        returnclazz ! =void.class;
    }

    private static String toLowerFirstCase(String src){
        char [] chars = src.toCharArray();
        chars[0] + =32;
        returnString.valueOf(chars); }}Copy the code

Create the GPClassLoader class.


public class GPClassLoader extends ClassLoader {

    private File classPathFile;
    public GPClassLoader(a){
        String classPath = GPClassLoader.class.getResource("").getPath();
        this.classPathFile = new File(classPath);
    }

    @Override
    protectedClass<? > findClass(String name)throws ClassNotFoundException {

        String className = GPClassLoader.class.getPackage().getName() + "." + name;
        if(classPathFile ! =null){
            File classFile = new File(classPathFile,name.replaceAll("\ \."."/") + ".class");
            if(classFile.exists()){
                FileInputStream in = null;
                ByteArrayOutputStream out = null;
                try{
                    in = new FileInputStream(classFile);
                    out = new ByteArrayOutputStream();
                    byte [] buff = new byte[1024];
                    int len;
                    while((len = in.read(buff)) ! = -1){
                        out.write(buff,0,len);
                    }
                    return defineClass(className,out.toByteArray(),0,out.size());
                }catch(Exception e){ e.printStackTrace(); }}}return null; }}Copy the code

Create the GPMeipo class.


public class GpMeipo implements GPInvocationHandler {
    private IPerson target;
    public IPerson getInstance(IPerson target){
        this.target = target; Class<? > clazz = target.getClass();return (IPerson) GPProxy.newProxyInstance(new GPClassLoader(),clazz.getInterfaces(),this);
    }

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        before();
        Object result = method.invoke(this.target,args);
        after();
        return result;
    }

    private void after(a) {
        System.out.println("We agreed to begin our relationship.");
    }

    private void before(a) {
        System.out.println("I'm a matchmaker. I've gathered your needs and started looking."); }}Copy the code

The client test code is as follows.


    public static void main(String[] args) {
        GpMeipo gpMeipo = new GpMeipo();
        IPerson zhangsan = gpMeipo.getInstance(new Zhangsan());
        zhangsan.findLove();


    }

Copy the code

At this point, the handwritten JDK dynamic proxy is complete. Do you have another “trump card” to use in interviews?

5 CGLib dynamic proxy API principle analysis

A brief look at the use of CGLib dynamic proxy, or matchmaker as an example, to create the CglibMeipo class.


public class CGlibMeipo implements MethodInterceptor {

    public Object getInstance(Class
        clazz) throws Exception{
        // Is equivalent to the JDK Proxy class, which is used to implement the Proxy
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(clazz);
        enhancer.setCallback(this);
        return enhancer.create();
    }

    public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) 						     throws Throwable {
        before();
        Object obj = methodProxy.invokeSuper(o,objects);
        after();
        return obj;
    }

    private void before(a){
        System.out.println("I'm a matchmaker. I'm going to find you a match, and I've confirmed your needs.");
        System.out.println("Start looking");
    }

    private void after(a){
        System.out.println("By mutual consent, the marriage will take place."); }}Copy the code

Create single Customer class Customer.


public class Customer {

    public void findLove(a){
        System.out.println("Fit the bill"); }}Copy the code

One small detail is that the target object of CGLib dynamic proxy does not need to implement any interface. It implements dynamic proxy by dynamically inheriting the target object. The client test code is as follows.


public static void main(String[] args) {

        try {

            // The JDK uses the read interface information
            //CGLib overrides the parent method
            // The purpose is to generate a new class to implement functions that enhance the code logic

            //JDK Proxy for users, there must be an interface implementation, the target class is relatively complex
            //CGLib can delegate to any ordinary class without any requirement

            //CGLib generates a proxy with more complex logic that makes calls more efficient, generating a FastClass that contains all the logic and no longer needs to reflect calls
            //JDK Proxy generation Proxy logic is simple, relatively low execution efficiency, each time to reflect the dynamic call

            //CGLib has a disadvantage. CGLib cannot proxy final methods

            Customer obj = (Customer) new CGlibMeipo().getInstance(Customer.class);
            System.out.println(obj);
            obj.findLove();
        } catch(Exception e) { e.printStackTrace(); }}Copy the code

How does CGLib dynamic proxy work? We can add a line to the client test code to write the.class file after the dynamic proxy of CGLib to disk and decompile to find out.


public static void main(String[] args) {
    try {

        // Use CGLib's proxy class to write. Class files in memory to local disks
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "E://cglib_proxy_class/");

        Customer obj = (Customer)new CglibMeipo().getInstance(Customer.class);
        obj.findLove();

    } catch(Exception e) { e.printStackTrace(); }}Copy the code

Re-execute the code and we’ll see three more. Class files in E://cglib_proxy_class, as shown below.

Through the debug tracking found that Customer EnhancerByCGLIBEnhancerByCGLIBEnhancerByCGLIB3feeb52a. Class is additional dynamic proxy generated proxy class, inherited the Customer class.


package com.tom.pattern.proxy.dynamicproxy.cglibproxy;

import java.lang.reflect.Method;
import net.sf.cglib.core.ReflectUtils;
import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.*;

public class Customer$$EnhancerByCGLIB$$3feeb52a extends Customer
    implements Factory
{...final void CGLIB$findLove$0()
    {
        super.findLove();
    }

    public final void findLove(a)
    {
        CGLIB$CALLBACK_0;
        if(CGLIB$CALLBACK_0 ! =null) goto _L2; else goto _L1
_L1:
        JVM INSTR pop ;
        CGLIB$BIND_CALLBACKS(this);
        CGLIB$CALLBACK_0;
_L2:
        JVM INSTR dup ;
        JVM INSTR ifnull 37;
           goto _L3 _L4
_L3:
        break MISSING_BLOCK_LABEL_21;
_L4:
        break MISSING_BLOCK_LABEL_37;
        this;
        CGLIB$findLove$0$Method;
        CGLIB$emptyArgs;
        CGLIB$findLove$0$Proxy;
        intercept();
        return;
        super.findLove();
        return; }... }Copy the code

We’ve overridden all the methods of the Customer class, and as you can see from the source code of the proxy class, the proxy class will get all the methods inherited from the parent class, and there will be a MethodProxy corresponding to it, Such as the Method additional findLovefindLovefindLove0 Method, MethodProxyCGLIBMethod, MethodProxy CGLIBMethod, MethodProxyCGLIBfindLove Methods such as 000Proxy are called in the findLove() method of the proxy class.



// Proxy methods (called by methodproxy.invokesuper ())
    final void CGLIB$findLove$0()
    {
        super.findLove();
    }

Methodproxy.invoke () is called by the propped method
// This is why calling methodProxy.invoke from an interceptor causes an infinite loop that keeps calling the interceptor.)
    public final void findLove(a)
    {...// Call the interceptor
        intercept();
        return;
        super.findLove();
        return;
    }

Copy the code

Call process is: the proxy object to invoke this. FindLove () method to call blocker, methodProxy. InvokeSuper () – > additional findLovefindLovefindLove0 – by proxy objects findLove () method. At this point, we see that the MethodInterceptor interceptor calls the proxy method from MethodProxy’s invokeSuper() method, so the code in the MethodProxy class is critical for analyzing what it does.


package net.sf.cglib.proxy;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import net.sf.cglib.core.AbstractClassGenerator;
import net.sf.cglib.core.CodeGenerationException;
import net.sf.cglib.core.GeneratorStrategy;
import net.sf.cglib.core.NamingPolicy;
import net.sf.cglib.core.Signature;
import net.sf.cglib.reflect.FastClass;
import net.sf.cglib.reflect.FastClass.Generator;

public class MethodProxy {
    private Signature sig1;
    private Signature sig2;
    private MethodProxy.CreateInfo createInfo;
    private final Object initLock = new Object();
    private volatile MethodProxy.FastClassInfo fastClassInfo;

    public static MethodProxy create(Class c1, Class c2, String desc, String name1, String name2) {
        MethodProxy proxy = new MethodProxy();
        proxy.sig1 = new Signature(name1, desc);
        proxy.sig2 = new Signature(name2, desc);
        proxy.createInfo = new MethodProxy.CreateInfo(c1, c2);
        returnproxy; }...private static class CreateInfo {
        Class c1;
        Class c2;
        NamingPolicy namingPolicy;
        GeneratorStrategy strategy;
        boolean attemptLoad;

        public CreateInfo(Class c1, Class c2) {
            this.c1 = c1;
            this.c2 = c2;
            AbstractClassGenerator fromEnhancer = AbstractClassGenerator.getCurrent();
            if(fromEnhancer ! =null) {
                this.namingPolicy = fromEnhancer.getNamingPolicy();
                this.strategy = fromEnhancer.getStrategy();
                this.attemptLoad = fromEnhancer.getAttemptLoad(); }}}... }Copy the code

Move on to the invokeSuper() method.


public Object invokeSuper(Object obj, Object[] args) throws Throwable {
    try {
        this.init();
        MethodProxy.FastClassInfo fci = this.fastClassInfo;
        return fci.f2.invoke(fci.i2, obj, args);
    } catch (InvocationTargetException var4) {
        throwvar4.getTargetException(); }}...private static class FastClassInfo {
    FastClass f1;
    FastClass f2;
    int i1;
    int i2;

    private FastClassInfo(a) {}}Copy the code

The above code calls to get the FastClass corresponding to the proxy class and executes the proxy method. Remember the three.class files you generated earlier? Customer EnhancerByCGLIBEnhancerByCGLIBEnhancerByCGLIB3feeb52a FastClass FastClassByCGLIBFastClassByCGLIBFastClassByCGLIB6aad62f1. Class is the proxy class, The Customer FastClassByCGLIBFastClassByCGLIBFastClassByCGLIB2669574a. Class is FastClass by proxy class. The CGLib dynamic proxy executes proxy methods more efficiently than the JDK because CGLib uses the FastClass mechanism. The principle is simple: generate a class for both the proxy class and the proxy-class. This class assigns an index (int) to the proxy class or the proxy-class method. This index is treated as an input parameter, and FastClass can directly locate the method to be called and make the call directly, eliminating reflection calls, thus making calls more efficient than JDK proxies using reflection calls. Let’s decompile a FastClass.


public int getIndex(Signature signature)
    {
        String s = signature.toString();
        s;
        s.hashCode();
        JVM INSTR lookupswitch 11: default 223... JVM INSTR pop ;return -1;
    }

// Some code is omitted

    // Locate the execution method directly according to index
    public Object invoke(int i, Object obj, Object aobj[])
        throws InvocationTargetException
    {
        (Customer)obj;
        i;
        JVM INSTR tableswitch 0 10: default 161
           goto _L1 _L2 _L3 _L4 _L5 _L6 _L7 _L8 _L9 _L10 _L11 _L12
_L2:
        eat();
        return null;
_L3:
        findLove();
        return null; ...throw new IllegalArgumentException("Cannot find matching method/constructor");
    }

Copy the code

FastClass is not generated with the proxy class, but is generated the first time MethodProxy’s invoke() or invokeSuper() methods are executed and placed in the cache.


//MethodProxy's invoke() or invokeSuper() methods call init()
private void init(a) {
    if(this.fastClassInfo == null) {
        Object var1 = this.initLock;
        synchronized(this.initLock) {
            if(this.fastClassInfo == null) {
                MethodProxy.CreateInfo ci = this.createInfo;
                MethodProxy.FastClassInfo fci = new MethodProxy.FastClassInfo();
// If in cache, fetch; If it is not in the cache, a new FastClass is generated
                fci.f1 = helper(ci, ci.c1);
                fci.f2 = helper(ci, ci.c2);
                fci.i1 = fci.f1.getIndex(this.sig1);// Get the index of the method
                fci.i2 = fci.f2.getIndex(this.sig2);
                this.fastClassInfo = fci; }}}}Copy the code

At this point, we have a basic understanding of the principle of CGLib dynamic proxy, interested in the details of the code can be delved into.

6 Comparison between CGLib and JDK Proxy

(1) JDK dynamic proxy implements the interface of the proxied object, CGLib dynamic proxy inherits the proxied object. (2) Both the JDK dynamic proxy and the CGLib dynamic proxy generate bytecodes at runtime. The JDK dynamic proxy writes Class bytecodes directly, while the CGLib dynamic proxy uses the ASM framework to write Class bytecodes. CGLib dynamic proxy implementation is more complex, generating proxy is less efficient than JDK dynamic proxy. (3) JDK dynamic proxy call proxy method is called through reflection mechanism, CGLib dynamic proxy is through FastClass mechanism directly call method, CGLib dynamic proxy execution efficiency is higher.

Pay attention to “Tom play architecture” reply to “design pattern” can obtain the complete source code.

Tom play architecture: 30 real cases of design patterns (attached source code), the challenge of annual salary 60W is not a dream

This article is “Tom play structure” original, reproduced please indicate the source. Technology is to share, I share my happiness! If this article is helpful to you, welcome to follow and like; If you have any suggestions can also leave a comment or private letter, your support is my motivation to adhere to the creation. Pay attention to “Tom bomb architecture” for more technical dry goods!