代理设计模式&JDK动态代理&CGLIB动态代理原理

代理设计模式

代理模式(Proxy),为其它对象提供一种代理以控制对这个对象的访问。如下图

从上面的类图可以看出,通过代理模式,客户端访问接口时的实例实际上是Proxy对象,Proxy对象持有RealSubject的引用,这样一来Proxy在可以在实际执行RealSubject前后做一些操作,相当于是对RealSubject的Reques方法做了增强。

java 复制代码
/**
 * @author kangming.ning
 * @date 2021/5/8 9:51
 */
public class Client {

    public static void main(String[] args) {
        Subject subject = new Proxy();
        subject.request();
    }
}

Proxy类对RealSubject的request方法进行了增强

java 复制代码
/**
 * @author kangming.ning
 * @date 2021/5/8 9:49
 */
public class Proxy implements Subject {
    private RealSubject realSubject;

    public Proxy() {
        realSubject = new RealSubject();
    }

    @Override
    public void request() {
        System.out.println("proxy do something before");
        realSubject.request();
        System.out.println("proxy do something after");
    }
}

代理设计模式就是这么简单,但却很好用。像上面这种提前设计好的代理可以称为静态代理,因为它是针对某个需要增强的接口直接进行编码设计的。这种方式事实上用的很少,因为它需要针对每个需要增强的接口添加代理类,试想类似于mybatis的Mapper代理如果都是静态代理,是不是会导致我们编写大量代理类,实在麻烦。所以项目中通常都是使用动态代理,所谓动态代理,可以简单理解为代理类可以在运行时动态创建并加载到JVM中,下面做详细介绍。

动态代理

动态代理:从 JVM 角度来说,动态代理是在运行时动态生成类字节码,并加载到 JVM 中的。

其本质和静态代理是一样的,只不过被设计为可以动态生成代理类,使用更加方便,在实际的开发中有大量应用。比如spring的AOP(Aspect Oriented Programming) 切面编程这种唬人的技术,其本质不过就是利用代理模式对方法进行增强。当然spring aop用的是动态代理技术,再具体就是利用JDK动态代理或CGLIB动态代理对针对接口或类生成动态代理类,然后实际执行方法时,实际执行的代理类的逻辑,代理类可以在方法前后做一些操作(增强)。

JDK动态代理

JDK动态代理Proxy是JDK提供的一个用于创建动态代理的一个工具类。下面用一个简单例子说明如何应用JDK动态代理

首先还是需要有被代理的接口,自定股票买卖行为

java 复制代码
/**
 *  股票接口
 * @author kangming.ning
 * @date 2024-01-19 16:40
 * @since 1.0
 **/
public interface StockService {

    /**
     * 购买股票接口
     * @param stockName 买的哪个股票
     * @param totalMoney
     */
    void buyStock(String stockName,double totalMoney);

    /**
     * 卖出股票接口
     * @param stockName 卖的哪个股票
     * @param totalMoney
     */
    void sellStock(String stockName,double totalMoney);
}

接口的实现 ,即买卖股票需要做的一些事情。

java 复制代码
/**
 * @author kangming.ning
 * @date 2024-01-19 16:54
 * @since 1.0
 **/
public class StockServiceImpl implements StockService {
    @Override
    public void buyStock(String stockName, double totalMoney) {
        System.out.println("成功购买了股票" + stockName + " 共" + totalMoney + "元");
    }

    @Override
    public void sellStock(String stockName, double totalMoney) {
        System.out.println("成功卖出了股票" + stockName + " 共" + totalMoney + "元");
    }
}

没有代理的情况,买卖这些事情是需要股民自己去做的

java 复制代码
/**
 * 没有代理的情况
 *
 * @author kangming.ning
 * @date 2024-01-22 09:50
 * @since 1.0
 **/
public class StockDirectClient {
    public static void main(String[] args) {
        StockService stockService = new StockServiceImpl();
        stockService.buyStock("001", 100);
        stockService.sellStock("002", 200);
    }
}

而有代理的情况,通常表现为,我们买卖的基金,其背后实际是大部分是股票(偏股基金),基金经理可以认为是我们的代理。

java 复制代码
/**
 *  韭菜侠(又称为基民)
 * @author kangming.ning
 * @date 2024-01-19 16:57
 * @since 1.0
 **/
public class FragrantFloweredGarlicMan {
    public static void main(String[] args) {
        //韭菜侠发现投资商机,基金好像跌到底部了,果断去抄底
        StockService stockService = new StockServiceImpl();
        StockService proxy = (StockService)Proxy.newProxyInstance(
                //目标类的类加载器
                stockService.getClass().getClassLoader(),
                stockService.getClass().getInterfaces(),
                new StockInvocationHandler(stockService));
        proxy.buyStock("003",100);
        proxy.sellStock("004",200);
    }
}

