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

news2024/11/15 11:00:17

代理设计模式

代理模式(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动态代理的增强逻辑已经粗略介绍完。

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

qml定位器:Row、Column、Grid、Flow

03.qml import QtQuickWindow {width: 640height: 480visible: truetitle: qsTr("2.7 定位器")Column {RedSquare {}RedSquare {}RedSquare {}spacing: 10 //间隔}Row {RedSquare {}RedSquare {}RedSquare {}spacing: 10x: 58}Flow { //中文是“流”的意思&#xff…

特征融合篇 | YOLOv8 引入长颈特征融合网络 Giraffe FPN

在本报告中,我们介绍了一种名为DAMO-YOLO的快速而准确的目标检测方法,其性能优于现有的YOLO系列。DAMO-YOLO是在YOLO的基础上通过引入一些新技术而扩展的,这些技术包括神经架构搜索(NAS)、高效的重参数化广义FPN(RepGFPN)、带有AlignedOTA标签分配的轻量级头部以及蒸馏增…

【大数据】流处理基础概念(一):Dataflow 编程基础、并行流处理

流处理基础概念&#xff08;一&#xff09;&#xff1a;Dataflow 编程基础、并行流处理 1.Dataflow 编程基础1.1 Dataflow 图1.2 数据并行和任务并行1.3 数据交换策略 2.并行流处理2.1 延迟与吞吐2.1.1 延迟2.1.2 吞吐2.1.3 延迟与吞吐 2.2 数据流上的操作2.2.1 数据接入和数据…

企业网架构

企业网架构 局域网通信不同网段 局域网通信 MAC地址&#xff1a;硬件地址&#xff0c;固定在网卡上的地址(唯一标识一个网卡)&#xff0c;确定网络设备位置的,数据链路层。一个设备可以有多个网卡&#xff0c;每一个网卡都需要一个唯一MAC。ARP协议&#xff1a;通过目的IP&…

回归预测 | Matlab实现GA-APSO-MBP、GA-MBP、MBP、BP多输入单输出回归预测

回归预测 | Matlab实现GA-APSO-MBP、GA-MBP、MBP、BP多输入单输出回归预测 目录 回归预测 | Matlab实现GA-APSO-MBP、GA-MBP、MBP、BP多输入单输出回归预测预测效果基本描述程序设计参考资料 预测效果 基本描述 1.Matlab实现GA-APSO-MBP、GA-MBP、MBP、BP多输入单输出回归预测&…

图片如何转文字?手把手教你图片转文字

面对日益增长的数字化信息&#xff0c;图片转文字成为了一种必不可少的技术&#xff0c;通过简单的步骤&#xff0c;将图片中的文字转换为可编辑和可搜索的文本&#xff0c;提高工作效率和准确性。 这是一款通过光学字符识别&#xff08;OCR&#xff09;实现从图像中提取文字的…

细数2023测试测量及通信行业的十大年度热点动态

2023年&#xff0c;全球测试测量及通信行业在多重技术革新和市场需求的驱动下呈现出强劲的增长态势。这一年里&#xff0c;5G通信网络的大规模部署与商用、人工智能技术的广泛应用、物联网&#xff08;IoT&#xff09;设备数量的爆炸性增长以及电动汽车行业的迅猛发展等因素&am…

在CentOS 7 中配置NFS服务器

目录 1、克隆两个虚拟机 2、安装 NFS 服务 3、NFS 服务使用 1、克隆两个虚拟机 nfs-servernfs-client&#xff08;修改ip地址&#xff09;[rootxnode1 ~]# cd /etc/sysconfig/network-scripts/[rootxnode1 network-scripts]# vi ifcfg-eno16777736 #修改内容如下 BOOTPROT…

浅析Java虚拟机中的ZGC

引言 为什么需要垃圾回收&#xff08;Garbage Collection&#xff09; 垃圾回收是Java开发中的关键机制&#xff0c;负责自动管理内存&#xff0c;防止内存泄漏&#xff0c;提高开发效率和应用程序的稳定性。 Java中主要的垃圾回收方法 标记-清除算法&#xff08;Mark and …

YZ系列工具之YZ05:代码运行中调用“计算器”

我给VBA下的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。我的教程一共九套一部VBA手册&#xff0c;教程分为初级、中级、高级三大部分。是对VBA的系统讲解&#xff0c;从简单的…

CentOS搭建DNS服务器

服务器规划 DNS服务器IP为&#xff1a;172.16.32.253 需要自定义域名解析 172.16.32.253 dns.zhangsan.com 172.16.32.128 test1.zhangsan.com 172.16.32.129 test2.zhangsan.com 172.16.32.130 www.zhangsan.com 1. 服务器初始化 [rootlocalhost ~]# hostnamectl set-hostnam…

python|写一个简单的web应用框架

写应用框架需要写底层服务器么? 这个要区分2种情况&#xff0c;如果应用框架&#xff0c;你没有参考WSGI标准&#xff0c;那么在写应用框架之前&#xff0c;你就必须要定义一套属于自己的服务器&#xff0c;当然本文不采取这种方式&#xff0c;专业的事情应该专业的人来做。我…

springboot集成easypoi

easypoi,主打的功能就是容易,通过简单的配置&#xff0c;就可以方便的写出Excel导出,Excel模板导出,Excel导入,Word模板导出 pom导入依赖 <dependencies><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-star…

实现钉钉与宁波银行对接,助力东吴黄金集团财务智能化

客户介绍&#xff1a; 某黄金集团有限公司是一家在国内外黄金市场上具有重要影响力的综合性黄金企业。该公司拥有一支高素质、专业化的团队&#xff0c;具备丰富的行业经验和卓越的执行力。在业务范围上&#xff0c;该公司涵盖了黄金勘探、采选、冶炼、加工、销售等全产业链&a…

华为机考入门python3--(0)模拟题2-vowel元音字母翻译

分类&#xff1a;字符串 知识点&#xff1a; 字符串转list&#xff0c;每个字符成为list中的一个元素 list(string) 字符串变大小写 str.upper(), str.lower() 题目来自【华为招聘模拟考试】 # If you need to import additional packages or classes, please import …

一篇搞定大论文参考文献,从找文献到交叉引用全流程

我们在写论文过程中&#xff0c;肯定会引用他人的文献&#xff0c;根据自己的写作经历&#xff0c;总结了一套很实用且不会出错的引用方法。1、记录文献顺序 你在论文中每引用一篇文献&#xff0c;你就在文献后加上[1][2]…[n]&#xff0c;然后新建一个word文档&#xff0c;在…

TCP高并发服务器简介(select、poll、epoll实现与区别)

select、poll、epoll三者的实现&#xff1a; select实现TCP高并发服务器的流程&#xff1a; 一、创建套接字&#xff08;socket函数&#xff09;&#xff1a;二、填充服务器的网络信息结构体&#xff1a;三、套接字和服务器的网络信息结构体进行绑定&#xff08;bind函数&…

七款网工在线画拓扑工具

网络工程师一般会使用多种软件进行绘图&#xff0c;以下是一些常用的软件&#xff1a; Visio&#xff1a;这是由微软开发的一款流程图和矢量图绘制软件&#xff0c;网络工程师通常使用它来绘制网络拓扑图、网络架构图等。亿图图示专家&#xff08;Edraw Max&#xff09;&#…

遇到继需证件照的时候怎么办?

你是否曾经遇到过这样的情况&#xff1a;急需一张证件照&#xff0c;却没有时间去照相馆或复印店&#xff0c;而且手头也没有现成的照片。这时候&#xff0c;你可能会感到很困扰&#xff0c;不知道该怎么办才好。别担心&#xff0c;今天我将为你揭示如何在短短一分钟内制作出自…

矩阵和矩阵如何相乘?

矩阵与矩阵相乘遵循特定的数学规则。为了相乘&#xff0c;第一个矩阵的列数必须等于第二个矩阵的行数。矩阵乘法的结果是一个新矩阵&#xff0c;其行数等于第一个矩阵的行数&#xff0c;列数等于第二个矩阵的列数。矩阵乘法不满足交换律&#xff0c;即 AB≠BA。 例子&#xff…