JDK动态代理和静态代理

本文详细介绍了代理的设计理念,区分了静态代理和动态代理的区别,并展示了JDK动态代理在SpringAOP中的应用,包括如何使用`InvocationHandler`和`Proxy.newProxyInstance`方法实现动态代理。
摘要由CSDN通过智能技术生成

代理是什么?

代理是一种设计理念,是一种设计思想。
对被代理的目标类进行增强。
比如,我有类A,B,C,现在需要对A、B、C类中的方法进行增强,每次调用三个类中的方法时,都会自动打印日志"target被增强了"。
而代理就可以实现对被代理的目标类的增强。
代理有两种实现,静态代理和动态代理。

静态代理是什么?

在程序编译前,我们就手动地将最终的代理类写好。
假设有接口IAservice,类Aservice实现IAservice。现在要对类Aservice进行增强,则需要写一个代理类AserviceProxy,实现IAservice,实现所有方法,并在方法中进行增强,然后再调用Aservice的原始方法。
代码如下

/**
 * 测试类
 */
public class MyStaticProxyTest {
    public static void main(String[] args) {
        IBservice bservice = new MyStaticProxy(new Bservice());
        bservice.say();
        /*
        打印结果如下:

        target被增强了
        你好,我是Bservice,实现了IBservice
         */
    }
}

/**
 * 目标类的接口
 */
interface IBservice {
    void say();
}

/**
 * 目标类,需要被增强的类。
 */
class Bservice implements IBservice {
    @Override
    public void say() {
        System.out.println("你好,我是Bservice,实现了IBservice");
    }
}

/**
 * 静态代理类,需要和被代理类{@link Bservice}实现相同的接口{@link IBservice},然后在和目标类的相同方法中,调用目标类的相同方法。
 */
class MyStaticProxy implements IBservice {
    /**
     * 被代理的目标类,{@link MyStaticProxy}就是对{@link this#target}进行增强
     */
    private final IBservice target;

    public MyStaticProxy(IBservice target) {
        this.target = target;
    }

    @Override
    public void say() {
        System.out.println("target被增强了");
        // 在代理类的say方法中调用目标类的say方法
        target.say();
    }
}

动态代理是什么?

在程序编译打包完成后,都没有生成代理类,而是在运行时,动态地生成代理类的字节码文件,所以叫做动态代理。
假设有接口IAservice,类Aservice实现IAservice。现在要对类Aservice进行增强,则AserviceProxy则是在程序运行的时候,动态生成。
我们只需要定义动态代理类,并制定动态代理类对哪些类进行代理即可。

JDK动态代理有哪些应用场景?

  • Spring AOP中的事务、过滤器、拦截器等。

如何使用JDK动态代理?

代码如下

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

public class MyJdkDynamicProxy1 {
    public static void main(String[] args) {
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
        /*使用自定义的动态代理类,对Aservice对象进行代理。
        需要注意的是,必须要使用IAservice接口作为引用,否则会报错,比如,使用Aservice来接口,就会报错。
        因为JDK动态代理是通过实现被代理类实现的接口的方式,所以必须要用接口作为引用。
        */
        IAservice strings = new MyJdkDynamicProxy(new Aservice()).getProxy();
        strings.say();
        /*
        打印结果如下:

        target被增强了
        你好,我是Aservice,实现了IAservice

        解释:调用了say接口的方法,打印了一次target被增强了。
        而该打印语句正是{@link MyJdkDynamicProxy#invoke()}方法中对原方法的增强。
        fa
         */
    }
}

interface IAservice {
    void say();
}

class Aservice implements IAservice {

    @Override
    public void say() {
        System.out.println("你好,我是Aservice,实现了IAservice");
    }

    public void run() {
        System.out.println("我在奔跑");
    }
}

/**
 * JDK动态代理类,需要实现{@link InvocationHandler}接口,
 * 然后实现{@link InvocationHandler#invoke(Object, Method, Object[])}方法,
 */
class MyJdkDynamicProxy implements InvocationHandler {
    /**
     * 被代理的目标类,{@link MyJdkDynamicProxy}就是对{@link this#target}进行增强
     */
    private final Object target;

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

    /**
     * 获取代理对象。
     *
     * @param <T> 方法泛型。此处方法泛型代表方法的返回类型是什么,方法泛型就是什么。
     * @return 代理之后的对象。
     */
    public <T> T getProxy() {
        /*创建代理对象,并对被代理对象的所有接口的方法进行代理。target.getClass().getInterfaces()就是目标类的所有接口*/
        return (T) Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
    }

    /**
     * 此方法中,可以对调用对象的方法进行增强,下面的代码中,System.out.println("target被增强了");就是对原来的方法进行增强。
     * method.invoke(target, args);代码就是对原来方法的调用。
     *
     * @return 返回原方法调用结果
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("target被增强了");
        return method.invoke(target, args);
    }
}

JDK动态代理是如何实现的?

  • 如何创建代理类
    在运行时动态生成代理类的字节码文件,并通过反射调用代理类的构造器,动态创建代理类。
查看java.lang.reflect.Proxy#newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)方法。
看该方法是如何创建代理类的。
以下代码中,只挑了关键部分。

public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
    {

        final Class<?>[] intfs = interfaces.clone();
        /*
         * Look up or generate the designated proxy class.
         * 在运行时动态地生成代理类
         */
        Class<?> cl = getProxyClass0(loader, intfs);

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            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) {//...省略异常处理代码
        } catch (InvocationTargetException e) {//...省略异常处理代码
        } catch (NoSuchMethodException e) {//...省略异常处理代码
        }
    }