StockInvocationHandler如下

java 复制代码
/**
 * @author kangming.ning
 * @date 2024-01-19 17:06
 * @since 1.0
 **/
public class StockInvocationHandler implements InvocationHandler {

    /**
     * 代理中的真实对象
     */
    private final Object target;

    public StockInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //调用方法之前,我们可以添加自己的操作
        System.out.println("before method " + method.getName());
        //执行被代理对象原方法
        Object invoke = method.invoke(target, args);
        //调用方法之后,我们同样可以添加自己的操作
        System.out.println("after method " + method.getName());
        return invoke;
    }
}

上面的打印结果类似

java 复制代码
before method buyStock
成功购买了股票003 共100.0元
after method buyStock
before method sellStock
成功卖出了股票004 共200.0元
after method sellStock

可以看出,通过动态代理,对原接口调用前后都分别处理了额外的逻辑,和静态代理实现的效果是一致的。只是动态代理不需要事先编辑好相关代理类,而是在执行过程中动态生成代理类,这样一来,接口变动我们也不必修改代理类,所有调整适配工作都在InvocationHandler的实现类里处理。

JDK动态代理原理

通过上面的案例,我们知道怎么去使用JDK代理了。下面探讨一下其实现原理。Proxy创建动态代理的方法如下

java 复制代码
    /**
     * Returns an instance of a proxy class for the specified interfaces
     * that dispatches method invocations to the specified invocation
     * handler.
     *
     * <p>{@code Proxy.newProxyInstance} throws
     * {@code IllegalArgumentException} for the same reasons that
     * {@code Proxy.getProxyClass} does.
     *
     * @param   loader the class loader to define the proxy class
     * @param   interfaces the list of interfaces for the proxy class
     *          to implement
     * @param   h the invocation handler to dispatch method invocations to
     * @return  a proxy instance with the specified invocation handler of a
     *          proxy class that is defined by the specified class loader
     *          and that implements the specified interfaces
     * @throws  IllegalArgumentException if any of the restrictions on the
     *          parameters that may be passed to {@code getProxyClass}
     *          are violated
     * @throws  SecurityException if a security manager, <em>s</em>, is present
     *          and any of the following conditions is met:
     *          <ul>
     *          <li> the given {@code loader} is {@code null} and
     *               the caller's class loader is not {@code null} and the
     *               invocation of {@link SecurityManager#checkPermission
     *               s.checkPermission} with
     *               {@code RuntimePermission("getClassLoader")} permission
     *               denies access;</li>
     *          <li> for each proxy interface, {@code intf},
     *               the caller's class loader is not the same as or an
     *               ancestor of the class loader for {@code intf} and
     *               invocation of {@link SecurityManager#checkPackageAccess
     *               s.checkPackageAccess()} denies access to {@code intf};</li>
     *          <li> any of the given proxy interfaces is non-public and the
     *               caller class is not in the same {@linkplain Package runtime package}
     *               as the non-public interface and the invocation of
     *               {@link SecurityManager#checkPermission s.checkPermission} with
     *               {@code ReflectPermission("newProxyInPackage.{package name}")}
     *               permission denies access.</li>
     *          </ul>
     * @throws  NullPointerException if the {@code interfaces} array
     *          argument or any of its elements are {@code null}, or
     *          if the invocation handler, {@code h}, is
     *          {@code null}
     */
    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)
        throws IllegalArgumentException
    {
        Objects.requireNonNull(h);

        final Class<?>[] intfs = interfaces.clone();
        final SecurityManager sm = System.getSecurityManager();
        if (sm != null) {
            checkProxyAccess(Reflection.getCallerClass(), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission(Reflection.getCallerClass(), cl);
            }

            final Constructor<?> cons = cl.getConstructor(constructorParams);
            final InvocationHandler ih = h;
            if (!Modifier.isPublic(cl.getModifiers())) {
                AccessController.doPrivileged(new PrivilegedAction<Void>() {
                    public Void run() {
                        cons.setAccessible(true);
                        return null;
                    }
                });
            }
            return cons.newInstance(new Object[]{h});
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError(e.toString(), e);
        } catch (InvocationTargetException e) {
            Throwable t = e.getCause();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError(t.toString(), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError(e.toString(), e);
        }
    }

先通过提供的接口和类加载器创建出代理类,明显这句代码就是创建动态代理的核心逻辑

java 复制代码
 /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);

断点跟踪进去

