动态代理 ---jdk

我们都对静态代理比较熟悉(不熟悉自己找个例子看看就知道了)

jdk动态代理,重点就是一个。自动生成一个类的静态代理,并实例化这个自动生成的代理。

动态代理 ,就是自动生成的  代理

 

先上一个标准用法

接口

public interface Dog {
    void run();

    int eatCount();

    boolean eta(String name);
}

实现

public class GunDog implements Dog {
    @Override
    public void run() {
        System.out.println("----------------飞奔");
    }

    @Override
    public int eatCount() {
        return 5;
    }

    @Override
    public boolean eta(String name) {
        return false;
    }
}

必不可少的 InvocationHandler 实现

public class MyInvocationHandler implements InvocationHandler {

    Object target; // 这个为 需要 被 代理 的对象 

    public void setTarget(Object target){
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // proxy 为动态代理生产的对象
        // 运行的方法
        // 方法对应的参数
        
        // 此处的逻辑完全自己掌控,经常的写法包含  写一些 逻辑 ,再 method.invoke(target , args) , 再写一下逻辑 
        
        //放回值为方法的放回,可以为 method.invoke(target , args) 的返回值 ,也可以完全 根据 自己需求来写
        return null;
    }
}

使用

public class MyProxyFactory {

    public static Object getProxy(Object target){
        InvocationHandler invocationHandler = new MyInvocationHandler();
        ((MyInvocationHandler) invocationHandler).setTarget(target);
        Object o = Proxy.newProxyInstance(target.getClass().getClassLoader() , target.getClass().getInterfaces() , invocationHandler);
        return o;
    }

    public static void main(String[] args) {
        Dog dog = new GunDog();
        Dog proxy = (Dog) MyProxyFactory.getProxy(dog);
        proxy.run();

        System.out.println(proxy.eatCount()==null?null:proxy.eatCount());
        proxy.eta("fish");
    }
}

 

简单的说明一下把,什么是代理 和 代理模式什么的,我就不说了

就先来看一下,这个代理对象到底是怎样的

public class ZTest {

    public static void main(String[] args) {
        m1();
    }

    public static void m1(){
        byte[] proxy0s = ProxyGenerator.generateProxyClass("$Proxy0", new Class[]{Dog.class});
        try (FileOutputStream fileOutputStream = new FileOutputStream("$Proxy0.class")) {
            fileOutputStream.write(proxy0s);
            fileOutputStream.close();
        } catch (FileNotFoundException e) {
            e.printStackTrace();
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}

这样会生产一个 $Proxy0.class 的文件(在哪里自己直接文件搜,不细说) 。 用itellij idea 打开 这个.class文件

import com.xsz.impl.proxy.Dog;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

public final class $Proxy0 extends Proxy implements Dog {
    private static Method m1;
    private static Method m3;
    private static Method m4;
    private static Method m2;
    private static Method m5;
    private static Method m0;

    public $Proxy0(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 void run() throws  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final boolean eta(String var1) throws  {
        try {
            return (Boolean)super.h.invoke(this, m4, 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 Integer eatCount() throws  {
        try {
            return (Integer)super.h.invoke(this, m5, (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);
        }
    }

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));
            m3 = Class.forName("com.xsz.impl.proxy.Dog").getMethod("run");
            m4 = Class.forName("com.xsz.impl.proxy.Dog").getMethod("eta", Class.forName("java.lang.String"));
            m2 = Class.forName("java.lang.Object").getMethod("toString");
            m5 = Class.forName("com.xsz.impl.proxy.Dog").getMethod("eatCount");
            m0 = Class.forName("java.lang.Object").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(var2.getMessage());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(var3.getMessage());
        }
    }
}

这个就是代理对象 反编译后 得到的java 文件的样子了 ,这个 重写 hashCode ,toString ,equals 方法,没什么好说的。

 

重要的点一,这个对象实现了Dog接口,且重写里面的方法 ,run  ,eatCount ,eta

重要点二 ,有 Method 类型的 私有静态 变量 ,在static 构造函数中直接 赋值(赋值简单明白)

重要点三,有一个 InvocationHandler 的构造函数。

 

再回过头来看 Object o = Proxy.newProxyInstance(target.getClass().getClassLoader() , target.getClass().getInterfaces() , invocationHandler); 做了什么(源码)

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

简单分析(这个结合我们$Proxy0 文件看)

 

 

 

返回的是代理对象,代理对象进行调用时 ,如调用 proxy.eta("fish") ,实际调用参考 $Proxy0  文件

 

 选一个方法分析。我们在看看 第二个参数 m4是什么 。

m4 为 静态私有方法,在static代码块 中 赋值。

 

 

本质, 调用 我们自己写的 InvocationHandler 中的 invoke 方法。返回结果 直接被 代理对象返回

也就是,我们可以在自己的 InvocationHandler 的invoke方法中,任意写自己的逻辑。

 

我个人以前也不太懂代理对象长啥样,怎么就是那样调用的,我想看了 $Proxy0 文件之后,就能一清二楚的知道整个调用逻辑了

还有一点,运行这个程序是会报错的,具体报错原因我就不细说了,反正就是直接返回null造成的

 

posted on 2021-03-13 12:27  xingshouzhan  阅读(79)  评论(0编辑  收藏  举报

导航