查看Class<?> cl = getProxyClass0(loader, intfs)方法
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);
    }
查看proxyClassCache.get(loader, interfaces)方法
    /**
     * Look-up the value through the cache. This always evaluates the
     * {@code subKeyFactory} function and optionally evaluates
     * {@code valueFactory} function if there is no entry in the cache for given
     * pair of (key, subKey) or the entry has already been cleared.
     *
     * @param key       possibly null key
     * @param parameter parameter used together with key to create sub-key and
     *                  value (should not be null)
     * @return the cached value (never null)
     * @throws NullPointerException if {@code parameter} passed in or
     *                              {@code sub-key} calculated by
     *                              {@code subKeyFactory} or {@code value}
     *                              calculated by {@code valueFactory} is null.
     */
    public V get(K key, P parameter) {
        Objects.requireNonNull(parameter);

        expungeStaleEntries();

        Object cacheKey = CacheKey.valueOf(key, refQueue);

        // lazily install the 2nd level valuesMap for the particular cacheKey
        ConcurrentMap<Object, Supplier<V>> valuesMap = map.get(cacheKey);
        if (valuesMap == null) {
            ConcurrentMap<Object, Supplier<V>> oldValuesMap
                = map.putIfAbsent(cacheKey,
                                  valuesMap = new ConcurrentHashMap<>());
            if (oldValuesMap != null) {
                valuesMap = oldValuesMap;
            }
        }

        // create subKey and retrieve the possible Supplier<V> stored by that
        // subKey from valuesMap
        // subKeyFactory.apply(key, parameter)会生成代理类的字节码
        Object subKey = Objects.requireNonNull(subKeyFactory.apply(key, parameter));
        // ...省略代码
    }

按住键盘Ctrl+Alt+鼠标左键点击,查看ProxyClassFactory中的方法
在这里插入图片描述

查看subKeyFactory.apply(key, parameter)
public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {
            Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);
            /*
             * Generate the specified proxy class.
             * 这里已经生成了字节码文件
             */
            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);
            try {
            	/*
            	 *这是一个native方法:private static native Class<?> defineClass0(ClassLoader loader, String name,
                                                byte[] b, int off, int len);
            	 */
                return defineClass0(loader, proxyName,
                                    proxyClassFile, 0, proxyClassFile.length);
            } catch (ClassFormatError e) {
            }
        }
    }
查看代码byte[] proxyClassFile = ProxyGenerator.generateProxyClass(
                proxyName, interfaces, accessFlags);

public static byte[] generateProxyClass(final String var0, Class<?>[] var1, int var2) {
        ProxyGenerator var3 = new ProxyGenerator(var0, var1, var2);
        final byte[] var4 = var3.generateClassFile();
        /*
         * saveGeneratedFiles如果是true,则会将生成的字节码文件写入磁盘。
         * 通过查看 Ctrl+鼠标左键查看saveGeneratedFiles
         * private static final boolean saveGeneratedFiles = (Boolean)AccessController.doPrivileged(new GetBooleanAction("sun.misc.ProxyGenerator.saveGeneratedFiles"));
         * 得知,可以通过sun.misc.ProxyGenerator.saveGeneratedFiles参数对saveGeneratedFiles进行设置,
         * 则可以通过如下代码对该参数进行设置
         */
        if (saveGeneratedFiles) {
            AccessController.doPrivileged(new PrivilegedAction<Void>() {
                public Void run() {
                    try {
                        int var1 = var0.lastIndexOf(46);
                        Path var2;
                        if (var1 > 0) {
                            Path var3 = Paths.get(var0.substring(0, var1).replace('.', File.separatorChar));
                            Files.createDirectories(var3);
                            var2 = var3.resolve(var0.substring(var1 + 1, var0.length()) + ".class");
                        } else {
                            var2 = Paths.get(var0 + ".class");
                        }

                        Files.write(var2, var4, new OpenOption[0]);
                        return null;
                    } catch (IOException var4x) {
                        throw new InternalError("I/O exception saving generated file: " + var4x);
                    }
                }
            });
        }

        return var4;
    }

  • 查看动态生成的代理类
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

	/**
	 * 类名为$Proxy0,JDK动态代理动态生成的类,类名一般是$符号开头,后面是Proxy0,我猜如果有多哥代理类,则是$Proxy1、$Proxy2...
	 * 继承了Proxy类
	 */
final class $Proxy0 extends Proxy implements IAservice {
    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    public $Proxy0(InvocationHandler var1) throws  {
        super(var1);
    }

	/**重写了equals方法*/
    public final boolean equals(Object var1) throws  {
        try {
        	// h是动态代理对象,即Main方法中创建的MyDynamicProxy的对象,
        	// 而m1是equals方法,这里就是通过反射调用目标类的equals方法,下面的toString、hashCode方法同理。
            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 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);
        }
    }

    public final void say() throws  {
        try {
        	// 通过反射调用h的invoke方法,而invoke方法对m3方法进行调用。
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    static {
    	/*
	     * 在静态代码快中,对m0、m1、m2、m3进行复制,
	     * 而m3正是目标类的say方法。
    	 */
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m3 = Class.forName("com.lhy.java.base.dynamic_proxy.IAservice").getMethod("say");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

参考

bilibili视频:Java面试必知必会.Java基础.05.动态代理(JDK/CGLIB)

  • 27
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值