java 复制代码
    /**
     * a cache of proxy classes
     */
    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>
        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

    private static Class<?> getProxyClass0(ClassLoader loader,
                                           Class<?>... interfaces) {
        if (interfaces.length > 65535) {
            throw new IllegalArgumentException("interface limit exceeded");
        }

        // If the proxy class defined by the given loader implementing
        // the given interfaces exists, this will simply return the cached copy;
        // otherwise, it will create the proxy class via the ProxyClassFactory
        return proxyClassCache.get(loader, interfaces);
    }

proxyClassCachhe定义了一个ProxyClassFactory,明显是用来生成代理的,跟踪进去,最终能在这个工厂类看到如下核心生成代理类的逻辑

java 复制代码
            /*
             * Generate the specified proxy class.
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
                /*
                 * A ClassFormatError here means that (barring bugs in the
                 * proxy class generation code) there was some other
                 * invalid aspect of the arguments supplied to the proxy
                 * class creation (such as virtual machine limitations
                 * exceeded).
                 */
                throw new IllegalArgumentException(e.toString());
            }

返回后,通过代理类直接调用接口方法会直接进入到InvocationHandler 的invoke方法了,因为这里的代理类是动态生成节点码再加载到JVM中的,所以断点并不能看出为什么会进入invoke方法。要明白这点,我们可以通过上面的ProxyGenerator来生成一个代理class节点码文件,再反编译就可以看到动态代理的类定义。

