java动态代理详解

时间:2025-04-26 09:13:50

本篇文章参考以下几篇博客:

/gonjan-blog/p/

/p/4e14dd223897

/benwu/articles/

参考上述博客,本文对java的动态代理机制从底层分析,并融入自己的理解。首先,实现动态代理需要使用包下提供的Proxy类和InvocationHandler接口。使用该机制实现动态代理,被代理的类必须实现某个接口,否则无法实现动态代理。

下面以一个实例来讲解java中的JDK动态代理。

首先定义一个接口类-Person:

//Person接口
public interface Person {
    void giveMoney();
}

接下来实现一个被代理对象类-Student,该类需要实现接口类-Person:

//被代理对象类-Student
public class Student implements Person {
    private String name;

    public Student(String name) {
         = name;
    }

    @Override
    public void giveMoney() {
        try {
            (1000);//假设数钱花了1000ms
        } catch (InterruptedException e) {
            ();
        }
        (name + "上交班费50元");
    }
}

之后,实现一个工具类MonitorUtil,用于测试运行时间(该类不是创建动态代理的必要步骤,只是为了展示动态代理可以在被代理对象的接口函数基础上可以拓展新的功能):

//时间检测工具类
public class MonitorUtil {
    private static ThreadLocal<Long> t1=new ThreadLocal<>();

    public static void start(){
        (());
    }

    public static void finish(String methodName){
        long finishTime=();
        (methodName+"方法耗时"+(())+"ms");
    }
}

接下来,定义一个类(StuInvocationHandler)实现InvocationHandler接口:

//JDK动态代理必须实现InvocationHandler接口
public class StuInvocationHandler implements InvocationHandler {
    //InvocationHandler持有的被代理对象
    private Student student;

    public StuInvocationHandler(Student student) {
         = student;
    }

    /**
     * proxy:代表动态代理对象
     * method:代表正在执行的方法
     * args:代表调用method方法要传入的参数
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //代理对象中插入时间检测
        ("代理执行" + () + "方法");
        ();
        //执行被代理对象的函数  这里使用Method类的invoke方法(反射机制)
        //参1:执行的函数底层所属的对象,通俗理解,就是调用哪个对象的method
        //参2:调用method传入的参数
        Object result = (student, args);
        (());
        return result;
    }
}

最后,定义一个测试类(ProxyTest),执行代理

public class ProxyTest {
    public static void main(String[] args) {
        //创建一个实例对象,表示被代理对象
        Student zhangsan =new Student("张三");
        //使用一个InvocationHandler与被代理对象关联
        InvocationHandler stuHandler=new StuInvocationHandler(zhangsan);
        //创建一个代理对象stuProxy来代理zhangsan,代理对象执行Student中的接口函数会替换执行InvocationHandler中的invoke方法
        Person stuProxy= (Person)((),new Class[]{},stuHandler);
        //代理对象执行被代理对象的接口函数,InvocationHandler中的invoke方法被执行
        ();
    }
}

执行一下main函数,输出:

代理执行giveMoney方法
张三上交班费50元
giveMoney方法耗时1010ms

为什么执行()函数的时候,InvocationHandler中的invoke方法被执行?让我们来分析一下整个动态代理是如下实现的,他的底层是怎样工作的。

首先,我们来看代理对象的生成,通过函数:

 Person stuProxy= (Person)((),new Class[]{},stuHandler);

我们重点关注newProxyInstance函数:

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

        final Class<?>[] intfs = ();
        final SecurityManager sm = ();
        if (sm != null) {
            checkProxyAccess((), loader, intfs);
        }

        /*
         * Look up or generate the designated proxy class.
         */
        Class<?> cl = getProxyClass0(loader, intfs);//得到代理类,这个类文件(class文件)会缓存在java虚拟机中

        /*
         * Invoke its constructor with the designated invocation handler.
         */
        try {
            if (sm != null) {
                checkNewProxyPermission((), cl);
            }

            final Constructor<?> cons = (constructorParams);//获取构造函数
            final InvocationHandler ih = h;
            if (!(())) {
                (new PrivilegedAction<Void>() {
                    public Void run() {
                        (true);
                        return null;
                    }
                });
            }
            return (new Object[]{h});//创建代理对象,将InvocationHandler h传入代理对象
        } catch (IllegalAccessException|InstantiationException e) {
            throw new InternalError((), e);
        } catch (InvocationTargetException e) {
            Throwable t = ();
            if (t instanceof RuntimeException) {
                throw (RuntimeException) t;
            } else {
                throw new InternalError((), t);
            }
        } catch (NoSuchMethodException e) {
            throw new InternalError((), e);
        }
    }

这里我们重点关注我加了注释的3行代码:

Class<?> cl = getProxyClass0(loader, intfs);//得到代理类,这个类文件(class文件)会缓存在java虚拟机中
final Constructor<?> cons = (constructorParams);//获取构造函数
return (new Object[]{h});//创建代理对象,将InvocationHandler h传入代理对象

