1. 什么是代理
以商品的生产、销售为例。现有某品牌U盘的生产工厂,该工厂并不直接销售U盘给我们,而是批发卖给N个商家,然后由商家进行销售。
我们可以理解为,商家就是工厂的代理。商家作为代理,可以实现工厂所不具备的功能,例如:
- 对会员用户进行打折优惠
- 对黑名单用户禁止销售
- 对部分用户赠送优惠券
- ……
2. 静态代理
2.1 静态代理代码示例
仍然以U盘为例,现有如下角色:
- U盘生产工厂:金士顿工厂、闪迪工厂
- 销售商家:金士顿商家、闪迪商家
- 客户(购买者)
接口类 UsbSellService
public interface UsbSellService {
//销售
float sell(int amount);
}
工厂类(目标类)
- 金士顿工厂 UsbKingFactory
public class UsbKingFactory implements UsbSellService {
@Override
public float sell(int amount) {
System.out.println("金士顿工厂卖出数量:" + amount);
return 10f * amount;
}
}
- 闪迪工厂
public class UsbSdFactory implements UsbSellService {
@Override
public float sell(int amount) {
System.out.println("闪迪工厂卖出数量:" + amount);
return 8f * amount;
}
}
商家类(代理类)
- 金士顿商家 UsbKingSeller
public class UsbKingSeller implements UsbSellService {
private UsbSellService usbKingFactory = new UsbKingFactory();
@Override
public float sell(int amount) {
float money = usbKingFactory.sell(amount);
String sellName = UsbKingSeller.class.getSimpleName();
System.out.println(sellName + "销售" + amount + "个,总价:" + money);
System.out.println(sellName + "赠送了10元优惠券");
return money;
}
}
- 闪迪商家 UsbSdSeller
public class UsbSdSeller implements UsbSellService {
private UsbSdFactory usbSdFactory = new UsbSdFactory();
@Override
public float sell(int amount) {
float money = usbSdFactory.sell(amount);
String sellName = UsbSdSeller.class.getSimpleName();
System.out.println(sellName + "销售" + amount + "个,总价:" + money);
System.out.println(sellName + "赠送了8元优惠券");
return money;
}
}
客户类(购买者)
public class CustomerApp {
public static void main(String[] args) {
UsbSellService usbKingSeller = new UsbKingSeller();
int amount = 50;
usbKingSeller.sell(amount);
System.out.println("---------------------------------------");
UsbSellService usbSdSeller = new UsbSdSeller();
usbSdSeller.sell(amount);
}
}
输出结果如下:
金士顿工厂卖出数量:50
UsbKingSeller销售50个,总价:500.0
UsbKingSeller赠送了10元优惠券
---------------------------------------
闪迪工厂卖出数量:50
UsbSdSeller销售50个,总价:400.0
UsbSdSeller赠送了8元优惠券
2.2 静态代理总结
Customer 并不直接操作工厂类,而是操作商家对象,每个商家类依赖了对应的工厂。
2.3 静态代理优缺点
优点
- 实现容易
- 便于理解
缺点
- 上述例子中,如果同一个商家要代理多个目标,则需要编写多个商家代理类
- 当工厂(目标)类增加了,代理类可能需要成倍地增加
- 当接口增加了方法,所有的实现类(目标类、代理类)都要修改
3. 动态代理
3.1 动态代理概念
Java的动态代理,主要由 InvocationHandler 和 Proxy 实现。
- InvocationHandler 负责实现代理需要的功能。主要是调用目标类的方法,以及实现功能增强
- Proxy 根据InvocationHandler,创建对应的代理对象
3.2 动态代理例子
创建 InvocationHandler
public class SellHandler implements InvocationHandler {
private Object target; //代理目标对象
public SellHandler(UsbSellService factory){
this.target = factory;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//调用目标对象方法
float result = (Float)method.invoke(target, args);
//AOP,功能增强
result += 25;
System.out.println("动态代理SellHandler向您赠送优惠券");
return result;
}
}
创建代理对象
public class ProxyCustomerApp {
public static void main(String[] args) {
//1.创建目标对象
UsbSellService factory = new UsbKingFactory();
//2.创建 InvocationHandler 对象
InvocationHandler handler = new SellHandler(factory);
//3.创建代理对象
UsbSellService proxy = (UsbSellService) Proxy.newProxyInstance(
factory.getClass().getClassLoader(),
factory.getClass().getInterfaces(),
handler);
proxy.sell(50);
}
}
4. 原理解析
上面这个例子,我们创建了proxy动态代理对象,通过这个对象执行了InvocationHandler对应的方法。
问题一: Proxy.newProxyInstance 创建的是什么对象,为什么可以强制转换成 UsbSellService 类型?
为了了解这个原理,我们进入 Proxy.newProxyInstance 源码看看。这里我只贴出几行关键代码,其他代码大家打开IDEA详细查看:
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
//1. 生成代理类型 com.sun.proxy.$Proxy0
Class<?> cl = getProxyClass0(loader, intfs);
//2. 获取代理类型构造函数
final Constructor<?> cons = cl.getConstructor(constructorParams);
final InvocationHandler ih = h;
//3. 将InvocationHandler作为参数,实例化 com.sun.proxy.$Proxy0 对象
return cons.newInstance(new Object[]{
h});
}
问题二: com.sun.proxy.$Proxy0 是何方神圣?它是怎么产生的?
我们大胆猜测,com.sun.proxy.$Proxy0 至少是实现了 UsbSellService 接口的类型,否则怎么可能强制转换。
修改之前写的 ProxyCustomerApp ,在第一行加上这句代码,然后重新运行
System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles","true");
此时,在项目根路径下会生成 com\sun\proxy\$Proxy0.class
文件,这是底层调用 new ProxyGenerator().generateProxyClass() 动态创建的类型。将 $Proxy0.class 使用JAD.EXE反编译(直接IDEA打开查看也可以,但是比较不直观)得到 $Proxy0.java 源代码文件,内容如下:
package com.sun.proxy;
public final class $Proxy0 extends Proxy
implements UsbSellService
{
public $Proxy0(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);
}
}
public final int hashCode()
{
try
{
return ((Integer)super.h.invoke(this, m0, null)).intValue();
}
catch(Error _ex) {
}
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
public final float sell(int i)
{
try
{
return ((Float)super.h.invoke(this, m3, new Object[] {
Integer.valueOf(i)
})).floatValue();
}
catch(Error _ex) {
}
catch(Throwable throwable)
{
throw new UndeclaredThrowableException(throwable);
}
}
private static Method m1;
private static Method m2;
private static Method m0;
private static Method m3;
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]);
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m3 = Class.forName("com.train.dynamicproxy.service.UsbSellService").getMethod("sell", new Class[] {
Integer.TYPE
});
}
catch(NoSuchMethodException nosuchmethodexception)
{
throw new NoSuchMethodError(nosuchmethodexception.getMessage());
}
catch(ClassNotFoundException classnotfoundexception)
{
throw new NoClassDefFoundError(classnotfoundexception.getMessage());
}
}
}
重点看2个地方:
- com.sun.proxy.$Proxy0 果然实现了 UsbSellService 接口
- com.sun.proxy.$Proxy0 的 sell() 方法,本质上就是调用 InvocationHandler.invoke 方法