什么是代理
代理就是客户类不再直接和委托类打交道, 而是通过一个中间层来访问, 这个中间层就是代理。为啥要这样呢, 是因为使用代理有两个优势:
- 可以隐藏委托类的实现
-
可以实现客户与委托类之间的解耦, 在不修改委托类代码的情况下能够做一些额外的处理
静态代理
首先, 定义接口和接口的实现类, 然后定义接口的代理对象, 将接口的实例注入到代理对象中, 然后通过代理对象去调用真正的实现类,实现过程非常简单也比较容易理解, 静态代理的代理关系在编译期间就已经确定了的。它适合于代理类较少且确定的情况。它可实现在怒修改委托类代码的情况下做一些额外的处理,比如包装礼盒,实现客户类与委托类的解耦。
HelloService接口
public interface HelloService {
String sayHello(String content);
}
HelloServiceImpl实现类
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String content) {
return "hello " + content;
}
}
HelloServiceProxy代理类
public class HelloServiceProxy implements HelloService {
private HelloService helloService;
public HelloServiceProxy(HelloService helloService) {
this.helloService = helloService;
}
@Override
public String sayHello(String content) {
System.out.println("before invoke");
String result = helloService.sayHello(content);
System.out.println("after invoke");
return result;
}
}
测试类
public class StaticProxyTest {
public static void main(String[] args) {
String content = "static proxy";
HelloService helloService = new HelloServiceImpl();
HelloServiceProxy proxy = new HelloServiceProxy(helloService);
System.out.println(proxy.sayHello(content));
}
}
输出结果
before invoke
after invoke
hello static proxy
JDK动态代理
在Java实现动态代理, 主要依赖java.lang.reflect包下的Proxy和InvocationHandler两个类,具体步骤如下:
- 创建一个实现接口InvocationHandler的类,它必须实现invoke方法
- 创建被代理的类以及接口
- 通过Proxy的newProxyInstance静态方法创建一个代理
- 通过代理调用方法
HelloService接口
public interface HelloService {
String sayHello(String content);
}
HelloServiceImpl实现类
public class HelloServiceImpl implements HelloService {
@Override
public String sayHello(String content) {
return "hello " + content;
}
}
MyInvocationHandler调用器
public class MyInvocationHandler implements InvocationHandler {
// 真实对象
private Object obj;
public MyInvocationHandler(Object obj) {
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before invoke......");
// 此处通过反射,调用真实对象的相关方法
Object result = method.invoke(obj, args);
System.out.println("after invoke......");
return result;
}
}
测试类
public class DynamicProxyTest {
public static void main(String[] args) {
// 通过此配置,会把生成的代理类保存在项目中
System.setProperty("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
String content = "dynamic proxy";
// 创建真实实现对象
HelloService helloService = new HelloServiceImpl();
// 创建调用器
InvocationHandler handler = new MyInvocationHandler(helloService);
// 创建代理对象
Object proxy = Proxy.newProxyInstance(helloService.getClass().getClassLoader(), helloService.getClass()
.getInterfaces(), handler);
System.out.println("代理类名称:" + proxy.getClass().getName());
System.out.println("代理类接口:" + proxy.getClass().getInterfaces()[0].getName());
// 类型转换
HelloService proxyService = (HelloService) proxy;
// 执行代理逻辑
System.out.println(proxyService.sayHello(content));
}
}
输出结果
代理类名称:com.sun.proxy.$Proxy0
代理类接口:com.sankuai.meituan.zcm.depot.proxy.HelloService
before invoke......
after invoke......
hello dynamic proxy
动态代理分析
$Proxy0代理类源码
public final class $Proxy0 extends Proxy implements HelloService {
private static Method m1;
private static Method m3;
private static Method m2;
private static Method m0;
public $Proxy0(InvocationHandler var1) throws {
super(var1);
}
public final boolean equals(Object var1) throws {
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 String sayHello(String var1) throws {
try {
return (String)super.h.invoke(this, m3, new Object[]{var1});
} catch (RuntimeException | Error var3) {
throw var3;
} catch (Throwable var4) {
throw new UndeclaredThrowableException(var4);
}
}
public final String toString() throws {
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() throws {
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("com.sankuai.meituan.zcm.depot.proxy.HelloService").getMethod("sayHello", Class.forName("java.lang.String"));
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());
}
}
}
通过上述源码,动态代理关系可以理解为两层静态代理关系组合,第一层代理关系是$Proxy0和MyInvocationHandler关于invoke()方法组成的静态代理,第二层代理关系是MyInvocationHandler和HelloServiceImpl关于sayHello()方法组成的代理关系。调用流程如下:
第一层静态代理
// 创建代理类
Object proxy = Proxy.newProxyInstance(helloService.getClass().getClassLoader(), helloService.getClass().getInterfaces(), handler);
// 调用代理类sayHello()方法,底层会调用hanlder的invoke()方法
proxyService.sayHello(content)
// $Proxy0的sayHello()方法,此处的h便是创建代理类时传入的handler
public final String sayHello(String var1) throws {
return (String)super.h.invoke(this, m3, new Object[]{var1});
}
第二次代理
// 设置创建并真实对象
HelloService helloService = new HelloServiceImpl();
InvocationHandler handler = new MyInvocationHandler(helloService);
// MyInvocationHandler.invoke方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before invoke......");
// 此处调用真实对象的sayHello()方法
Object result = method.invoke(obj, args);
System.out.println("after invoke......");
return result;
}
CGLIB动态代理
CGLIB代理是针对类来实现代理的,原理是对指定的委托类生成一个子类并重写其中业务方法来实现代理。代理类对象是由Enhancer类创建的。
- 查找目标类上的所有非final的public类型的方法 (final 的不能被重写)
- 将这些方法的定义转成字节码
- 将组成的字节码转换成相应的代理的Class对象然后通过反射获得代理类的实例对象
- 实现MethodInterceptor接口, 用来处理对代理类上所有方法的请求
JDK 动态代理和 CGLIB 动态代理的区别
- 实现方式:JDK 动态代理基于 Java 反射机制实现, 必须要实现了接口的业务类才能用这种方法生成代理对象。CGLIB 动态代理基于 ASM 框架通过生成业务类的子类来实现。
- 实现难度:JDK 动态代理的优势是最小化依赖关系,减少依赖意味着简化开发和维护并且有 JDK 自身支持。还可以平滑进行 JDK 版本升级,代码实现简单。基于 CGLIB 框架的优势是无须实现接口,达到代理类无侵入,我们只需操作我们关系的类,不必为其它相关类增加工作量,性能比较高。