如果深入研究getProxyClass0函数,我们会发现其会创建类的字节码文件,读取并生成class。

之后我们利用该class文件就可以获取其构造函数,获取完构造函数就可以调用newInstance函数创建代理对象了(这边都是java反射相关的知识)。

我们可以在上面main函数添加代码获取这个class文件,添加代码后的文件为:

public class ProxyTest {
    public static void main(String[] args) throws IOException {
        //创建一个实例对象,表示被代理对象
        Student zhangsan =new Student("张三");
        //使用一个InvocationHandler与被代理对象关联
        InvocationHandler stuHandler=new StuInvocationHandler(zhangsan);
        //创建一个代理对象stuProxy来代理zhangsan,代理对象执行Student中的接口函数会替换执行InvocationHandler中的invoke方法
        Person stuProxy= (Person)((),new Class[]{},stuHandler);
        //代理对象执行被代理对象的接口函数,InvocationHandler中的invoke方法被执行
        ();

        byte[] classFile = ("$Proxy0", ());
        String path = "E:\\java_learn\\out\\production\\proxydemo\\$";//class文件输出目录
        FileOutputStream out = null;
        try{
            out = new FileOutputStream(path);
            (classFile);
            ();
            ("代理类class文件写入成功");
        } catch (Exception e) {
            ("写文件错误");
        }finally {
            ();
        }
    }
}

因此,我们可以获取这个class文件并将其进行反编译,我是在idea中直接打开该class文件,可以得到如下内容(中间添加了我的注释):

import ;
import ;
import ;
import ;

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

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

    public final boolean equals(Object var1) throws  {
        try {
            return (Boolean)(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)(this, m2, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }
    //这里的m3是Student类里的giveMoney,因为这里的h是StuInvocationHandler,而StuInvocationHandler里包含一个Student对象
    public final void giveMoney() throws  {
        try {
            (this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final int hashCode() throws  {
        try {
            return (Integer)(this, m0, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    //静态初始化块,通过反射获取方法giveMoney,令其为m3
    static {
        try {
            m1 = ("").getMethod("equals", (""));
            m2 = ("").getMethod("toString");
            m3 = ("Person").getMethod("giveMoney");
            m0 = ("").getMethod("hashCode");
        } catch (NoSuchMethodException var2) {
            throw new NoSuchMethodError(());
        } catch (ClassNotFoundException var3) {
            throw new NoClassDefFoundError(());
        }
    }
}

也就是说,当我们执行函数时,底层会帮我们创建一个$Proxy0类对象,$Proxy0类继承了Proxy类并实现了Person接口,上述代码中的h为父类Proxy的成员,下面截取Proxy类的部分源码:

public class Proxy implements  {
      protected InvocationHandler h;
      protected Proxy(InvocationHandler h) {
      	(h);
         = h;
    }
}

因此,在主函数中创建的代理对象stuProxy其实是一个$Proxy0对象,只不过该对象实现了Person接口,因此我们执行()函数,其会调用$Proxy0类的giveMoney函数,即

//h为InvocationHandler,在这里就是我们传进去的StuInvocationHandler 所以这里会执行StuInvocationHandler的invoke方法
//因此,m3代表StuInvocationHandler包含的被代理对象Student中的giveMoney方法,由于giveMoney无需传参,所以最后一个参数为null
(this, m3, (Object[])null);

大致到这里整个代理的工作流程已经串接起来了!

再来总结一下代理步骤:

  • 创建一个接口A,声明方法
public interface A{
    method1();
    ...
}
  • 创建一个被代理对象类AImpl,并实现接口方法
public class AImpl implements A{
    //实现接口方法
    method1()
    {
        ...
    }
}
  • 创建一个类AImplInvocationHandler,实现InvocationHandler接口,在该类中要包含一个被代理对象B对象,并实现invoke方法
public class AImplInvocationHandler implements InvocationHandler {
    //InvocationHandler持有的被代理对象
    private AImpl a;
    
    public AImplInvocationHandler(AImpl a) {
    	 = a;
    }
    
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) {
        //可以在此拓展功能代码
        Object result = (a, args);
        //可以在此拓展功能代码
        return result;
    }
}
  • 测试,创建一个被代理对象,使用一个InvocationHandler与被代理对象关联,调用创建代理对象,使用代理对象调用接口函数
  • //创建一个被代理对象
    AImpl a=new AImpl();
    //使用一个InvocationHandler与被代理对象关联
    InvocationHandler handler= new AImplInvocationHandler(a);
    //创建一个代理对象proxy来代理a
    A proxy=(A)((),new Class[]{},handler);
    //代理对象执行被代理对象的接口函数,InvocationHandler中的invoke方法被执行
    proxy.method1();

代理对象持有一个InvocationHandler对象,InvocationHandler对象包含一个被代理的对象(被代理对象中有接口方法),而InvocationHandler包含接口函数invoke方法,大致就是这样!!!