Loading... ## 静态代理 静态代理其实代理类和被代理类实现同一个接口,然后将被代理类的实例通过构造函数传入代理类,在代理类中,调用接口上的方法时,去调用被代理类的同方法即可,在调用前、后、异常等场景可以加入你的代理逻辑。 如: ```java public class SimpleStaticProxyDemo { static interface IService { public void sayHello(); } static class RealService implements IService { @Override public void sayHello() { System.out.println("hello"); } } static class TraceProxy implements IService { private IService realService; public TraceProxy(IService realService) { this.realService = realService; } @Override public void sayHello() { System.out.println("entering sayHello"); this.realService.sayHello(); System.out.println("leaving sayHello"); } } public static void main(String[] args) { IService realService = new RealService(); IService proxyService = new TraceProxy(realService); proxyService.sayHello(); } } ``` 在这个例子中,`IService` 是公共接口,实际对象是 `RealService`,代理类为 `TraceProxy`,`TraceProxy` 内部持有一个 `IService` 类型的成员变量,指向实际对象,在构造函数中初始化,对于 `sayHello` 的调用,它转发给了实际对象,在调用前后输出了一些跟踪信息。 程序输出为: ``` entering sayHello hello leaving sayHello ``` 静态代理实现了代理模式的核心需求:"**在不修改原类的情况下增强其功能**" 不过,输出跟踪日志是一个很常见的需求,可能不止 `RealService` 需要,如果更多的类需要的话,就要为每个实际对象创建代理,代理要实现所有接口,整个过程非常繁琐。所以就有了动态代理。 ## 动态代理 -JDK 和静态代理不一样,动态代理的代理类是动态生成的,如: ```java public class SimpleJDKDynamicProxyDemo { static interface IService { public void sayHello(); } static class RealService implements IService { @Override public void sayHello() { System.out.println("hello"); } } 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 " + method.getName()); Object result = method.invoke(realObj, args); System.out.println("leaving " + method.getName()); return result; } } public static void main(String[] args) { IService realService = new RealService(); IService proxyService = (IService) Proxy.newProxyInstance(IService.class.getClassLoader(), new Class<?>[] { IService.class }, new SimpleInvocationHandler(realService)); proxyService.sayHello(); } } ``` 只需要创建一个类,实现 `InvocationHandler` 接口的 `invoke` 方法即可,方法第一个参数是**代理类**,也就是生成的代理对象,第二个参数是调用的方法,第三个参数是方法参数。 那么如何调用这个它来生成代理类呢,使用 JDK 自带的 `Proxy.newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)` - `loader`:类加载器,使用和实际类实现的接口的类加载器即可。 - `interfaces`:实际类实现的**接口**列表。 - `h`:就是上面我们实现 `InvocationHandler` 接口的实现类 `SimpleInvocationHandler`。 JDK 动态代理的原理是自动创建一个类,实现的接口为上面的 `interfaces` 中的接口列表,也就是实际类实现的接口列表,生成的代理类代码大概如下: ```java final class $Proxy0 extends Proxy implements SimpleJDKDynamicProxyDemo.IService { private static Method m1; private static Method m3; private static Method m2; private static Method m0; public $Proxy0(InvocationHandler paramInvocationHandler) { super(paramInvocationHandler); } public final boolean equals(Object paramObject) { return ((Boolean) this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue(); } public final void sayHello() { this.h.invoke(this, m3, null); } public final String toString() { return (String) this.h.invoke(this, m2, null); } public final int hashCode() { return ((Integer) this.h.invoke(this, m0, null)).intValue(); } static { m1 = Class.forName("java.lang.Object").getMethod("equals", new Class[] { Class.forName("java.lang.Object") }); m3 = Class.forName("laoma.demo.proxy.SimpleJDKDynamicProxyDemo$IService") .getMethod("sayHello",new Class[0]); m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]); m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]); } } ``` 这个类通过构造方法传入一个 `InvocationHandler` 类型的对象,调用所有方法时,包含 Object 中的 hashCode,equals 和 toString 方法,都会调用 `InvocationHandler` 的 `invoke` 方法,也就是这个类其实只做转发作用。 通过 JDK 的动态代理,可以不用再为每个实际类手动创建一个代理类了,上面我们是获取 `IService` 的一个代理类: ```java IService proxyService = (IService) Proxy.newProxyInstance(IService.class.getClassLoader(), new Class<?>[] { IService.class }, new SimpleInvocationHandler(realService)); ``` 实际上我们也可以获取其他接口实现的代理类,如 `IServiceA`、`IServiceB`: ```java IServiceA proxyService = (IServiceA) Proxy.newProxyInstance(IServiceA.class.getClassLoader(), new Class<?>[] { IServiceA.class }, new SimpleInvocationHandler(realService)); ``` ```java IServiceB proxyService = (IServiceB) Proxy.newProxyInstance(IServiceB.class.getClassLoader(), new Class<?>[] { IServiceB.class }, new SimpleInvocationHandler(realService)); ``` ## 动态代理 - CGLIB <mark style="background: #FFB8EBA6 ;">JDK 动态代理的局限是只能为接口创建代理,返回的代理类也只能转为某个接口实现类</mark>,如果一个类没有接口,或者向代理非接口中定义的方法,就没办法实现了。 cglib 使用其他方式避免了这些局限,如: ```java 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; } } @SuppressWarnings("unchecked") 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) throws Exception { RealService proxyService = getProxy(RealService.class); proxyService.sayHello(); } } ``` 注:上述代码引用了 cglib 的包,以 maven 为例: ```xml <dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.3.0</version> </dependency> ``` RealService 为被代理的类,它没有接口, getProxy 方法接收一个 Class 对象,方法返回一个代理类。 getProxy 方法中使用了 cglib 的 Enhancer 类,Enhancer 类的 superclass 设置成了被代理的类。setCallback 设置被代理类的 public 非 final 方法被调用时的处理类。上例用使用的处理类是 `SimpleInterceptor` 继承 `MethodInterceptor` 并实现 `intercept` 方法。 - 第一个参数是 object,**是代理类对象**。 - 第二个参数是 method,是被调用的方法。 - 第三个参数是 args,是被调用的方法参数。 - 第四个参数是 methodProxy,方法中使用它的 invokeSuper (object, args) 来调用被代理类的方法,注意别写成了 invoke (object, args),不然它调用的是代理类,然后又进入了这个方法,会无限循化。 与 JDK 动态代理不同的是,cglib 的用法,我们在 main 方法中也没有创建被代理类的对象,创建的对象直接就是代理对象。 实现原理: cglib 不同于 jdk 动态代理,**它是通过继承实现的**,它是动态创建了一个类,但这个类的父类是被代理的类,代理类重写了父类的所有 public 非 final 方法,改为调用 callback 中的相关方法,在上例中,调用 SimpleInterceptor 的 intercept 方法。 ## 比较 从原理上来看: - JDK 动态代理面向的是一组接口,它为这些接口创建了一个实现类,接口的具体实现是通过自定义的 InvocationHandler 实现的,也就是说,一个代理类背后都不一定有真正被代理的对象,也可能有多个实际对象,根据情况动态选择。局限性是要求被代理类必须实现了接口。 - CGLIB 动态代理面向的是一个具体的类,它为这个类动态创建一个子类,父类所有 public 非 final 方法,然后在这些方法中调用 MethodInterceptor 的实现,这个实现中定义了代理逻辑。不要求代理类实现接口。 从代理的角度来看: - JDK 动态代理:代理的是对象,需要首先有一个实际对象,然后使用自定义 InvocationHandler 引用该对象。 - CGLIB:只会创建代理类,所有的操作都是针对代理类,无需手动创建实际对象。 ## 参考链接 - https://www.cnblogs.com/swiftma/p/6869790.html 最后修改:2023 年 01 月 08 日 © 允许规范转载 打赏 赞赏作者 支付宝微信 赞 如果觉得我的文章对你有用,请我喝杯咖啡吧。