Java动态代理

Java动态代理

代理模式,目前应用广泛的一种设计模式之一,最熟悉的莫过于在Spring的AOP了,如此重要的设计模式,在Java中如何使用呢?又是怎么实现的呢?接下来将围绕着这两个问题来进行实验解析。

首先是如何使用?在使用它的动态代理之前,先实现一个简单的静态代理。创建两个类,分别是PersonPersonProxy,并让这两个类都实现Walkable接口,然后我们通过PersonProxy来代替Person走路。

定义如下:

package top.devonte.stream.proxy;

public interface Walkable {
    void walk();
}

package top.devonte.stream.proxy;

public class People implements Walkable {

    @Override
    public void walk() {
        System.out.println("people is walking...");
    }
}

package top.devonte.stream.proxy;

public class WalkableProxy implements Walkable {
    
    private Walkable walkable;

    public WalkableProxy(Walkable walkable) {
        this.walkable = walkable;
    }

    @Override
    public void walk() {
        System.out.println("proxy make it walk");
        walkable.walk();
        System.out.println("proxy make it walk");
    }
}

package top.devonte.stream.proxy;

public class ProxyTest {
    public static void main(String[] args) {
        Walkable walkable = new WalkableProxy(new People());
        walkable.walk();
    }
}

上面这段代码的输出结果为:

proxy make it walk

people is walking...

proxy make it walk

可以看到,静态代理实际上就是对被代理对象进行了一个包装。而代理类在程序运行时创建的代理方式被称为动态代理,在Java中,为我们提供了Proxy类,我们可以通过Proxy类来创建对应的代理对象。下面来看看在这个动态代理的实验中,能有什么样的收获吧。

首先定义一个Lion类,同样实现了Walkable接口,然后我们定义一个JdkProxyHandler类,实现InvocationHanlder接口。

package top.devonte.stream.proxy;

public class Lion implements Walkable {
    @Override
    public void walk() {
        System.out.println("lion is walking...");
    }
}

package top.devonte.stream.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class JdkProxyHandler implements InvocationHandler {

    private Walkable walkable;

    public JdkProxyHandler(Walkable walkable) {
        this.walkable = walkable;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("current invocation is: " + method.getName());
        System.out.println("invocationHandler start work...");
        Object result = method.invoke(walkable, args);
        System.out.println("invocationHandler work completed...");
        return result;
    }
}

package top.devonte.stream.proxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class ProxyTest {

    public static void main(String[] args) {
        Lion lion = new Lion();
        InvocationHandler handler = new JdkProxyHandler(lion);
        Walkable walkable = (Walkable) Proxy.newProxyInstance(Lion.class.getClassLoader(),
                new Class[]{Walkable.class}, handler);
        System.out.println("======== walkable.equals(lion) ========");
        System.out.println(walkable.equals(lion));
        System.out.println("======== walkableProxyClass.getClassLoader() ========");
        Class<? extends Walkable> walkableProxyClass = walkable.getClass();
        ClassLoader classLoader = walkableProxyClass.getClassLoader();
        System.out.println(classLoader);
        System.out.println("======== walkableProxyClass.getClassLoader() ========");
        Method[] methods = walkableProxyClass.getMethods();
        for (Method method : methods) {
            System.out.println(method.getName());
        }
        System.out.println("======== walkable.walk() ========");
        walkable.walk();
    }

}

上述代码的输出结果为:

======== walkable.equals(lion) ========

current invocation is: equals invocationHandler start work...

invocationHandler work completed... true 

======== walkableProxyClass.getClassLoader() ========

sun.misc.Launcher$AppClassLoader@18b4aac2

======== walkableProxyClass.getMethods() ======== 

equals

toString

hashCode

walk

isProxyClass

getInvocationHandler

getProxyClass

newProxyInstance

wait

wait

wait

getClass

notify

notifyAll

======== walkable.walk() ========

current invocation is: walk

invocationHandler start work...

lion is walking...

invocationHandler work completed...

将一系列的类属性打印出来后,我们可以得出以下几点:

  • 调用对象的equals方法时,实际上调用的是JdkProxyHandlerinvoke方法

  • 使用的类加载器为sun.misc.Launcher$AppClassLoader

  • 包含的方法相对于普通的对象多了isProxyClassgetInvocationHandlergetProxyClassnewProxyInstance

当我们把创建代理对象的代码改成如下形式时,会产生java.lang.IllegalArgumentException: top.devonte.stream.proxy.Lion is not an interface异常:

Lion walkable = (Lion) Proxy.newProxyInstance(Lion.class.getClassLoader(),new Class[]{Lion.class}, handler);

Java的动态代理需要通过接口来实现,如果没有实现相应的接口是没办法创建代理对象的。接着我们通过代理对象来获取代理对象的Class信息,探究代理对象到底是一个怎么样的类。

System.out.println("======== walkable.getClass() ========");
System.out.println(walkable.getClass());

得到的输出如下:

======== walkable.getClass() ========

class com.sun.proxy.$Proxy0

Java给我们动态生成了一个Proxy类,这个类对象是如何创建出来的呢?即Java是如何实现动态代理的呢?我们从获取类对象的方法开始探究。

执行newProxyInstance时,会将我们传入的interfaces进行克隆,接着通过类加载器获取代理类对象,如果可以进行代理,则通过反射获取到这个类的构造器,使用newInstance创建出代理类。

final Class<?>[] intfs = interfaces.clone();
...省略一部分代码...
Class<?> cl = getProxyClass0(loader, intfs);
...省略一部分代码...
return cons.newInstance(new Object[]{h});

分析到这里,我们对Java的动态代理应该有了一个比较深刻的认识,Java通过InvocationHandler来实现了具体的代理功能,通过反射动态生成一个存在于内存中的Proxy类,最终的调用都是通过这个InvocationHandler来实现方法的调用。如果想探究生成的代理类到底是一个怎么样的东西,可以通过反射将代理类输出到文件中,然后通过反编译来查看。下面给出将代理类输出到文件中的方法:

byte[] classFile = ProxyGenerator.generateProxyClass("$Proxy0", Lion.class.getInterfaces());
String path = "LionProxy.class";
try(FileOutputStream fos = new FileOutputStream(path)) {
    fos.write(classFile);
    fos.flush();
    System.out.println("代理类class文件写入成功");
} catch (Exception e) {
    System.out.println("写文件错误");
}

参考资料:

java动态代理实现与原理详细分析(友好版)

JAVA动态代理(详细)

tric

tric