JDK 动态代理

Proxy 和 InvocationHandler

Java 的动态代理需要使用这两个类来实现。这两个类的作用描述如下:

Proxy 的描述

provides static methods for creating dynamic proxy classes and instances, and it is also the superclass of all dynamic proxy classes created by those methods.

为创建的动态代理的类和实例提供静态方法。并且是所有动态代理类的超类。

InvocationHandler 的描述

Processes a method invocation on a proxy instance and returns the result. This method will be invoked on an invocation handler when a method is invoked on a proxy instance that it is associated with.

在代理实例上处理方法调用并返回结果。当在与其关联的代理实例上调用方法时,将在 InvocationHandler 上调用此方法。

实现一个动态代理

定义类和接口

// 业务类
public interface Service {
    void add();
}
// 业务类的接口
public class ServiceImpl implements Service {
    @Override
    public void add() {
        System.out.println("It is service impl.");
    }
}

定义代理类

public class ServiceProxy implements InvocationHandler {
        // 被代理的对象
    private Object target;
    public ServiceProxy(Object target) {
        this.target = target;
    }
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 执行方法调用之前要做的事情
        System.out.println("---- before ----");
        Object res = method.invoke(target, args);
        // 执行方法调用之后要做的事情
        System.out.println("---- after ----");
        return res;
    }

    // 生成代理的对象
    public Object getProxy() {
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        Class[] interfaces = target.getClass().getInterfaces();
        // 这里会生成一个代理类,并且返回该代理类的一个实例对象
        return Proxy.newProxyInstance(loader, interfaces, this);
    }
}

使用方式

public class Main {
    public static void main(String[] args) {
            // 被代理的对象
        Service object = new ServiceImpl();
        ServiceProxy serviceProxy = new ServiceProxy(object);
        // 代理的对象
        Service service1 = (Service) serviceProxy.getProxy();
        service1.add();
    }
}

方法调用之后打印的结果如下:

---- before ----
It is service impl.
---- after ----

实现原理

Proxy.newProxyInstance

这个方式是实现代理类的关键。做的事情如下:

  • 首先检查 Proxy.proxyClassCache 是否已有代理类

  • 有:则直接返回;没有:使用内部类 ProxyClassFactory 创建

  • native方法 Proxy.defineClass0 负责字节码加载的实现,并返回对应的Class对象

  • 利用 clazz.newInstance 反射机制生成代理类的对象

ProxyClassFactory 中生成代理类字节码的方法如下:

// proxyName:格式如 "com.sun.proxy.$Proxy0";
// interfaces:代理类需要实现的接口数组;
// accessFlags:代理类的访问标识;
byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);

生成的代理类

我们可以保存生成的字节码到 class 文件,并通过反编译工具得到相应的 java 文件,具体如下:

// 可以看到,代理类的超类为 Proxy ,并且实现了被代理类具有的接口
public final class $proxy0 extends Proxy implements Service {

        // 这里传入了定义的 InvocationHandler 
    public $proxy1(InvocationHandler invocationhandler) {
        super(invocationhandler);
    }

    public final boolean equals(Object obj) {
        try {
            return ((Boolean)super.h.invoke(this, m1, new Object[] {
                obj
            })).booleanValue();
        }
        catch(Error _ex) { }
        catch(Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final String toString() {
        try {
            return (String)super.h.invoke(this, m2, null);
        }
        catch(Error _ex) { }
        catch(Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    // 代理类里面也有一个 add 方法
    // 执行代理对象的方法,就是执行 InvocationHandle 对象的 invoke 方法,传入的参数分别是当前代理对象,当前执行的方法和参数
    public final void add() {
        try {
            // h: 传入的 InvocationHandler 类的实例
            // this: 代理对象
            // 调用的方法
            // 传入的参数(这里没有,所以为 null)
            super.h.invoke(this, m3, null);
            return;
        }
        catch(Error _ex) { }
        catch(Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    public final int hashCode() {
        try {
            return ((Integer)super.h.invoke(this, m0, null)).intValue();
        }
        catch(Error _ex) { }
        catch(Throwable throwable) {
            throw new UndeclaredThrowableException(throwable);
        }
    }

    private static Method m1;
    private static Method m2;
    private static Method m3;
    private static Method m0;

    static {
        try {
            m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] {
                Class.forName("java.lang.Object")
            });
            m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
            m3 = Class.forName("zzzzzz.Service").getMethod("add", new Class[0]);
            m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
        }
        catch(NoSuchMethodException nosuchmethodexception) {
            throw new NoSuchMethodError(nosuchmethodexception.getMessage());
        }
        catch(ClassNotFoundException classnotfoundexception) {
            throw new NoClassDefFoundError(classnotfoundexception.getMessage());
        }
    }
}

局限性

jdk动态代理是通过反射类 ProxyInvocationHandler 回调接口实现的。过程中要求被代理类必须实现一个接口,但事实上并不是所有类都有接口,对于没有实现接口的类,便无法使用该方方式实现动态代理。这种情况可以使用 cglib 实现。

参考文章

说说Java代理模式

Java高级 ----> Java动态代理的原理

你可能感兴趣的:(JDK 动态代理)