java 复制代码
    public static void main(String[] args) {
        createProxyClassFile();
    }

    private static void createProxyClassFile() {
        //代理对象名称 随便起就行
        String name = "stockServiceJdkProxy";
        byte[] data = ProxyGenerator.generateProxyClass(name, new Class[]{StockService.class});
        FileOutputStream out = null;
        try {
            out = new FileOutputStream(name + ".class");
            System.out.println((new File("hello")).getAbsolutePath());
            out.write(data);
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            if (null != out) {
                try {
                    out.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }
    }

生成的代理类如下

java 复制代码
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

import com.javaguide.basic.proxy.dynamicproxy.jdkfundproxy.StockService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class stockServiceJdkProxy extends Proxy implements StockService {
    private static Method m1;
    private static Method m2;
    private static Method m4;
    private static Method m3;
    private static Method m0;

    public stockServiceJdkProxy(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 new UndeclaredThrowableException(var4);
        }
    }

    public final String toString() throws  {
        try {
            return (String)super.h.invoke(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final void sellStock(String var1, double var2) throws  {
        try {
            super.h.invoke(this, m4, new Object[]{var1, var2});
        } catch (RuntimeException | Error var5) {
            throw var5;
        } catch (Throwable var6) {
            throw new UndeclaredThrowableException(var6);
        }
    }

    public final void buyStock(String var1, double var2) throws  {
        try {
            super.h.invoke(this, m3, new Object[]{var1, var2});
        } catch (RuntimeException | Error var5) {
            throw var5;
        } catch (Throwable var6) {
            throw new UndeclaredThrowableException(var6);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)super.h.invoke(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m4 = Class.forName("com.basic.proxy.dynamicproxy.jdkfundproxy.StockService").getMethod("sellStock", Class.forName("java.lang.String"), Double.TYPE);
            m3 = Class.forName("com.basic.proxy.dynamicproxy.jdkfundproxy.StockService").getMethod("buyStock", Class.forName("java.lang.String"), Double.TYPE);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

这个代理类继承了Proxy类实现了我们提供的接口StockService,静态代码块中,将两个接口通过Class.forName("xxx").getMethod的加载了接口方法定义到成员Method m3,m4变量中。然后就是对接口的实现方法

java 复制代码
    public final void sellStock(String var1, double var2) throws  {
        try {
            super.h.invoke(this, m4, new Object[]{var1, var2});
        } catch (RuntimeException | Error var5) {
            throw var5;
        } catch (Throwable var6) {
            throw new UndeclaredThrowableException(var6);
        }
    }

    public final void buyStock(String var1, double var2) throws  {
        try {
            super.h.invoke(this, m3, new Object[]{var1, var2});
        } catch (RuntimeException | Error var5) {
            throw var5;
        } catch (Throwable var6) {
            throw new UndeclaredThrowableException(var6);
        }
    }

可以看出只有一句核心代码 super.h.invoke(this, m4, new Object[]{var1, var2}); super指的是Proxy类,于是回头找找super.h是啥

java 复制代码
    /**
     * the invocation handler for this proxy instance.
     * @serial
     */
    protected InvocationHandler h;

就是Proxy里面的一个 InvocationHandler。再回头看看我们的InvocationHandler实现

java 复制代码
package com.basic.proxy.dynamicproxy.jdkfundproxy;

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

/**
 * @author kangming.ning
 * @date 2024-01-19 17:06
 * @since 1.0
 **/
public class StockInvocationHandler implements InvocationHandler {

    /**
     * 代理中的真实对象
     */
    private final Object target;

    public StockInvocationHandler(Object target) {
        this.target = target;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //调用方法之前,我们可以添加自己的操作
        System.out.println("before method " + method.getName());
        //执行被代理对象原方法
        Object invoke = method.invoke(target, args);
        //调用方法之后,我们同样可以添加自己的操作
        System.out.println("after method " + method.getName());
        return invoke;
    }
}

所以动态代理在调用某个代理方法时,实际就是调用了 InvocationHandler的invoke方法,第一个参数proxy即为代理类本身,第二个参数method为当前调用方法的方法对象,有了它,就可以利用反射调用实际的方法。最后一个是参数列表传进来的值,反射调用某个方法必须要提供实现方法的对象和调用方法所必须的参数值。可以看到,对于方法的具体实现类,我们是可以自由替换,或者直接不提供实现,通过方法的定义的处理一些业务(mybatis的Mapper动态代理就是这样处理的),总的来讲是非常灵活的。

CGLIB动态代理

JDK动态代理只能代理实现了接口的类,一个没实现任何接口的类是不能被JDK的Proxy进行代理的。CGLIB动态代理则可以完成这个任务,下面看一个简单的使用案例。

引用cglib依赖

java 复制代码
        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
        </dependency>

声明被代理类(为了演示与JDK动态代理的区别,这里使用一个没实现接口的类)

java 复制代码
/**
 * @author kangming.ning
 * @date 2024-01-22 13:38
 * @since 1.0
 **/
public class StockService {

    public void buyStock(String stockName, double totalMoney) {
        System.out.println("成功购买了股票" + stockName + " 共" + totalMoney + "元");
    }

    /**
     * CGLIB 动态代理是通过生成一个被代理类的子类来拦截被代理类的方法调用,因此不能代理声明为 final 类型的类和方法
     * 此方法无法增强
     */
    public final void sellStock(String stockName, double totalMoney) {
        System.out.println("成功卖出了股票" + stockName + " 共" + totalMoney + "元");
    }
}

创建cglib动态代理

java 复制代码
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

/**
 * @author kangming.ning
 * @date 2024-01-22 13:46
 * @since 1.0
 **/
public class CglibStockTest {
    public static void main(String[] args) {
        StockService cglibProxy = (StockService) getCglibProxy(StockService.class);
        cglibProxy.buyStock("001",100);
        cglibProxy.sellStock("002",300);
    }


    private static Object getCglibProxy(Class<?> clazz) {
        //创建动态代理增强类
        Enhancer enhancer = new Enhancer();
        //是否使用缓存
        enhancer.setUseCache(false);
        //设置类加载器
        enhancer.setClassLoader(clazz.getClassLoader());
        //设置代理类
        enhancer.setSuperclass(clazz);
        //设置方法拦截器
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                //调用方法之前,我们可以添加自己的操作
                System.out.println("before method " + method.getName());
                Object result = proxy.invokeSuper(obj, args);
                //调用方法之后,我们同样可以添加自己的操作
                System.out.println("after method " + method.getName());
                return result;
            }
        });
        //创建代理对象
        Object proxy = enhancer.create();
        return proxy;
    }
}

执行打印

java 复制代码
before method buyStock
成功购买了股票001 共100.0元
after method buyStock
成功卖出了股票002 共300.0元

可以看到,cglib可以正常代理没实现接口的普通类。但由于CGLIB 动态代理是通过生成一个被代理类的子类来拦截被代理类的方法调用,因此不能代理声明为 final 类型的类和方法。从上面的执行结果可以看到,被final修饰的方法并没增强。

CGLIB动态代理原理

在调用生成动态代理前,通过设置就可以得到动态代理的.class文件

java 复制代码
public static void main(String[] args) {
        //设置动态代理的.class文件生成路径(用于分析,非必须)
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\class");
        //生成动态代理
        StockService cglibProxy = (StockService) getCglibProxy(StockService.class);
        cglibProxy.buyStock("001",100);
        cglibProxy.sellStock("002",300);
    }

会生成3个.class文件

先看最核心的代理类

java 复制代码
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.proxy.dynamicproxy.stockproxy.cglibproxy;

import java.lang.reflect.Method;
import net.sf.cglib.core.ReflectUtils;
import net.sf.cglib.core.Signature;
import net.sf.cglib.proxy.Callback;
import net.sf.cglib.proxy.Factory;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

public class StockService$$EnhancerByCGLIB$$f50a685c extends StockService implements Factory {
    private boolean CGLIB$BOUND;
    public static Object CGLIB$FACTORY_DATA;
    private static final ThreadLocal CGLIB$THREAD_CALLBACKS;
    private static final Callback[] CGLIB$STATIC_CALLBACKS;
    private MethodInterceptor CGLIB$CALLBACK_0;
    private static Object CGLIB$CALLBACK_FILTER;
    private static final Method CGLIB$buyStock$0$Method;
    private static final MethodProxy CGLIB$buyStock$0$Proxy;
    private static final Object[] CGLIB$emptyArgs;
    private static final Method CGLIB$equals$1$Method;
    private static final MethodProxy CGLIB$equals$1$Proxy;
    private static final Method CGLIB$toString$2$Method;
    private static final MethodProxy CGLIB$toString$2$Proxy;
    private static final Method CGLIB$hashCode$3$Method;
    private static final MethodProxy CGLIB$hashCode$3$Proxy;
    private static final Method CGLIB$clone$4$Method;
    private static final MethodProxy CGLIB$clone$4$Proxy;

    static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class var0 = Class.forName("com.basic.proxy.dynamicproxy.stockproxy.cglibproxy.StockService$$EnhancerByCGLIB$$f50a685c");
        Class var1;
        Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
        CGLIB$equals$1$Method = var10000[0];
        CGLIB$equals$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
        CGLIB$toString$2$Method = var10000[1];
        CGLIB$toString$2$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
        CGLIB$hashCode$3$Method = var10000[2];
        CGLIB$hashCode$3$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$3");
        CGLIB$clone$4$Method = var10000[3];
        CGLIB$clone$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
        CGLIB$buyStock$0$Method = ReflectUtils.findMethods(new String[]{"buyStock", "(Ljava/lang/String;D)V"}, (var1 = Class.forName("com.basic.proxy.dynamicproxy.stockproxy.cglibproxy.StockService")).getDeclaredMethods())[0];
        CGLIB$buyStock$0$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;D)V", "buyStock", "CGLIB$buyStock$0");
    }

    final void CGLIB$buyStock$0(String var1, double var2) {
        super.buyStock(var1, var2);
    }

    public final void buyStock(String var1, double var2) {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            var10000.intercept(this, CGLIB$buyStock$0$Method, new Object[]{var1, new Double(var2)}, CGLIB$buyStock$0$Proxy);
        } else {
            super.buyStock(var1, var2);
        }
    }

    final boolean CGLIB$equals$1(Object var1) {
        return super.equals(var1);
    }

    public final boolean equals(Object var1) {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            Object var2 = var10000.intercept(this, CGLIB$equals$1$Method, new Object[]{var1}, CGLIB$equals$1$Proxy);
            return var2 == null ? false : (Boolean)var2;
        } else {
            return super.equals(var1);
        }
    }

    final String CGLIB$toString$2() {
        return super.toString();
    }

    public final String toString() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        return var10000 != null ? (String)var10000.intercept(this, CGLIB$toString$2$Method, CGLIB$emptyArgs, CGLIB$toString$2$Proxy) : super.toString();
    }

    final int CGLIB$hashCode$3() {
        return super.hashCode();
    }

    public final int hashCode() {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            Object var1 = var10000.intercept(this, CGLIB$hashCode$3$Method, CGLIB$emptyArgs, CGLIB$hashCode$3$Proxy);
            return var1 == null ? 0 : ((Number)var1).intValue();
        } else {
            return super.hashCode();
        }
    }

    final Object CGLIB$clone$4() throws CloneNotSupportedException {
        return super.clone();
    }

    protected final Object clone() throws CloneNotSupportedException {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        return var10000 != null ? var10000.intercept(this, CGLIB$clone$4$Method, CGLIB$emptyArgs, CGLIB$clone$4$Proxy) : super.clone();
    }

    public static MethodProxy CGLIB$findMethodProxy(Signature var0) {
        String var10000 = var0.toString();
        switch (var10000.hashCode()) {
            case -636135857:
                if (var10000.equals("buyStock(Ljava/lang/String;D)V")) {
                    return CGLIB$buyStock$0$Proxy;
                }
                break;
            case -508378822:
                if (var10000.equals("clone()Ljava/lang/Object;")) {
                    return CGLIB$clone$4$Proxy;
                }
                break;
            case 1826985398:
                if (var10000.equals("equals(Ljava/lang/Object;)Z")) {
                    return CGLIB$equals$1$Proxy;
                }
                break;
            case 1913648695:
                if (var10000.equals("toString()Ljava/lang/String;")) {
                    return CGLIB$toString$2$Proxy;
                }
                break;
            case 1984935277:
                if (var10000.equals("hashCode()I")) {
                    return CGLIB$hashCode$3$Proxy;
                }
        }

        return null;
    }

    public StockService$$EnhancerByCGLIB$$f50a685c() {
        CGLIB$BIND_CALLBACKS(this);
    }

    public static void CGLIB$SET_THREAD_CALLBACKS(Callback[] var0) {
        CGLIB$THREAD_CALLBACKS.set(var0);
    }

    public static void CGLIB$SET_STATIC_CALLBACKS(Callback[] var0) {
        CGLIB$STATIC_CALLBACKS = var0;
    }

    private static final void CGLIB$BIND_CALLBACKS(Object var0) {
        StockService$$EnhancerByCGLIB$$f50a685c var1 = (StockService$$EnhancerByCGLIB$$f50a685c)var0;
        if (!var1.CGLIB$BOUND) {
            var1.CGLIB$BOUND = true;
            Object var10000 = CGLIB$THREAD_CALLBACKS.get();
            if (var10000 == null) {
                var10000 = CGLIB$STATIC_CALLBACKS;
                if (var10000 == null) {
                    return;
                }
            }

            var1.CGLIB$CALLBACK_0 = (MethodInterceptor)((Callback[])var10000)[0];
        }

    }

    public Object newInstance(Callback[] var1) {
        CGLIB$SET_THREAD_CALLBACKS(var1);
        StockService$$EnhancerByCGLIB$$f50a685c var10000 = new StockService$$EnhancerByCGLIB$$f50a685c();
        CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
        return var10000;
    }

    public Object newInstance(Callback var1) {
        CGLIB$SET_THREAD_CALLBACKS(new Callback[]{var1});
        StockService$$EnhancerByCGLIB$$f50a685c var10000 = new StockService$$EnhancerByCGLIB$$f50a685c();
        CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
        return var10000;
    }

    public Object newInstance(Class[] var1, Object[] var2, Callback[] var3) {
        CGLIB$SET_THREAD_CALLBACKS(var3);
        StockService$$EnhancerByCGLIB$$f50a685c var10000 = new StockService$$EnhancerByCGLIB$$f50a685c;
        switch (var1.length) {
            case 0:
                var10000.<init>();
                CGLIB$SET_THREAD_CALLBACKS((Callback[])null);
                return var10000;
            default:
                throw new IllegalArgumentException("Constructor not found");
        }
    }

    public Callback getCallback(int var1) {
        CGLIB$BIND_CALLBACKS(this);
        MethodInterceptor var10000;
        switch (var1) {
            case 0:
                var10000 = this.CGLIB$CALLBACK_0;
                break;
            default:
                var10000 = null;
        }

        return var10000;
    }

    public void setCallback(int var1, Callback var2) {
        switch (var1) {
            case 0:
                this.CGLIB$CALLBACK_0 = (MethodInterceptor)var2;
            default:
        }
    }

    public Callback[] getCallbacks() {
        CGLIB$BIND_CALLBACKS(this);
        return new Callback[]{this.CGLIB$CALLBACK_0};
    }

    public void setCallbacks(Callback[] var1) {
        this.CGLIB$CALLBACK_0 = (MethodInterceptor)var1[0];
    }

    static {
        CGLIB$STATICHOOK1();
    }
}

