一 简介
1.1 生活当中的代理案例
- 房屋中介代理
房屋中介从 包租婆手中获取房源 卖给需要租房的人。
- 商品代购(比如卖面膜)
卖面膜的人 从厂家拿面膜 在朋友圈当中宣传 卖给熟人。
1.2 为什么要使用代理
- 对于消费者而言只需要关注自己需要的商品,可以减少成本,不需要去寻找房源或者渠道,
- 对于生产者(包租婆,厂家)只需要关注自己的产品,销售只需要让代理者去处理就好 关注自家
- 对于代理者而言,可以增加类 扩展业务需求 比如收中介费
1.3代理模式在java当中的应用
- 统一了异常处理
- mybatis使用了代理
- spring aop实现了原理
- 日志框架
1.4概述
- 代理模式(Proxy Pattern):是23中设计模式中的一种,属于结构型的模式,指的是一个对象本身不做实际操作,而是通过其他对象来得到自己想要的结果。
- 意义:目标对象只需要关心自己的实现细节,通过代理对象来实现功能的增强,可以扩展目标对象的功能。
- 体现了非常重要的编程思想:不能随意修改源码,如果需要修改源码,通过修改代理的方式来实现功能的扩展。
1.5图示
- 元素组成
- 接口:定义行为和规范
- 被代理类:是目标对象
- 代理类:做功能增强的
- 值得注意的点
- 用户只需要关注接口功能,而不在乎谁提供了功能。
- 接口真正实现这是目标对象,但是他不与用户接触,而是通过代理。
- 代理就是Proxy,由于它实现了接口,所以他能够与用户直接接触
- 用户调用Proxy的时候,Proxy内部调用了目标对象realSubject。所以Proxy是代理者,它可以增强RealSubject的功能操作。
二 静态代理
2.1 案例
通过代理模式实现日志操作
2.2 实现案例
假设有一个大商场,商场有很多的柜台,有一个柜台卖茅台酒并且增加品尝和是否购买的需求。我们进行代码的模拟。
2.2.1 创建接口SellWine
首先得有一个接口,通用的接口是代理模式实现的基础。这个接口我们命名为 SellWine,代表卖酒这个行为。
public interface SellWine {
void maiJiu();
}
2.2.2 创建目标对象MaiTaiJiu类
这个表示真正的对象,它实现了SellWine接口,卖酒的行为有了
(如果按照原始的方式就是在MaoTaiJiu这个对象的每一个方法当中分别添加pinchang()、isGouMai(),就很麻烦 代码冗余,并且如果想要添加增强类的话就需要这个真实对象的源码)
public class MaoTaiJiu implements SellWine{
public void maiJiu() {
System.out.println("我卖的是茅台酒!");
}
public void baoZhuang(){
System.out.println("茅台酒的包装");
}
public void jiancha(){
System.out.println("茅台酒日期的检查");
}
}
2.2.3 创建Proxy GuiTai类
GuiTai就是一个Proxy对象 他有一个maiJiu()方法,不过在调用maiJiu()方法的时候有一些额外的行为比如说pinchang()、isGouMai()
public class GuiTai implements SellWine {
//代理的是MaoTaiJiu这个真实对象 类型固定了 无法代理别的品牌的酒
private MaoTaiJiu maoTaiJiu;
public void setMaoTaiJiu(MaoTaiJiu maoTaiJiu){
this.maoTaiJiu=maoTaiJiu;
}
/* public GuiTai(MaoTaiJiu maoTaiJiu){
this.maoTaiJiu=maoTaiJiu;
}*/
public void maiJiu() {
pinchang();
maoTaiJiu.maiJiu();
goumai(true);
}
public void pinchang(){
System.out.println("客户您可以品尝!");
}
public void goumai(boolean isMai){
if(isMai){
System.out.println("购买结账!");
}else{
System.out.println("在考虑考虑!");
}
}
}
2.2.4 创建客户 Client类
public class Client {
public static void main(String[] args) {
//真实对象: 茅台酒
MaoTaiJiu maoTaiJiu = new MaoTaiJiu();
//代理对象:柜台
GuiTai guiTai = new GuiTai();
//通过代理角色 来实现真实对象茅台酒的卖酒
//1 获取代理的是茅台酒的
guiTai.setMaoTaiJiu(maoTaiJiu);
guiTai.maiJiu();
}
}
结果:
现在可以看到,代理模式可以在不修改被代理对象的基础上,通过扩展代理类,进行一些功能的附加与增强。值得注意的是,代理类和被代理类应该共同实现一个接口,或者是共同继承某个类。
上面介绍的是静态代理的内容,为什么叫做静态呢?因为它的类型是事先预定好的
三 动态代理
上一节代码中 GuiTai类是代理,我们需要手动编写代码让 GuiTai 实现 SellWine 接口,而在动态代理中,我们可以让程序在运行的时候自动在内存中创建一个实现 SellWine 接口的代理,而不需要去定义 GuiTai 这个类。这就是它被称为动态的原因。
3.1 jdk动态代理
3.1.1 案例
3.1.1.1创建接口SellWine
可以理解为卖酒的许可证
public interface SellWine {
void maiJiu();
}
3.1.1.2创建真实对象MaoTaiJiu类
public class MaoTaiJiu implements SellWine{
public void maiJiu() {
System.out.println("我卖的是茅台酒!");
}
public void baoZhuang(){
System.out.println("茅台酒的包装");
}
public void jiancha(){
System.out.println("茅台酒日期的检查");
}
}
3.1.1.3创建ProxyInvocationHandler类
//柜台A代理卖酒
public class GuiTaiA implements InvocationHandler {
//被代理类的接口
/* private SellWine sellWine;*/
//升级一下 卖的酒的品牌不止一家
private Object pingpai;
//增强类
private PinChang pinChang;
//传入InvocationHandler当中
/* public void setSellWine(SellWine sellWine,PinChang pinChang){
this.sellWine=sellWine;
this.pinChang=pinChang;
}*/
public GuiTaiA() {
}
public GuiTaiA(PinChang pinChang) {
this.pinChang = pinChang;
}
public GuiTaiA(Object pingpai) {
this.pingpai = pingpai;
}
public GuiTaiA(Object pingpai, PinChang pinChang) {
this.pingpai = pingpai;
this.pinChang = pinChang;
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object ret=null;
if(pinChang!=null){
pinChang.pinchang();
}
System.out.println("开始售卖,柜台是"+this.getClass().getSimpleName());
ret =method.invoke(pingpai,args);
if(pinChang!=null){
pinChang.goumai(true);
}
System.out.println("售卖结束");
return ret;
}
}
3.1.1.4客户
//有额外的功能
public static void main(String[] args) {
//真实对象: 茅台酒
MaoTaiJiu maoTaiJiu = new MaoTaiJiu();
//增强类
PinChang pinChang=new PinChang();
//要代理哪一个真实对象通过ProxyInvocationHandler
GuiTaiA pih = new GuiTaiA(maoTaiJiu,pinChang);// 获取代理的是茅台酒的
//要强制转换为接口类型
SellWine proxy = (SellWine) Proxy.newProxyInstance(maoTaiJiu.getClass().getClassLoader(),
maoTaiJiu.getClass().getInterfaces(),
pih);
proxy.maiJiu();
}
与静态代理相比,我们并没有像静态代理那样为SellWine接口实现一个代理实现类,而是弄了一个invocationHandler类 (代表这一类的代理类 可以利用里面的newProxyInstance()来创建代理类的实例)
3.1.2jdk动态代理的语法
A Proxy类
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces,
InvocationHandler h)
参数:
- ClassLoader:类加载器
- interface:被代理的接口 (抽象角色的接口)
- h:一个InvocationHandler对象
B InvocationHandler类
InvocationHandler是一个接口。
每个代理的实例都有与之关联的一个InvocationHandler实现类,如果代理的方法被调哦那个,那么代理便会同志和转发给内部的InvocationHandler实现类,由他决定处理。
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
InvocationHandler接口内部只有一个invoke()方法,正是因为这个方法决定了怎么样处理代理传递过来的方法调用。
参数:
- proxy:代理对象
- method:代理对象调用的方法(通过这个method去寻找真实对象的Class的)
- args:调用的方法当中的参数
因为:proxy动态产生的代理会调用InvocationHandler实现类,所以InvocationHandler实现类是实际执行者!
3.1.3 加大难度 真实对象多个!!
我们不仅要卖茅台酒,还要卖五粮液!!!!
添加WuLiangYe类
public class WuLiangYe implements SellWine {
public void maiJiu() {
System.out.println("我卖的是五粮液");
}
}
客户端
public static void main(String[] args) {
//真实对象: 茅台酒
MaoTaiJiu maoTaiJiu = new MaoTaiJiu();
//真实对象:五粮液
WuLiangYe wuLiangYe = new WuLiangYe();
//增强类
PinChang pinChang = new PinChang();
GuiTaiA pihM = new GuiTaiA(maoTaiJiu);// 获取代理的是茅台酒的 这一块空间有标签是放茅台酒的
GuiTaiA pihW = new GuiTaiA(wuLiangYe);// 获取代理的是五粮液的 这一块空间有标签是放五粮液的
SellWine maoTaiJiuProxy = (SellWine) Proxy.newProxyInstance(MaoTaiJiu.class.getClassLoader(),
MaoTaiJiu.class.getInterfaces(), pihM
);
SellWine wuLiangYeProxy = (SellWine) Proxy.newProxyInstance(MaoTaiJiu.class.getClassLoader(),
MaoTaiJiu.class.getInterfaces(), pihW
);//注意反射房中获取Class的几种方法 不要大小写书写错误
maoTaiJiuProxy.maiJiu();
wuLiangYeProxy.maiJiu();
}
}
结果展示:
是一类代理角色,代理角色实例1卖茅台 ,代理角色实例2卖五粮液 !!!这一类的代理角色都是代理SellWine接口的
3.1.4 加大难度 多个抽象角色(比如还想卖烟)
柜台A 还想要卖烟
3.1.4.1创建SellYan接口
public interface SellYan {
void MaiYan();
}
3.1.4.2创建烟的真实角色
public class ZhongHua implements SellYan {
public void MaiYan() {
System.out.println("售卖的是中华烟,可以扫描条形码查证。");
}
}
3.1.4.3 客户
public class Client {
public static void main(String[] args) {
//真实对象
MaoTaiJiu maoTaiJiu = new MaoTaiJiu();
WuLiangYe wuLiangYe = new WuLiangYe();
ZhongHua zhongHua = new ZhongHua();
//增强类
PinChang pinChang = new PinChang();
//代理卖酒的接口
GuiTaiA m = new GuiTaiA(maoTaiJiu);
GuiTaiA w = new GuiTaiA(wuLiangYe);
SellWine maotaijiuProxy = (SellWine) Proxy.newProxyInstance(MaoTaiJiu.class.getClassLoader(), MaoTaiJiu.class.getInterfaces(), m);//卖茅台酒
SellWine wuliangyeProxy = (SellWine)Proxy.newProxyInstance(MaoTaiJiu.class.getClassLoader(), MaoTaiJiu.class.getInterfaces(), w);//卖五粮液
maotaijiuProxy.maiJiu();
wuliangyeProxy.maiJiu();
//代理卖烟的接口
GuiTaiA z = new GuiTaiA(zhongHua);
SellYan zhonghuayanProxy = (SellYan) Proxy.newProxyInstance(ZhongHua.class.getClassLoader(), ZhongHua.class.getInterfaces(), z);//卖中华烟的
zhonghuayanProxy.MaiYan();
}}
结果展示:
结果符合预期。大家仔细观察一下代码,同样是通过 Proxy.newProxyInstance() 方法,却产生了 SellWine 和 SellCigarette 两种接口的实现类代理,这就是动态代理的魔力
四 动态代理的秘密
我们会疑惑为什么Proxy能够动态长生不同接口类型的代理?
猜测可能是因为通过传入的接口==》然后通过反射机制生成了一个接口实例!
比如 SellWine 是一个接口,那么 Proxy.newProxyInstance() 内部肯定会有
new SellWine();
这样相同作用的代码,不过它是通过反射机制创建的。那么事实是不是这样子呢?直接查看它们的源码好了。需要说明的是,我当前查看的源码是 1.8 版本。
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h) throws IllegalArgumentException { Objects.requireNonNull(h); final Class<?>[] intfs = interfaces.clone(); /* * Look up or generate the designated proxy class. */ Class<?> cl = getProxyClass0(loader, intfs); /* * Invoke its constructor with the designated invocation handler. */ try { final Constructor<?> cons = cl.getConstructor(constructorParams); final InvocationHandler ih = h; if (!Modifier.isPublic(cl.getModifiers())) { AccessController.doPrivileged(new PrivilegedAction<Void>() { public Void run() { cons.setAccessible(true); return null; } }); } return cons.newInstance(new Object[]{h}); } catch (IllegalAccessException|InstantiationException e) { throw new InternalError(e.toString(), e); } catch (InvocationTargetException e) { Throwable t = e.getCause(); if (t instanceof RuntimeException) { throw (RuntimeException) t; } else { throw new InternalError(t.toString(), t); } } catch (NoSuchMethodException e) { throw new InternalError(e.toString(), e); } }
newProxyInstance 的确创建了一个实例,它是通过 cl 这个 Class 文件的构造方法反射生成。cl 由 getProxyClass0() 方法获取
private static Class<?> getProxyClass0(ClassLoader loader, Class<?>... interfaces) { if (interfaces.length > 65535) { throw new IllegalArgumentException("interface limit exceeded"); } // If the proxy class defined by the given loader implementing // the given interfaces exists, this will simply return the cached copy; // otherwise, it will create the proxy class via the ProxyClassFactory return proxyClassCache.get(loader, interfaces); }
直接通过缓存获取,如果获取不到,注释说会通过 ProxyClassFactory 生成。
/** * A factory function that generates, defines and returns the proxy class given * the ClassLoader and array of interfaces. */ private static final class ProxyClassFactory implements BiFunction<ClassLoader, Class<?>[], Class<?>> { // Proxy class 的前缀是 “$Proxy”, private static final String proxyClassNamePrefix = "$Proxy"; // next number to use for generation of unique proxy class names private static final AtomicLong nextUniqueNumber = new AtomicLong(); @Override public Class<?> apply(ClassLoader loader, Class<?>[] interfaces) { Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length); for (Class<?> intf : interfaces) { /* * Verify that the class loader resolves the name of this * interface to the same Class object. */ Class<?> interfaceClass = null; try { interfaceClass = Class.forName(intf.getName(), false, loader); } catch (ClassNotFoundException e) { } if (interfaceClass != intf) { throw new IllegalArgumentException( intf + " is not visible from class loader"); } /* * Verify that the Class object actually represents an * interface. */ if (!interfaceClass.isInterface()) { throw new IllegalArgumentException( interfaceClass.getName() + " is not an interface"); } /* * Verify that this interface is not a duplicate. */ if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) { throw new IllegalArgumentException( "repeated interface: " + interfaceClass.getName()); } } String proxyPkg = null; // package to define proxy class in int accessFlags = Modifier.PUBLIC | Modifier.FINAL; /* * Record the package of a non-public proxy interface so that the * proxy class will be defined in the same package. Verify that * all non-public proxy interfaces are in the same package. */ for (Class<?> intf : interfaces) { int flags = intf.getModifiers(); if (!Modifier.isPublic(flags)) { accessFlags = Modifier.FINAL; String name = intf.getName(); int n = name.lastIndexOf('.'); String pkg = ((n == -1) ? "" : name.substring(0, n + 1)); if (proxyPkg == null) { proxyPkg = pkg; } else if (!pkg.equals(proxyPkg)) { throw new IllegalArgumentException( "non-public interfaces from different packages"); } } } if (proxyPkg == null) { // if no non-public proxy interfaces, use com.sun.proxy package proxyPkg = ReflectUtil.PROXY_PACKAGE + "."; } /* * Choose a name for the proxy class to generate. */ long num = nextUniqueNumber.getAndIncrement(); String proxyName = proxyPkg + proxyClassNamePrefix + num; /* * Generate the specified proxy class. */ byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); try { return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length); } catch (ClassFormatError e) { /* * A ClassFormatError here means that (barring bugs in the * proxy class generation code) there was some other * invalid aspect of the arguments supplied to the proxy * class creation (such as virtual machine limitations * exceeded). */ throw new IllegalArgumentException(e.toString()); } } }
这个类的注释说,通过指定的 ClassLoader 和 接口数组 用工厂方法生成 proxy class。 然后这个 proxy class 的名字是
// Proxy class 的前缀是 “$Proxy”, private static final String proxyClassNamePrefix = "$Proxy"; long num = nextUniqueNumber.getAndIncrement(); String proxyName = proxyPkg + proxyClassNamePrefix + num;
所以,动态生成的代理类名称是包名+$Proxy+id序号。
生成的过程,核心代码如下:
byte[] proxyClassFile = ProxyGenerator.generateProxyClass( proxyName, interfaces, accessFlags); return defineClass0(loader, proxyName, proxyClassFile, 0, proxyClassFile.length);
这两个方法,我没有继续追踪下去,defineClass0() 甚至是一个 native 方法。我们只要知道,动态创建代理这回事就好了。
现在我们还需要做一些验证,我要检测一下动态生成的代理类的名字是不是包名+$Proxy+id序号。
public static void main(String[] args) { //真实对象 MaoTaiJiu maoTaiJiu = new MaoTaiJiu(); WuLiangYe wuLiangYe = new WuLiangYe(); ZhongHua zhongHua = new ZhongHua(); //增强类 PinChang pinChang = new PinChang(); //代理卖酒的接口 GuiTaiA m = new GuiTaiA(maoTaiJiu); GuiTaiA w = new GuiTaiA(wuLiangYe); SellWine maotaijiuProxy = (SellWine) Proxy.newProxyInstance(MaoTaiJiu.class.getClassLoader(), MaoTaiJiu.class.getInterfaces(), m);//卖茅台酒 SellWine wuliangyeProxy = (SellWine)Proxy.newProxyInstance(MaoTaiJiu.class.getClassLoader(), MaoTaiJiu.class.getInterfaces(), w);//卖五粮液 maotaijiuProxy.maiJiu(); wuliangyeProxy.maiJiu(); //代理卖烟的接口 GuiTaiA z = new GuiTaiA(zhongHua); SellYan zhonghuayanProxy = (SellYan) Proxy.newProxyInstance(ZhongHua.class.getClassLoader(), ZhongHua.class.getInterfaces(), z);//卖中华烟的 zhonghuayanProxy.MaiYan(); System.out.println("maotaijiuProxy class name:"+maotaijiuProxy.getClass().getName()); System.out.println("wuliangyeProxy class name:"+wuliangyeProxy.getClass().getName()); System.out.println("zhonghuayanProxy class name:"+zhonghuayanProxy.getClass().getName()); }
结果展示:
![]()
SellWine 接口的代理类名是:
com.sun.proxy.$Proxy0
SellYan 接口的代理类名是:com.sun.proxy.$Proxy1
这说明动态生成的 proxy class 与 Proxy 这个类同一个包。
下面用一张图让大家记住动态代理涉及到的角色。
红框中
$Proxy0
就是通过 Proxy 动态生成的。$Proxy0
实现了要代理的接口。$Proxy0
通过调用InvocationHandler
来执行任务。
总结:代理的作用
主要作用,还是在不修改被代理对象的源码上,进行功能的增强。
这在 AOP 面向切面编程领域经常见。
- 代理分为静态代理和动态代理两种。
- 静态代理,代理类需要自己编写代码写成。
- 动态代理,代理类通过 Proxy.newInstance() 方法生成。
- 不管是静态代理还是动态代理,代理与被代理者都要实现两样接口,它们的实质是面向接口编程。
- 静态代理和动态代理的区别是在于要不要开发者自己定义 Proxy 类。
- 动态代理通过 Proxy 动态生成 proxy class,但是它也指定了一个 InvocationHandler 的实现类。
- 代理模式本质上的目的是为了增强现有代码的功能。