转载

Java 动态代理初探

对于使用过 Spring 的朋友, 应该都使用过 AOP, 那么今天我们来对 AOP 的原理: 动态代理 来一探究竟.

静态代理

在动态代理之前, 我们先来看看静态代理, 看下其实现方式及其优缺点.

静态代理的原理是委托类和代理类都实现同一个接口, 代理类中会调用委托类的方法, 同时代理类中可以加一些自己的逻辑.

公共接口

public interface IService {
    public void sayHello();
}

委托类

public class RealService implements IService {
    @Override
    public void sayHello() {
        System.out.println("hello");
    }
}

代理类

public class ProxyService implements IService {

    private IService iService;

    public ProxyService(IService iService) {
        this.iService = iService;
    }

    @Override
    public void sayHello() {
        System.out.println("before...");
        iService.sayHello();
        System.out.println("after...");
    }
}

测试

public class Main {

    public static void main(String[] args) {
        IService realService = new RealService();
        IService proxyService = new ProxyService(realService);
        proxyService.sayHello();
    }
}

输出:

before...
hello
after...

可以看到委托类和代理类都实现了同一个接口, 然后代理类在初始化时, 传入委托类对象, 然后在代理类自己的 sayHello() 方法中, 即调用了委托类的 sayHello() 方法, 还加了自己的逻辑, 输出了 before 和 after .

但这种方式有着明显的缺点:

  • 必须要继承一个接口
  • 必须要手工创建对应的代理类
  • 硬编码, 当接口需要改动或代理类较多时不宜维护.

动态代理

JDK 动态代理

公共接口

public interface IService {
    public void sayHello();
}

委托类

public class RealService implements IService {
    @Override
    public void sayHello() {
        System.out.println("hello");
    }
}

代理类

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

public class SimpleInvocationHandler implements InvocationHandler {

    private Object realObject;

    public SimpleInvocationHandler(Object realObject) {
        this.realObject = realObject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        System.out.println("before " + method.getName());
        Object result = method.invoke(realObject, args);
        System.out.println("leaving " + method.getName());
        return result;
    }
}

测试

import java.lang.reflect.Proxy;

public class Main {
    
    public static void main(String[] args) {

        // 保存生成的代理类的字节码文件
        System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");

        IService realService = new RealService();
        IService proxyService = (IService) Proxy.newProxyInstance(
                IService.class.getClassLoader(),
                new Class<?>[]{IService.class},
                new SimpleInvocationHandler(realService));
        proxyService.sayHello();
    }
}

代码看起来好像更复杂了一些, 我们可以看到 IService 和 RealService 的定义不变, 但创建代理类的方式变化了, 它使用 java.lang.reflect.Proxy 的静态方法 newProxyInstance 来创建代理类.

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

它有三个参数:

  • loader 表示类加载器.
  • interfaces 表示代理类要实现的接口列表, 元素的类型只能是接口.
  • h 就是上面我们定义的 SimpleInvocationHandler 它实现了 InvocationHandler 接口, 并复写了 invoke 方法, 对代理接口的所有方法调用都会转到此方法上.

newProxyInstance 的返回值是 Object , 可以强制转化为 interfaces 数组中的任意接口类型, 但不能转化为某个普通类型, 如 RealService . 即使它代理的实际对象是 RealService .

接着我们在来看看 SimpleInvocationHandler , 它实现了 InvocationHandler 接口, 它通过构造方法传入被代理对象 realObject . 复写的 invoke 方法有三个参数:

proxy
method
args

需要注意这句代码:

Object result = method.invoke(realObject, args);
``

千万不能把 proxy 当成 method.invoke 的第一个参数. 这样会造成死循环, 因为这样表示代理类代理了它自身.


#### 原理解析

刚才的测试类中, 我们有一段代码, 它用来保存生成后的代理类的 class 文件: 