这个代理类的声明是像下面这样的,它继承了被代理类,实现了一个Factory接口,既然是继承,那当父类的方法被声明为final,这个子类是不能重写它的。

java 复制代码
public class StockService$$EnhancerByCGLIB$$f50a685c extends StockService implements Factory {...}

代理类核心代理段如下

java 复制代码
private MethodInterceptor CGLIB$CALLBACK_0;
private static final Method CGLIB$buyStock$0$Method;
private static final MethodProxy CGLIB$buyStock$0$Proxy;

    static void CGLIB$STATICHOOK1() {
        CGLIB$THREAD_CALLBACKS = new ThreadLocal();
        CGLIB$emptyArgs = new Object[0];
        Class var0 = Class.forName("com.basic.proxy.dynamicproxy.stockproxy.cglibproxy.StockService$$EnhancerByCGLIB$$f50a685c");
        Class var1;
        Method[] var10000 = ReflectUtils.findMethods(new String[]{"equals", "(Ljava/lang/Object;)Z", "toString", "()Ljava/lang/String;", "hashCode", "()I", "clone", "()Ljava/lang/Object;"}, (var1 = Class.forName("java.lang.Object")).getDeclaredMethods());
        CGLIB$equals$1$Method = var10000[0];
        CGLIB$equals$1$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/Object;)Z", "equals", "CGLIB$equals$1");
        CGLIB$toString$2$Method = var10000[1];
        CGLIB$toString$2$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/String;", "toString", "CGLIB$toString$2");
        CGLIB$hashCode$3$Method = var10000[2];
        CGLIB$hashCode$3$Proxy = MethodProxy.create(var1, var0, "()I", "hashCode", "CGLIB$hashCode$3");
        CGLIB$clone$4$Method = var10000[3];
        CGLIB$clone$4$Proxy = MethodProxy.create(var1, var0, "()Ljava/lang/Object;", "clone", "CGLIB$clone$4");
        CGLIB$buyStock$0$Method = ReflectUtils.findMethods(new String[]{"buyStock", "(Ljava/lang/String;D)V"}, (var1 = Class.forName("com.basic.proxy.dynamicproxy.stockproxy.cglibproxy.StockService")).getDeclaredMethods())[0];
        CGLIB$buyStock$0$Proxy = MethodProxy.create(var1, var0, "(Ljava/lang/String;D)V", "buyStock", "CGLIB$buyStock$0");
    }

    public final void buyStock(String var1, double var2) {
        MethodInterceptor var10000 = this.CGLIB$CALLBACK_0;
        if (var10000 == null) {
            CGLIB$BIND_CALLBACKS(this);
            var10000 = this.CGLIB$CALLBACK_0;
        }

        if (var10000 != null) {
            var10000.intercept(this, CGLIB$buyStock$0$Method, new Object[]{var1, new Double(var2)}, CGLIB$buyStock$0$Proxy);
        } else {
            super.buyStock(var1, var2);
        }
    }

