关于Java动态代理的一点想法

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了关于Java动态代理的一点想法相关的知识,希望对你有一定的参考价值。


如有错误请指正

1.    动态代理的作用

 1. 虚拟机生成的动态代理对象可以轻松地对原有方法进行各种重写

 2. 若没有动态代理,想实现重写,必须做一个继承基类的子类



2.  实例演示:

package com.didi.test;


public interface Person {


    String skill();


    String play();

}



    package com.didi.test;


public class Programmer implements  Person{



    @Override

    public String skill() {

        System.out.println("coding-------------------------");

        return "调用skill方法";

    }


    @Override

    public String play() {

        return null;

    }

}

   

       package com.didi.test;


import java.lang.reflect.InvocationHandler;

import java.lang.reflect.Method;


public class MyInvocationHandler implements InvocationHandler {


    Programmer programmer;


    public MyInvocationHandler(Programmer programmer) {

        this.programmer = programmer;

    }


    @Override

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

        System.out.println("before------------");

        System.out.println(proxy.getClass().getName());

        Object obj =  method.invoke(programmer);

        System.out.println("after----------------------");

        if(!method.getName().equals("skill")){

            return "调用娱乐方法";

        }

        return obj;

    }

}

package com.didi.test;


import java.lang.reflect.Proxy;


public class ProxTest {


    public static void main(String[] args) {

        Programmer p = new Programmer();

        Person programmerProxy = (Person)Proxy.newProxyInstance(

                ProxTest.class.getClassLoader(),

               p.getClass().getInterfaces(),

                new MyInvocationHandler(p));

        System.out.println("动态代理类的父类:" + programmerProxy.getClass().getSuperclass().getName());

        String mes = programmerProxy.play();

        System.out.println(mes);

    }

}

控制台打印:

动态代理类的父类:java.lang.reflect.Proxy

before------------

com.sun.proxy.$Proxy0

after----------------------

调用娱乐方法


3. 底层实现原理


 1. 如下源码:

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

        }

    }


 2. 源码解析

 Proxy.newProxyInstance产生代理类对象,这个方法做了两件重要的事情,首先生成实现传入接口的实现类,然后通过反射创建对象,创建对象时传入一个参数,即我们自己实现的MyInvocationHandler


3. 具体运行情况

当运行代理对象的方法时,虚拟机做如下处理:

1.收集三个参数:代理对象的引用,当前调用方法的方法名,方法的参数

2.调用MyInvocationHandler的invoke方法,参数为上一步收集的参数


4. 实际应用

    1. 日志,事务管理

    2. 远程过程调用RPC,远程调用时,具体实现在服务端,这时我们在invoke中实现通信即可(调用方法时,让远端的方法被调用,将结果发挥即可),这一切都是通过invoke中的socket通信实现的

 

 啰嗦一下:

 通过动态代理可以轻松地对一个类进行方法增强,改造,自定义

 

      


以上是关于关于Java动态代理的一点想法的主要内容,如果未能解决你的问题,请参考以下文章

关于动态代理的一点心得

关于仿照java的内存回收机制实现C++的自动内存回收的一点想法

关于java的动态代理

理解java动态代理

Java动态代理 深度详解

深入浅出Java动态代理