美文网首页程序员java架构经验分享
java开发两年!连JDK动态代理-使用及原理都不知道,你加薪无

java开发两年!连JDK动态代理-使用及原理都不知道,你加薪无

作者: 前程有光 | 来源:发表于2020-10-21 13:57 被阅读0次

    JDK动态代理

    为了引出动态代理,我们看看一个案列!

    广东广州,早上9:00,一位靓仔穿着人字拖、提着鸟笼,走进了早茶店。没错,这就是广州典型的包租公!名下几栋楼,只收租为生,没工作,这人身真是无趣至极!

    这里就得出一个问题:收租不算工作?好吧,其实正真的包租公不会自己去收租,都是委托给中介去做。为什么呢?这其中可以说牵扯到安全、隐私等等。想一下,假如包租公自己收租,当下租客很多,其他包租公就不爽了,干脆找人去捣乱,比如只问不租,浪费包租公时间。当然不仅仅是这样…

    简单使用

    好的,租房中介就出来了,租客看房、谈价、签合同、交付钥匙等等都让中介(代理)去做,房东就坐等收钱就行了。

    代码中使用输出语句代替真正的业务!

    首先,我们定义一个房东接口,和一个房东实现类。(为什么需要接口?后面代理类是根据接口得到的,而且房东类不应该暴露出去的)

    public interface ILandlordService {
        void deliver();
    }
    
    
    public class LandlordServiceImpl implements ILandlordService {
        public void deliver() {
            try {
                System.out.println("告知房东出租成功,房东收钱");
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println("出现异常,终止");
            }
        }
    }
    
    

    接下来创建一个类,实现java.lang.reflect.InvocationHandler接口,该类用来对原来的方法增强。(注意包,被导错)

    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    
    public class TransactionInvocationHandler implements InvocationHandler {
        // 需要被代理的对象(房东)
        private Object target;
    
        public Object getTarget() {
            return target;
        }
    
        public void setTarget(Object target) {
            this.target = target;
        }
    
        /*
            实现接口需要重写的方法
            proxy:代理对象
            method:房东接口中被调用的方法
            args:房东接口中被调用的方法需要的参数
         */
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            Object retVal = null;
            try {
                System.out.println("交付前与中介的交谈");
                // 房东收钱
                retVal = method.invoke(target, args);
                System.out.println("租房成功");
                System.out.println("给钥匙");
            } catch (Exception e) {
                e.printStackTrace();
                System.out.println("出现异常,终止交付");
            }
            return retVal;
        }
    }
    
    

    最后写测试类(租客)

    import org.junit.Test;
    import java.lang.reflect.Proxy;
    
    public class TenantTest {
    
        @Test
        public void test() {
    
            //这两个创建对象和设置值应在spring中配置,让spring创建、设值
            // 创建一个房东对象
            ILandlordService landlordService = new LandlordServiceImpl();
            // 创建增强类对象
            TransactionInvocationHandler transactionInvocationHandler = new TransactionInvocationHandler();
            // 把房东对象设置到增强类对象中使用
            transactionInvocationHandler.setTarget(landlordService);
    
            /*
                 使用强转得到房东类代理对象(中介)
                 newProxyInstance方法需要三个参数
                 ClassLoader loader:类加载器,直接使用本类的类加载器(系统类加载器)
                 Class<?>[] interfaces:接口数组
                 InvocationHandler h:增强类
             */
            ILandlordService proxy = (ILandlordService) Proxy.newProxyInstance(
                    this.getClass().getClassLoader(),
                    // 这里landlordService对象暂时在测试类中创建,
                    // 使用spring后,注入到TransactionInvocationHandler(增强类)的字段(target)中,
                    // 直接到增强类中获取,这样测试类(租客)就不知道实现类了(房东)
                    landlordService.getClass().getInterfaces(),
                    transactionInvocationHandler
            );
            // 调用代理对象的deliver(),会执行增强类的invoke方法,参数method为代理对象调用的方法
            proxy.deliver();
        }
    }
    
    
    执行结果:
    交付前与中介的交谈
    告知房东出租成功,房东收钱
    租房成功
    给钥匙
    
    

    此时很多人就疑惑了,怎么创建代理类的?又怎么会调用增强类的invoke方法的?那就接下去看看原理吧!建议截图看,代码有点多!

    原理

    测试类中的newProxyInstance方法,点进去

    ILandlordService proxy = (ILandlordService) Proxy.newProxyInstance(
        this.getClass().getClassLoader(),
        landlordService.getClass().getInterfaces(),
        transactionInvocationHandler
    );
    
    

    到proxy类的newProxyInstance方法,省去不必要的代码

    @CallerSensitive
    public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h) {
        /*
        * Look up or generate the designated proxy class and its constructor.
        * 查找或生成 指定的代理类和它的构造函数
        */
        /*
            获取生成类的构造器
            loader:类加载器
            interfaces:接口数组
        */
        Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);
        return newProxyInstance(caller, cons, h);
    }
    
    

    往newProxyInstance方法中走,省去不必要的代码,try-catch也省去了

    private static Object newProxyInstance(Class<?> caller,
                                               Constructor<?> cons,
                                               InvocationHandler h) {
        // caller有关安全的,这里忽略
        checkNewProxyPermission(caller, cons.getDeclaringClass());
         /* 
            反射调用构造器创建这个类的对象
            cons:代理类构造器
            h:增强类
        */
        return cons.newInstance(new Object[]{h});
    }
    
    

    使用以下代码获取代理类字节码文件

    ProxyGenerator 在 JDK 11 已经不是公有的了,我这里的源码是JKD 11
    下面获取代理类字节码用的是JDK 8

    import sun.misc.ProxyGenerator;
    import java.io.FileOutputStream;
    
    public class DynamicProxyClassGenerator {
        public static void main(String[] args) throws Exception {
            // 实现类的class对象
            generateClassFile(LandlordServiceImpl.class, "LandlordServiceProxy");
        }
    
        public static void generateClassFile(Class<?> targetClass, String proxyName) throws Exception {
            // 根据类信息和提供的代理类名称,生成字节码
            byte[] classFile = ProxyGenerator.generateProxyClass(proxyName, targetClass.getInterfaces());
            String path = targetClass.getResource(".").getPath();
            System.out.println(path);
            FileOutputStream out = null;
            // 保留到硬盘中
            out = new FileOutputStream(path + proxyName + ".class");
            out.write(classFile);
            out.close();
        }
    }
    
    

    代理类生成的class文件是这样的,deliver()方法是根据我们定义的类得来的,省去几个Object类得到的方法内容

    调用有参构造器创建对象

    import com.hao.service.ILandlordService;
    import java.lang.reflect.InvocationHandler;
    import java.lang.reflect.Method;
    import java.lang.reflect.Proxy;
    import java.lang.reflect.UndeclaredThrowableException;
    
    public final class LandlordServiceProxy extends Proxy implements ILandlordService {
        private static Method m1;
        private static Method m2;
        private static Method m3;
        private static Method m0;
    
         // 创建对象时调用的方法
        public LandlordServiceProxy(InvocationHandler var1) throws  {
            // 调用父类(proxy类)构造器
            super(var1);
        }
    
        public final boolean equals(Object var1) throws  {
        }
    
        public final String toString() throws  {
        }
    
        public final void deliver() throws  {
            try {
                super.h.invoke(this, m3, (Object[])null);
            } catch (RuntimeException | Error var2) {
                throw var2;
            } catch (Throwable var3) {
                throw new UndeclaredThrowableException(var3);
            }
        }
    
        public final int hashCode() throws  {
        }
    
        static {
            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.hao.service.ILandlordService").getMethod("deliver");
                m0 = Class.forName("java.lang.Object").getMethod("hashCode");
            } catch (NoSuchMethodException var2) {
                throw new NoSuchMethodError(var2.getMessage());
            } catch (ClassNotFoundException var3) {
                throw new NoClassDefFoundError(var3.getMessage());
            }
        }
    }
    
    
    protected Proxy(InvocationHandler h) {
        Objects.requireNonNull(h);
        // 把增强类设置到proxy类的字段上
        this.h = h;
    }
    
    
    protected InvocationHandler h;
    
    

    最终Proxy.newProxyInstance()返回的是代理类(中介)

    ILandlordService proxy = (ILandlordService) Proxy.newProxyInstance(
    );
    proxy.deliver();
    
    

    然后我们调用deliver()方法,进入的是代理类的deliver()方法

    public final void deliver() throws  {
        try {
            // 调用父类的h字段(增强类)中的invoke方法,m3在本类static{}中获取
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    
    

    m3在static{}中

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            // 通过反射得到deliver方法
            m3 = Class.forName("com.hao.service.ILandlordService").getMethod("deliver");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
    
    

    最后

    可以看出,调用代理类最终会调用增强类的invoke方法。感谢你看到这里,文章有什么不足还请指正,觉得文章对你有帮助的话记得给我点个赞,每天都会分享java相关技术文章或行业资讯,欢迎大家关注和转发文章!

    相关文章

      网友评论

        本文标题:java开发两年!连JDK动态代理-使用及原理都不知道,你加薪无

        本文链接:https://www.haomeiwen.com/subject/srosmktx.html