代理类的这个buyStock方法是重写父类StockService的,然后它通过一个MethodInterceptor的实例去调用intercept方法,当然,这个实例是我们在创建cglib动态代理时传进来的。调用时,第一个参数传的代理类本身,第二个参数CGLIB$buyStock0Method实际是在静态代码块中加载的buyStock方法对象,也是可以用于反射调用原方法的,第三个参数就是调用方法所需的参数列表值,第四个参数是一个MethodProxy,实际使用时可以直接使用这个方法代理调用原对象的方法。回顾一下

java 复制代码
public class CglibStockTest {
    public static void main(String[] args) {
        //设置动态代理的.class文件生成路径(用于分析,非必须)
        System.setProperty(DebuggingClassWriter.DEBUG_LOCATION_PROPERTY, "D:\\class");
        //生成动态代理
        StockService cglibProxy = (StockService) getCglibProxy(StockService.class);
        cglibProxy.buyStock("001",100);
        cglibProxy.sellStock("002",300);
    }


    private static Object getCglibProxy(Class<?> clazz) {
        //创建动态代理增强类
        Enhancer enhancer = new Enhancer();
        //是否使用缓存
        enhancer.setUseCache(false);
        //设置类加载器
        enhancer.setClassLoader(clazz.getClassLoader());
        //设置代理类
        enhancer.setSuperclass(clazz);
        //设置方法拦截器
        enhancer.setCallback(new MethodInterceptor() {
            @Override
            public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
                //调用方法之前,我们可以添加自己的操作
                System.out.println("before method " + method.getName());
                Object result = proxy.invokeSuper(obj, args);
                //调用方法之后,我们同样可以添加自己的操作
                System.out.println("after method " + method.getName());
                return result;
            }
        });
        //创建代理对象
        Object proxy = enhancer.create();
        return proxy;
    }
}

