JDK 动态代理

2019-01-05  本文已影响0人  vader哥哥

  准备代码:2个功能接口,一个委托类,一个委托处理器(自己起的名),一个测试类,代码部分可以先跳过。


//  1、篮球接口

public interface BasketBall {

    String playBasketBall ();

    void eatFood (String food);

}

//  2、足球接口

public interface FootBall {



    void playFootBall();

}

//  3、委托类

public class Player implements BasketBall,FootBall {

    @Override

    public String playBasketBall() {

        System.out.println("BasketBall method : playBasketBall");

        return "kobe";

    }

    @Override

    public void eatFood(String food) {

        System.out.println("BasketBall method : eatFood ,param : " + food);

    }

    @Override

    public void playFootBall() {

        System.out.println("FootBall method: playFootBall");

    }

}

//  4、委托处理器,委托处理器的功能是加强委托类,控制对委托类的访问。

public class PlayerHandler implements InvocationHandler {

    private Player player;

    PlayerHandler (Player player) {

        this.player = player;

    }

    @Override

    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

        // 调用前增强逻辑

        System.out.println("proxy : " + proxy.getClass().getName());

        System.out.println("method: " + method.getName());

        if (args != null && args.length > 0) {

            for (Object obj:

                    args) {

                System.out.println("参数:" + obj);

            }

        }

        //  调用为委托类的目标方法

        Object result = method.invoke(player, args);



        //  调用后增强逻辑

        System.out.println("result: " + result);

        return result;

    }

}

//  5、测试类

public class ProxyTest {

    public static void main(String[] args) throws Exception{

        // 委托方

        Player player = new Player();

        // 生成代理对象

        Object obj =  Proxy.newProxyInstance(

                player.getClass().getClassLoader(),

                new Class[]{BasketBall.class,FootBall.class}, new PlayerHandler(player));



        BasketBall basketBallProxy = (BasketBall) obj;

        String re = basketBallProxy.playBasketBall();

        System.out.println("return by playBasketBall method, value: " +re);



        basketBallProxy.eatFood("egg");



        FootBall footBallProxy = (FootBall) obj;

        footBallProxy.playFootBall();



        buildClass();

    }

    //  获取代理类的 class 文件

    public static void  buildClass() throws Exception{

        String fileName = "$pdy_dynamic";

        byte[] proxyClassFile = ProxyGenerator.generateProxyClass(

                fileName, new Class[]{BasketBall.class, FootBall.class});

        String filePath = "/a/b/c/"+ fileName + ".class";

        File file = new File(filePath);

        FileOutputStream fileOutputStream = new FileOutputStream(file);

        fileOutputStream.write(proxyClassFile);

        fileOutputStream.close();

    }

}

//  控制台打印结果

1、调用 BasketBall 接口的 playBasketBall 方法

proxy : com.sun.proxy.$Proxy0

method: playBasketBall

BasketBall method : playBasketBall

result: kobe

return by playBasketBall method, value: kobe

2、调用 BasketBall 接口的 eatFood 方法

proxy : com.sun.proxy.$Proxy0

method: eatFood

参数:egg

BasketBall method : eatFood ,param : egg

result: null

3、调用FootBall 接口的 playFootBall 方法

proxy : com.sun.proxy.$Proxy0

method: playFootBall

FootBall method: playFootBall

result: null

  所有测试代码都在上面了,开始分析:先从测试类的 main 方法入手。


Proxy.newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)

这是 Proxy 类创建代理对象的静态方法,从上面看出这个方法需要3个参数:

  1. 类加载器对象

  2. 代理对象需要实现的接口列表

  3. 委托处理器

前面两个参数没什么好说的了,来看下第三个,所有的委托处理器都要实现 InvocationHandler 接口,这个接口就是用来处理委托类和代理类的业务逻辑的,或者说是用来增强委托类的。接口很简单:


public interface InvocationHandler {

    public Object invoke(Object proxy, Method method, Object[] args)

        throws Throwable;

}

接口里面就只有一个方法,这个方法需要三个参数,第一个是==代理对象==,第二个是一个方法对象,第三个是方法参数,看看前面我写的那个实现类,大概就明白些意思了,后面还会继续介绍。

  接下来进入 Proxy.newProxyInstance 方法里面看看:


    public static Object newProxyInstance(ClassLoader loader,

                                          Class<?>[] interfaces,

                                          InvocationHandler h)

        throws IllegalArgumentException

    {

        ...............

        //  获取代理类的类对象

        Class<?> cl = getProxyClass0(loader, intfs);

        try {

        .................

            //  返回代理实例

            final Constructor<?> cons = cl.getConstructor(constructorParams);

            return cons.newInstance(new Object[]{h});

        }

        ...............

    }

代码太长,只留了需要关注的代码,上面这个 getProxyClass0 方法就是获取代理类的Class类型方法了。


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

    }