```java
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true")

我们来反编译看下, 它为我们动态生成的代理类:

package com.sun.proxy;

import im.zhaojun.jdk_proxy.IService;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;

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

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

    public final boolean equals(Object var1)  {
        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 sayHello()  {
        try {
            super.h.invoke(this, m3, (Object[])null);
        } catch (RuntimeException | Error var2) {
            throw var2;
        } catch (Throwable var3) {
            throw new UndeclaredThrowableException(var3);
        }
    }

    public final String toString()  {
        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()  {
        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"));
            m3 = Class.forName("im.zhaojun.jdk_proxy.IService").getMethod("sayHello");
            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());
        }
    }
}

可以看到, 其实也就是生成了一个类, 实现了我们传入的接口, 它所有方法都是调用的 SimpleInvocationHandler 的 invoke 方法.

相比于静态代理, 这里的动态代理看起来麻烦了不少, 但它却更加通用. 我们不用为每个被代理的类都创建一个静态代理类, 而是当代理类要做的功能不变时, 只需要有这一个代理类即可. 说起来可能有些不好理解, 看代码吧:

public class GeneralProxyDemo {
    static interface IServiceA {
        public void sayHello();
    }

    static class ServiceAImpl implements IServiceA {

        @Override
        public void sayHello() {
            System.out.println("hello");
        }
    }

    static interface IServiceB {
        public void fly();
    }

    static class ServiceBImpl implements IServiceB {

        @Override
        public void fly() {
            System.out.println("flying");
        }
    }

    static class SimpleInvocationHandler implements InvocationHandler {
        private Object realObj;

        public SimpleInvocationHandler(Object realObj) {
            this.realObj = realObj;
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
            System.out.println("entering " + realObj.getClass().getSimpleName() + "::" + method.getName());
            Object result = method.invoke(realObj, args);
            System.out.println("leaving " + realObj.getClass().getSimpleName() + "::" + method.getName());
            return result;
        }
    }

    @SuppressWarnings("unchecked")
    private static <T> T getProxy(Class<T> intf, T realObj) {
        return (T) Proxy.newProxyInstance(intf.getClassLoader(), new Class<?>[] { intf },
                new SimpleInvocationHandler(realObj));
    }

    public static void main(String[] args) throws Exception {
        IServiceA a = new ServiceAImpl();
        IServiceA aProxy = getProxy(IServiceA.class, a);
        aProxy.sayHello();

        IServiceB b = new ServiceBImpl();
        IServiceB bProxy = getProxy(IServiceB.class, b);
        bProxy.fly();
    }
}

在这里有两个接口 ServiceA 和 ServiceB , 他们对应的实现类为 ServiceAImpl 和 ServiceBImpl . 虽然他们的接口和实现类完全不同, 但通过动态代理. 他们都可以使用 SimpleInvocationHandler 中 invoke 中的代理逻辑.

CGLIB 动态代理

上面讲到的 JDK 动态代理, 有一定的局限性, 那就是只能为接口创建代理, 返回的对象也只能是接口类型的, 如果一个类没有接口, 或者想代理 非接口中定义的方法 , JDK 动态代理就无法实现了. 这里就要用到 CGLIB 动态代理了.

import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

import java.lang.reflect.Method;

public class SimpleCGLibDemo {

    /**
     * 被代理类.
     */
    static class RealService {
        public void sayHello() {
            System.out.println("hello");
        }
    }

    /**
     * 方法拦截器.
     */
    static class SimpleInterceptor implements MethodInterceptor {

        @Override
        public Object intercept(Object object, Method method,
                Object[] args, MethodProxy proxy) throws Throwable {
            System.out.println("entering " + method.getName());
            Object result = proxy.invokeSuper(object, args);
            System.out.println("leaving " + method.getName());
            return result;
        }
    }

    private static <T> T getProxy(Class<T> cls) {
        Enhancer enhancer = new Enhancer();
        enhancer.setSuperclass(cls);                    // 设置被代理类
        enhancer.setCallback(new SimpleInterceptor());  // 设置方法拦截器
        return (T) enhancer.create();
    }

    public static void main(String[] args) {
        RealService proxyService = getProxy(RealService.class);
        proxyService.sayHello();
    }
}

RealService 表示被代理的类, 它没有实现任何接口. getProxy() 方法为一个类生成代理对象, 这个代理对象可以转换为被代理类的类型, 它使用了 cglib 的 Enhancer 类, Enhancer 类的 setSuperclass 设置被代理的类, setCallback 设置被代理类的方法被调用时的处理类, Enhancer 支持多种类型, 这里使用的类实现了 MethodInterceptor 接口, 它与 JDK 动态代理中的 InvocationHandler 有点类似, 方法名称变成了intercept, 多了一个MethodProxy类型的参数.

与前面的 InvocationHandler 不同, SimpleInterceptor 中没有被代理的对象,它通过 MethodProxy 的 invokeSuper 方法调用被代理类的方法:

Object result = proxy.invokeSuper(object, args);

注意,它不能这样调用被代理类的方法:

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

object 是代理对象,不能自己代理自己,会造成死循环。

基本的使用就这些, 先消化下, 自己动手实现, 后续我会更新一些 JDK 更细节上的内容.

原文  http://www.zhaojun.im/java-dynamic-proxy/
正文到此结束
Loading...