可见MethodInterceptor的intercept的四个参数刚好对上。到这里大概知道是怎么调用到intercept方法的了。然后我们看一下下面这句代码的源码

java 复制代码
 Object result = proxy.invokeSuper(obj, args);

这个是利用MethodProxy去执行原方法

源码如下

java 复制代码
    /**
     * Invoke the original (super) method on the specified object.
     * @param obj the enhanced object, must be the object passed as the first
     * argument to the MethodInterceptor
     * @param args the arguments passed to the intercepted method; you may substitute a different
     * argument array as long as the types are compatible
     * @see MethodInterceptor#intercept
     * @throws Throwable the bare exceptions thrown by the called method are passed through
     * without wrapping in an <code>InvocationTargetException</code>
     */
    public Object invokeSuper(Object obj, Object[] args) throws Throwable {
        try {
            init();
            FastClassInfo fci = fastClassInfo;
            return fci.f2.invoke(fci.i2, obj, args);
        } catch (InvocationTargetException e) {
            throw e.getTargetException();
        }
    }

通过init方法来生成代理类和被代理类的FastClass对象,fci.f2是代理类的FastClass对象,因此可观察这个对象的invoke方法。断点发现fci.i2的值为17,obj为生成的代理对象,args为参数值列表

代理类的FastClass