代码在上面了,关注下上面的英文说明,大概意思就是:根据给定的类加载器和接口列表定义的代理类如果在内存中存在的话,就返回缓存中的一个副本,否则就通过 ProxyClassFactory 工厂创建。分析到这里,就搞清楚这个代理类从哪里来的了。


    private static final WeakCache<ClassLoader, Class<?>[], Class<?>>

        proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());

这是上面那个 proxyClassCache 对象,在构造方法中看到了 ProxyClassFactory ,这个就是创建代理类 Class 文件的工厂了,WeakCache 这个类,从名字就可以看出,他是用来缓存 ProxyClass 对象的,至于怎么缓存的,这里不是很关心。ProxyClassFactory 是 Proxy 类里面的私有静态工厂类:


private static final class ProxyClassFactory

        implements BiFunction<ClassLoader, Class<?>[], Class<?>>

    {

        // prefix for all proxy class names

        private static final String proxyClassNamePrefix = "$Proxy";

        // next number to use for generation of unique proxy class names

        private static final AtomicLong nextUniqueNumber = new AtomicLong();

        @Override

        public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {

            ...............

            /*

            * Choose a name for the proxy class to generate.

            */

            long num = nextUniqueNumber.getAndIncrement();

            String proxyName = proxyPkg + proxyClassNamePrefix + num;

            /*

            * Generate the specified proxy class.

            */

            byte[] proxyClassFile = ProxyGenerator.generateProxyClass(

                proxyName, interfaces, accessFlags);

            try {

                return defineClass0(loader, proxyName,

                                    proxyClassFile, 0, proxyClassFile.length);

            } catch (ClassFormatError e) {



                throw new IllegalArgumentException(e.toString());

            }

        }

    }

我把这个类主要的部分给贴出来了:在 apply 方法里面 ProxyGenerator 由这个类生成了 ProxyClass 的字节数组,我们看到的代理类的类名都是这种形式出现: $Proxy + 数字。在这里就清楚了吧。来分析下,ProxyGenerator.generateProxyClass 方法:


ProxyGenerator.generateProxyClass(final String var0, Class<?>[] var1, int var2)

ProxyGenerator.generateProxyClass(final String var0, Class<?>[] var1)

这个方法有有一个重载方法,我在前面的 代码5 测试类中有,这里分析下该方法的参数:

1、要创建的这个代理类的类名

2、代理类实现的接口列表

3、第三个参数是该类的访问权限

可以用这个方法生成自己的 ProxyClass 类文件,在我前面的 代码5 测试类,有写。下面就用我自己生成的 ProxyClass 类,来看看代理类究竟是什么样的。


public final class $pdy_dynamic extends Proxy implements BasketBall, FootBall {

    private static Method m1;

    private static Method m4;

    private static Method m3;

    private static Method m5;

    private static Method m2;

    private static Method m0;

    public $pdy_dynamic(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 eatFood(String var1) throws  {

        try {

            super.h.invoke(this, m4, new Object[]{var1});

        } catch (RuntimeException | Error var3) {

            throw var3;

        } catch (Throwable var4) {

            throw new UndeclaredThrowableException(var4);

        }

    }

    public final String playBasketBall() throws  {

        try {

            return (String)super.h.invoke(this, m3, (Object[])null);

        } catch (RuntimeException | Error var2) {

            throw var2;

        } catch (Throwable var3) {

            throw new UndeclaredThrowableException(var3);

        }

    }

    public final void playFootBall() throws  {

        try {

            super.h.invoke(this, m5, (Object[])null);

        } catch (RuntimeException | Error var2) {

            throw var2;

        } catch (Throwable var3) {

            throw new UndeclaredThrowableException(var3);

        }

    }

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

        }

    }

    static {

        try {

            m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));

            m4 = Class.forName("com.pdy.proxy.BasketBall").getMethod("eatFood", Class.forName("java.lang.String"));

            m3 = Class.forName("com.pdy.proxy.BasketBall").getMethod("playBasketBall");

            m5 = Class.forName("com.pdy.proxy.FootBall").getMethod("playFootBall");

            m2 = Class.forName("java.lang.Object").getMethod("toString");

            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 类,实现了BasketBall 和 FootBall 接口,在静态代码块里面生成了代理类接口方法对象,构造方法需要一个 InvocationHandler 对象,初始化父类的构造方法,我们在调用代理对象的方法时,其实就是在调用 InvocationHandler 的 invoke 方法,再看看我前面写的 4、委托处理器代码,和这里面的方法传入参数是不是对应上了。

总结一下:

代理模式的目的就是控制对象的访问,动态代理让我们在处理对象访问时可以动态进行,如何运用好动态代理模式,就看阁下的委托处理器设计的怎么样了。动态代理的实现,是在操作字节码文件的基础上完成的,其他几种方式实现动态代理都是基于字节码的操作,不管过程如何,他们的最终目的都是实现动态代理。

上一篇 下一篇

猜你喜欢

热点阅读