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

代理设计模式

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

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

/**
 * @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方法进行了增强

/**
 * @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动态代理

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

/**
 *  股票接口
 * @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);
}

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

/**
 * @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 + "元");
    }
}

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

/**
 * 没有代理的情况
 *
 * @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);
    }
}

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

/**
 *  韭菜侠(又称为基民)
 * @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如下

/**
 * @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;
    }
}

 上面的打印结果类似

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

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

JDK动态代理原理

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

    /**
     * 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);
        }
    }

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

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

 断点跟踪进去

    /**
     * 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,明显是用来生成代理的,跟踪进去,最终能在这个工厂类看到如下核心生成代理类的逻辑

            /*
             * 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节点码文件,再反编译就可以看到动态代理的类定义。

    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();
                }
            }
        }
    }

生成的代理类如下

//
// 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变量中。然后就是对接口的实现方法

    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是啥

    /**
     * the invocation handler for this proxy instance.
     * @serial
     */
    protected InvocationHandler h;

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

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依赖

        <dependency>
            <groupId>cglib</groupId>
            <artifactId>cglib</artifactId>
            <version>3.3.0</version>
        </dependency>

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

/**
 * @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动态代理

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;
    }
}

执行打印

before method buyStock
成功购买了股票001 共100.0元
after method buyStock
成功卖出了股票002 共300.0元

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

CGLIB动态代理原理

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

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文件

先看最核心的代理类

//
// 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,这个子类是不能重写它的。

public class StockService$$EnhancerByCGLIB$$f50a685c extends StockService implements Factory {...}

代理类核心代理段如下

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$buyStock$0$Method实际是在静态代码块中加载的buyStock方法对象,也是可以用于反射调用原方法的,第三个参数就是调用方法所需的参数列表值,第四个参数是一个MethodProxy,实际使用时可以直接使用这个方法代理调用原对象的方法。回顾一下

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方法的了。然后我们看一下下面这句代码的源码

 Object result = proxy.invokeSuper(obj, args);

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

源码如下

    /**
     * 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

    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(当然,每次执行都会不同),那么执行的是

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

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

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

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

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/341321.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

内网穿透的应用-使用Docker搭建一个Wiki.Js知识库系统并实现分享他人远程创作

文章目录 1. 安装Docker2. 获取Wiki.js镜像3. 本地服务器打开Wiki.js并添加知识库内容4. 实现公网访问Wiki.js5. 固定Wiki.js公网地址 不管是在企业中还是在自己的个人知识整理上&#xff0c;我们都需要通过某种方式来有条理的组织相应的知识架构&#xff0c;那么一个好的知识整…

Consul使用详解

简介 Consul是一个由HashiCorp公司开发的开源软件&#xff0c;其发展历程可以概括为以下几个阶段&#xff1a; 初期阶段&#xff08;2014-2015年&#xff09;&#xff1a;Consul最初发布于2014年5月&#xff0c;这个版本是基于Go语言开发的&#xff0c;并提供了诸如服务发现、…

【百面机器学习】读书笔记(一)

本文系列主要作用就是读书笔记&#xff0c;自己看的话比较杂&#xff0c;没怎么归类过&#xff0c;所以现在跟着这个分类走一遍。本文主要内容为前两章&#xff0c;特征工程和模型评估。 如果我想起一些相关的内容也会做适当的补充&#xff0c;主打就是一个intuition&#xff…

深度学习算法应用实战 | DINOv2 图像相似度实战

特征提取简介 什么是特征提取 特征提取器负责为音频或视觉模型准备输入特征。包括从序列中提取特征&#xff0c;例如&#xff0c;对音频文件进行预处理以生成对数梅尔频谱图特征。以及从图像中提取特征&#xff0c;例如裁剪图像文件&#xff0c;还包括填充、归一化以及转换为N…

Vue+Element(el-switch的使用)+springboot

目录 1、编写模板 2、发送请求 3、后端返数据 1.Controller类 2.interface接口&#xff08;Service层接口&#xff09; 3.Service&#xff08;接口实现&#xff09; 4.interface接口&#xff08;Mapper层接口&#xff09; 5.xml 6.效果 4、el-switch属性 1、编写模板 …

CSDN COC西安城市开发者社区2023年度线下聚会

1. 活动背景 CSDN始终致力于促进城市区域内尖端新型技术开发者交流&#xff0c;提供开放自由的切磋平台。在这个充满挑战和机遇的一年即将结束之际&#xff0c;通过本次聚会&#xff0c;汇聚西安本地各行各业的开发者朋友&#xff0c;回顾过去一年城市社区的成就和收获&#x…

一文(10图)了解Cornerstone3D核心概念(万字总结附导图)

Cornerstone3D介绍 Cornerstone3D是一个专门为处理三维医学影像而设计的JavaScript库。 它是Cornerstone项目的一部分&#xff0c;旨在为医学影像社区提供高性能、可扩展且易于使用的开源Web工具&#xff0c;专注于提供交互式的3D医学图像浏览体验&#xff0c;适用于多种医学…

不想要网页默认的右键菜单栏,怎么封装一个可以自定义的右键菜单组件?

说在前面 &#x1f388;网页的功能和用途可能各不相同&#xff0c;在传统右键菜单栏中无法满足每个用户的个性化需求。通过自定义右键菜单栏&#xff0c;用户可以根据自己的需求添加、调整和删除菜单选项&#xff0c;以实现个性化定制。通过自定义右键菜单栏&#xff0c;可以为…

如何使用 Helm 在 K8s 上集成 Prometheus 和 Grafana|Part 3

在本教程的前两部分&#xff0c;我们分别了解和学习了Prometheus 和 Grafana 的基本概念和使用的前提条件&#xff0c;以及使用 Helm 在 Kubernetes 上安装 Prometheus。 在今天的教程中&#xff0c;我们将为你介绍以下内容&#xff1a; 安装 Grafana&#xff1b;集成 Promethe…

centos 启动nacos pg版本

背景&#xff1a;支持国产化需求&#xff0c;不再使用mysql 1.修改插件 git clone https://github.com/wuchubuzai2018/nacos-datasource-extend-plugins.git cd nacos-datasource-extend-plugins/nacos-postgresql-datasource-plugin-ext mvn package编译成功后&#xff0c;…

Docker(七)使用网络

作者主页&#xff1a; 正函数的个人主页 文章收录专栏&#xff1a; Docker 欢迎大家点赞 &#x1f44d; 收藏 ⭐ 加关注哦&#xff01; Docker 中的网络功能介绍 Docker 允许通过外部访问容器或容器互联的方式来提供网络服务。 一、外部访问容器 容器中可以运行一些网络应用&…

代码随想录算法训练营29期|day27 任务以及具体安排

39. 组合总和// 剪枝优化 class Solution {public List<List<Integer>> combinationSum(int[] candidates, int target) {List<List<Integer>> res new ArrayList<>();Arrays.sort(candidates); // 先进行排序backtracking(res, new ArrayList&…

NetSuite 文心一言(Ernie)的AI应用

有个故事&#xff0c;松下幸之助小时候所处的年代是明治维新之后&#xff0c;大量引用西洋技术的时期。当时大家对“电”能干什么事&#xff0c;充满好奇。“电能干什么&#xff1f;它能帮我们开门么&#xff1f;” 松下幸之助的爷爷对电不屑&#xff0c;于是就问他。松下幸之助…

仓储管理系统——软件工程报告(可行性研究报告及分析)①

可行性研究报告及分析 一、问题定义 1.1项目背景 随着社会的发展以及企业规模的扩大和业务的复杂化&#xff0c;仓库管理变得愈发重要。传统的手工管理方式已经导致了一系列问题&#xff0c;包括库存准确性低、订单处理效率慢等。为了提高仓库运作效率、降低成本并优化库存管…

Qt5.12.0 与 VS2017 在 .pro文件转.vcxproj文件

一、参考资料 stackoverflow qt - How to generate .sln/.vcproj using qmake - Stack Overflowhttps://stackoverflow.com/questions/2339832/how-to-generate-sln-vcproj-using-qmake?answertabtrending#tab-topqt - 如何使用 qmake 生成 .sln/.vcproj - IT工具网 (coder.wo…

搜索与图论第六期 最短路问题

前言 最短路问题真的很重要很重要希望大家都能够完全掌握所有最短路算法&#xff01;&#xff01; 一、最短路问题的分类 Dijkstra&#xff1a; Dijkstra算法是一种著名的图算法&#xff0c;主要用于求解有权图中的单源最短路径问题。它由荷兰计算机科学家艾兹赫尔戴克斯特…

(十)Head first design patterns组合模式(c++)

组合模式 组合模式在参考链接中已经讲得很好了&#xff0c;这里只简单讲讲就好。 组合模式的意图是表达部分-整体层次结构。 当你需要管理一个组合对象&#xff0c;又要管理这个组合对象的单个对象。这个时候就可以让这个组合对象和单个对象继承同一个基类&#xff0c;以便用…

pytorch学习笔记(十一)

优化器学习 把搭建好的模型拿来训练&#xff0c;得到最优的参数。 import torch.optim import torchvision from torch import nn from torch.nn import Sequential, Conv2d, MaxPool2d, Flatten, Linear from torch.utils.data import DataLoaderdataset torchvision.datas…

E. Increasing Subsequences

Part1 寒假思维训练之每日一道构造题&#xff08;思维 构造 数学&#xff09;题目链接&#xff1a; Problem - E - Codeforces 题意&#xff1a; 给定一个整数&#xff0c;数字n的范围是&#xff0c;闭区间&#xff0c;要求构造一个递增子序列&#xff08;可以不连续&…

在Python环境中运行R语言的配环境实用教程

前情提要 在做一些生物信息与医学统计的工作&#xff0c;本来偷懒希望只靠python完成的&#xff0c;结果还是需要用R语言&#xff0c;倒腾了一会儿&#xff0c;调成功了&#xff0c;就记录一下这个过程。 我的环境&#xff1a; win10, pycharm, R-4.3.2 首先&#xff0c;我们…