java的代理模式是很常见的一种设计模式,记录一下我的理解。
代理模式主要的思想是将委托类(被代理类)和真正使用委托类逻辑的类做一个解耦。
太官方的原理我也说不好,所以还是说说自己的理解好了。
java的代理模式分两种:
- 静态代理
- 动态代理
我个人认为,如果要理解这种思想,还是主要要搞懂静态代理,这样才能更好地理解这种模式,而动态代理则是调用java的Proxy这个API对静态代理做的一种更灵活的实现吧。
首先,我们以生活场景来说。
假设现在我得朋友圈里有一个做微商的小姐姐分享了一个我喜欢的鞋子我很喜欢,我想买这时直接从她那边下单。她肯定不是自己生产的呀,她手上肯定有一个厂家,我在她这里下单,她直接向厂家下单,甚至货物都直接从厂家发给我,而我并不知道她背后的厂家是谁,不知道他们是怎么生产的,也不知道厂家定价是多少钱。这时我和厂家直接就是解耦的,也就是说厂家如果要调整价格,那么厂家价格调整之后真正到我手上的价格就是看代理姐姐心情了,我也不知道。这时候角色就定了:厂家(被代理类),小姐姐(代理类),我(我就是我咯)
静态代理
思路就是这么个思路,代码的实现我们这就开始:
我们抽象出一个Business类,这个类是不管厂商还是代理都需要的一些公共的特征。比如厂家和小姐姐他们的公共特征那就是卖货,这是他们真正的目的:
package com.example.builderdemo.proxy.staticProxy;
// 这是一个生意抽象出来的类,等于是一些公共的东西,比如做生意就是卖货,卖服务等等这种公共行为
// 不管是厂家还是代理商都要做的一些事情
public interface Business {
long sell();
}
接下来,我们需要把厂商定义出来,我想定义一个国产的厂商,来个安踏吧:
package com.example.builderdemo.proxy.dynmaicProxy;
import com.example.builderdemo.LogUtils;
// 这是厂家类,也就是一个实现了Business类接口的实现类
public class Anta implements Business {
// 厂家可以具有某些属性,比如生产货物,定价等等
// 以此为例的话,其实不一定所有的Business都有生产货物的能力
// 要表达的也只是这些能力并不用被消费者所知道,所以这个对消费者来说是透明的,他们不用关心
private String cargo = "鞋子";// 厂家生产的货物
private long price = 100;// 厂家的定价
@Override
public long sell() {
LogUtils.e("I am a menufacturers, i sell my cargo:" + cargo);
return price;
}
}
厂商肯定具备卖货的特征,这就是我们刚刚抽象出来的接口,所以我们实现Business接口。
接下来得把代理商也就是小姐姐定义出来,代理商同样是要卖货,所以我们也要实现Business接口:
package com.example.builderdemo.proxy.staticProxy;
// 这是一个代理类,这个类代理厂家的贸易,帮厂家进货。
// 代理商也是以做生意卖货为生,所以也是一个Business,是个商人,也具备卖货的属性
// 但是
public class Agent implements Business {
// 每一个代理商手头必须有一个厂家吧,不然他们的货从何而来啊
// 代理商其实就是赚中间的差价,所以如果他黑心一点他就翻倍加价,或者有的代理商热衷慈善他一分钱不赚
Anta anta= null;
// 构造函数中将真正要被代理的类传入,也是一个最简单的依赖注入,这里不展开说
// 现实中也很好理解,以此为例的话就是你要做一个代理商,你必须要代理某一个厂家的产品吧
public Agent(Anta anta) {
this.anta = anta;
}
@Override
public long sell() {
return anta.sell();// 很显然,我们碰上好人了,一分钱价格都没涨,直接卖了
// 但是,现实中往往最缺的就是这种人,如果不加价他怎么养活自己呢,所以我们看到有些较黑心的商人他是这样子的
// long price = anta.sell();
// return price + 100;// 但是不可否认,对于经商来说他很成功,他是个合格的代理
}
}
这样子就满足了java代理模式的三个要素:
- 接口
- 实现接口的委托类,也就是被代理的类。
- 代理类
那么我们怎么买这个鞋子呢?
public void byShooes() {
Anta anta = new Anta();
Agent agent = new Agent(anta);
long price = agent.sell();
LogUtils.e("The shooes is " + price+ " yuan!");
// 到此为止,我们的顾客成功买到了想要的鞋子
}
至此,静态代理搞定了。代理的思想不知道有没有被我曲解,但是我个人是这么理解的。
总的来说,就是:
- 创建一个抽象接口
- 创建抽象接口的真正实现类来实现该抽象接口,也就是委托类(被代理类)
- 创建代理类,代理类也要实现该抽象接口,这样主要是为了能和接口有一样的方法,让用户能无障碍的直接调用相同的方法,感觉就像是调用的就是委托类(被代理类)一样。
- 在代理类中持有委托类(被代理类)的引用。
- 在抽象接口实现过来的方法中直接调用委托类(被代理类)的相对应方法。
但是,从上面你有没有想过,要是我想要特步怎么办,小姐姐这边没有代理他们家的鞋子,那我就得到别处去买了。怎么可能,小姐姐多精啊,能吃这亏?
package com.example.builderdemo.proxy.staticProxy;
import com.example.builderdemo.LogUtils;
// 这是厂家特步的类,也就是一个实现了Business类接口的实现类
public class Tebu implements Business {
private String cargo = "鞋子";// 厂家生产的货物
private long price = 110;// 厂家的定价
@Override
public long sell() {
LogUtils.e("I am a menufacturers, i sell my cargo:" + cargo);
return price;
}
}
接着我们的小姐姐要扩展业务了:
package com.example.builderdemo.proxy.staticProxy;
// 这是一个代理类,这个类代理厂家的贸易,帮厂家进货。
// 代理商也是以做生意卖货为生,所以也是一个Business,是个商人,也具备卖货的属性
// 但是
public class Agent implements Business {
// 每一个代理商手头必须有一个厂家吧,不然他们的货从何而来啊
// 代理商其实就是赚中间的差价,所以如果他黑心一点他就翻倍加价,或者有的代理商热衷慈善他一分钱不赚
Business shooes= null;
// 构造函数中将真正要被代理的类传入,也是一个最简单的依赖注入,这里不展开说
// 现实中也很好理解,以此为例的话就是你要做一个代理商,你必须要代理某一个厂家的产品吧
public Agent(Business shooes) {
this.shooes = shooes;
}
@Override
public long sell() {
return shooes.sell();// 很显然,我们碰上好人了,一分钱价格都没涨,直接卖了
// 但是,现实中往往最缺的就是这种人,如果不加价他怎么养活自己呢,所以我们看到有些较黑心的商人他是这样子的
// long price = shooes.sell();
// return price + 100;// 但是不可否认,对于经商来说他很成功,他是个合格的代理
}
}
我们把接口传递进来,那么这时候不管你什么厂家,管你安踏李宁特步鸿星尔克,通通搞定。
臃肿的静态代理
但是我们又考虑到,不可能客户直接就去找小姐姐买鞋呀,小姐姐得天天打广告,晒截图我们才能选择相信她呀,不能就凭她长得好看就去,这是原则问题。那问题就来了,小姐姐还得打广告,还得晒截图,要是发展着发展着小姐姐想多赚点还得做回访呀,还得做活动呀,各种事情,那么是不是每一种操作都得做一遍:
package com.example.builderdemo.proxy.staticProxy;
// 这是一个代理类,这个类代理厂家的贸易,帮厂家进货。
// 代理商也是以做生意卖货为生,所以也是一个Business,是个商人,也具备卖货的属性
// 但是
public class Agent implements Business {
// 每一个代理商手头必须有一个厂家吧,不然他们的货从何而来啊
// 代理商其实就是赚中间的差价,所以如果他黑心一点他就翻倍加价,或者有的代理商热衷慈善他一分钱不赚
Business shooes = null;
// 构造函数中将真正要被代理的类传入,也是一个最简单的依赖注入,这里不展开说
// 现实中也很好理解,以此为例的话就是你要做一个代理商,你必须要代理某一个厂家的产品吧
public Agent(Business shooes) {
this.shooes = shooes;
}
@Override
public long sell() {
return shooes.sell();// 很显然,我们碰上好人了,一分钱价格都没涨,直接卖了
// 但是,现实中往往最缺的就是这种人,如果不加价他怎么养活自己呢,所以我们看到有些较黑心的商人他是这样子的
// long price = shooes.sell();
// return price + 100;// 但是不可否认,对于经商来说他很成功,他是个合格的代理
}
@Override
public void ad() {
anta.ad();
}
@Override
public long show() {
return anta.show();
}
@Override
public long event() {
return anta.event();
}
...
}
那是不是只要接口加入一个方法我们在委托类也实现这个方法,那代理类也必须得加入这个方法,那么这时候厂家做出各种各样的活动,那么小姐姐就算咱们什么都不改,厂家做什么活动咱照着做什么活动,厂家说打九折咱也不说打九五折了,直接打九折的话代理类也得变呀。那代码是不是又臭又长,里面会有各种的方法。这时候,动态代理出现了
动态代理
动态代理在java中很常见,动态代理与静态代理的区别在于,代理类只需要一个方法就ok,不管你后面厂家出什么幺蛾子,代理类都能通过反射执行对应的方法。
这时我们只需要改变代理类的实现即可,先上代码,后面再来解释:
public class DynmaicAgent implements InvocationHandler {
Object object;
public DynmaicAgent(Object object) {
this.object = object;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(object, args);
if (method.getName().equals("sell")) {
return (long) result + 100;
}
return result;
}
}
从上面看到,我们的代理类需要实现一个叫InvocationHandler的接口,这个接口是jdk中定义的API,里面只有一个方法:
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
根据API中的解释,我尝试着解释了一下这几个参数的含义:
- poxy:委托类(被代理类)的实例
- 委托类(被代理类)被调用到的方法
- “2”中方法所具备的参数
注意:invoke方法的返回值是一个Object,这个方法的返回值既可以是委托类的实例,也可以是执行方法的返回值
同样的,代理类也持有一个委托类(被代理类)的引用。(我感觉这步骤可以不用,而直接使用invoke的proxy参数)
这时候当我要买鞋子的时候:
public void byShooesDynmaic() {
Business anta = new Anta();
DynmaicAgent agent = new DynmaicAgent(anta);
ClassLoader classLoader = anta.getClass().getClassLoader();
Business business = (Business) Proxy.newProxyInstance(classLoader, new Class[]{Business.class}, agent);
long price = business.sell();
LogUtils.e("The shooes is " + price + " yuan!");
}
Proxy:
Proxy类也是jdk里面提供的API,API中对该类的解释是:
这个类提供了静态方法用来创建一个动态代理的和实例,并且它也是所有被该方法创建的所有
的动态代理的的父类。
我们这里常用的方法就是这个Proxy.newProxyInstance,看看API的解释:
这个方法的作用下面有解释:返回一个指定接口的代理的实例,并且该实例调用执行的方法会
分发给对应的invocationHandler去执行。
这里我们的理解是返回一个委托类(被代理类)的实例,并且该类调用的方法会被分发到
参数h的invoke去执行。
接下来我们回到刚才的代码中:
public void byShooesDynmaic() {
Business anta = new Anta();
DynamicAgent agent = new DynamicAgent(anta);
ClassLoader classLoader = anta.getClass().getClassLoader();
Business business = (Business) Proxy.newProxyInstance(classLoader, anta.getClass().getInterfaces(), agent);
long price = business.sell();
LogUtils.e("The shooes is " + price + " yuan!");
}
我们来分析一下上面这段代码:
- 首先,我们创建了一个Business接口类型的变量,并且运用java的多态实际创建的是Anta类型的实例。
- 其次我们创建一个DynamicAgent实例,DynamicAgent的构造传入的是一个Object类型参数。如果这时候我们想要说让DynamicAgent的逻辑更清晰一点,说我们这个动态代理只代理Business这个接口,那么我们传递个Business的实现类做实参这就符合需求了。
- 接着要准备一个委托类(被代理类)的类加载器,anta.getClass().getClassLoader()
- 紧接着获取委托类(被代理类)实现的所有接口。
- 代理类我们已经实现好了,直接传入
- 我们可以看到Proxy.newProxyInstance返回的就是一个委托类(被代理类)的实例,所以我们接下来要做什么操作直接做就行了
当然,我们的动态代理类中的invoke方法可以返回任何能当做返回值的类型,只要我们在内部做好判断就好了,比如:
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(object, args);
if (method.getName().equals("sell")) {
return (long) result + 100;
}
return result;
}
这个invoke中,我们判断如果是sell方法,那么long类型数据再加上100块,如果我们还有一些别的方法比如getName返回字符串,那就是返回字符串,比如getAnta我们要把整个委托类(被代理类)引用返回,那也ok。
至此,动态代理记录完毕
看过两个文章,人家讲的应该更好一点,记录下来下回可以复习:
https://blog.csdn.net/yaomingyang/article/details/80981004
https://blog.csdn.net/yaomingyang/article/details/81040390
动态代理和装饰者模式经常会搞混了,这两天再理解一下装饰者,装饰者和静态代理那边我认
为主要就是差别在sell方法内部,代理模式只是对一些逻辑增加一些功能或者判断什么的,
而装饰者是一层一层得往委托类(被代理类)内调用,并且每个结果又依据委托类(被代理类
)的结果一层一层地装饰。