对于使用过 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
.
但这种方式有着明显的缺点:
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
中的代理逻辑.
上面讲到的 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 更细节上的内容.