java 复制代码
    public Object invoke(int var1, Object var2, Object[] var3) throws InvocationTargetException {
        StockService..EnhancerByCGLIB..f50a685c var10000 = (StockService..EnhancerByCGLIB..f50a685c)var2;
        int var10001 = var1;

        try {
            switch (var10001) {
                case 0:
                    return new Boolean(var10000.equals(var3[0]));
                case 1:
                    return var10000.toString();
                case 2:
                    return new Integer(var10000.hashCode());
                case 3:
                    return var10000.clone();
                case 4:
                    return var10000.newInstance((Callback[])var3[0]);
                case 5:
                    return var10000.newInstance((Callback)var3[0]);
                case 6:
                    return var10000.newInstance((Class[])var3[0], (Object[])var3[1], (Callback[])var3[2]);
                case 7:
                    var10000.buyStock((String)var3[0], ((Number)var3[1]).doubleValue());
                    return null;
                case 8:
                    var10000.setCallback(((Number)var3[0]).intValue(), (Callback)var3[1]);
                    return null;
                case 9:
                    f50a685c.CGLIB$SET_THREAD_CALLBACKS((Callback[])var3[0]);
                    return null;
                case 10:
                    f50a685c.CGLIB$SET_STATIC_CALLBACKS((Callback[])var3[0]);
                    return null;
                case 11:
                    var10000.setCallbacks((Callback[])var3[0]);
                    return null;
                case 12:
                    return var10000.getCallback(((Number)var3[0]).intValue());
                case 13:
                    return var10000.getCallbacks();
                case 14:
                    return f50a685c.CGLIB$findMethodProxy((Signature)var3[0]);
                case 15:
                    return var10000.CGLIB$toString$2();
                case 16:
                    return new Integer(var10000.CGLIB$hashCode$3());
                case 17:
                    var10000.CGLIB$buyStock$0((String)var3[0], ((Number)var3[1]).doubleValue());
                    return null;
                case 18:
                    return var10000.CGLIB$clone$4();
                case 19:
                    return new Boolean(var10000.CGLIB$equals$1(var3[0]));
                case 20:
                    f50a685c.CGLIB$STATICHOOK1();
                    return null;
                case 21:
                    var10000.sellStock((String)var3[0], ((Number)var3[1]).doubleValue());
                    return null;
            }
        } catch (Throwable var4) {
            throw new InvocationTargetException(var4);
        }

        throw new IllegalArgumentException("Cannot find matching method/constructor");
    }

fci.i2的值为17(当然,每次执行都会不同),那么执行的是

java 复制代码
 case 17:
                    var10000.CGLIB$buyStock$0((String)var3[0], ((Number)var3[1]).doubleValue());
                    return null;

var10000是代理类对象,所以上面实际执行的是

java 复制代码
    final void CGLIB$buyStock$0(String var1, double var2) {
        super.buyStock(var1, var2);
    }

所以 实际就是执行了其父类的buyStock。到此,整个cglib动态代理的增强逻辑已经粗略介绍完。

相关推荐
让学习成为一种生活方式7 分钟前
R包下载太慢安装中止的解决策略-R语言003
java·数据库·r语言
晨曦_子画13 分钟前
编程语言之战:AI 之后的 Kotlin 与 Java
android·java·开发语言·人工智能·kotlin
南宫生36 分钟前
贪心算法习题其三【力扣】【算法学习day.20】
java·数据结构·学习·算法·leetcode·贪心算法
Heavydrink1 小时前
HTTP动词与状态码
java
ktkiko111 小时前
Java中的远程方法调用——RPC详解
java·开发语言·rpc
计算机-秋大田1 小时前
基于Spring Boot的船舶监造系统的设计与实现,LW+源码+讲解
java·论文阅读·spring boot·后端·vue
神里大人1 小时前
idea、pycharm等软件的文件名红色怎么变绿色
java·pycharm·intellij-idea
小冉在学习1 小时前
day53 图论章节刷题Part05(并查集理论基础、寻找存在的路径)
java·算法·图论
代码之光_19802 小时前
保障性住房管理:SpringBoot技术优势分析
java·spring boot·后端
ajsbxi2 小时前
苍穹外卖学习记录
java·笔记·后端·学习·nginx·spring·servlet