1 定义
代理模式,为想要访问的对象创建一个代理,使访问原对象变为访问代理对象。代理模式可以提供非常好的访问控制。生活中最多的代理模式例子就是中介。
2 代理模式结构与实现
代理模式通用类图如下所示(来自《大话设计模式》):
- Subject
抽象主题角色:可以是抽象类,也可以是接口。抽象主题是一个普通的业务类型,无特殊要求。 - RealSubject
具体主题角色:也叫做被委托角色或被代理角色,是业务逻辑的具体执行者。 - Proxy
代理主题角色:也叫做委托类或代理类。它负责对真实角色的应用,把所有抽象主题类定义的方法限制委托给真实主题角色实现,并且在具体主题角色处理完毕前后做预处理和善后处理工作。
一个代理类可以代理多个被委托者或被代理者,因此一个代理类具体代理哪个具体主题角色,是由场景类决定的。最简单的情况是一个主题类和一个代理类。通常情况下,一个接口只需要一个代理类,具体代理哪个实现类有高层模块决定。
3 代理模式的优点
职责清晰
具体角色是实现具体的业务逻辑,不用关心其他非本职责的事务,通过后期的代理完成一件事务,代码清晰。在某些情况下,一个客户类不想或者不能直接引用一个委托对象,而代理类对象可以在客户类和委托对象之间起到中介的作用,其特征是代理类和委托类实现相同的接口。
高扩展性
具体主题角色随时会发生变化,但是只要实现了接口,接口不变,代理类就可以不做任何修改继续使用,符合“开闭原则”。
另外,代理类除了是客户类和委托类的中介之外,我们还可以通过给代理类增加额外的功能来扩展委托类的功能,这样做我们只需要修改代理类而不需要再修改委托类,同样符合开闭原则。
4 静态代理
所谓静态也就是在程序运行前就已经存在代理类的字节码文件,代理类和真实主题角色的关系在运行前就确定了。淘宝商家卖货品,就是一个代理,用户想要买东西的时候,直接找淘宝卖家下单,就能买到心仪的货品。
4.1 卖货品接口Sell
/**
* @program: design-pattern-learning
* @author: zgr
* @create: 2021-09-08 09:58
**/
public interface Sell {
/**
* 卖手套
*/
String sellGloves();
/**
* 卖袜子
*/
String sellSocks();
/**
* 卖鞋子
*/
String sellShoes();
}
4.2 源商家YONEX
/**
* @program: design-pattern-learning
* @author: zgr
* @create: 2021-09-08 10:08
**/
public class Yonex implements Sell {
@Override
public String sellGloves() {
return "卖尤尼克斯的手套";
}
@Override
public String sellSocks() {
return "卖尤尼克斯的袜子";
}
@Override
public String sellShoes() {
return "卖尤尼克斯的鞋子";
}
}
4.3 淘宝代理TaoBaoProxy
/**
* @program: design-pattern-learning
* @author: zgr
* @create: 2021-09-08 10:10
**/
public class TaoBaoProxy implements Sell{
private final Yonex yonex;
private String COMMON_PEDDLE = "我是淘宝代理,";
public TaoBaoProxy(){
yonex = new Yonex();
}
@Override
public String sellGloves() {
return COMMON_PEDDLE + yonex.sellGloves();
}
@Override
public String sellSocks() {
return COMMON_PEDDLE + yonex.sellSocks();
}
@Override
public String sellShoes() {
return COMMON_PEDDLE + yonex.sellShoes();
}
}
4.4 买家Buyer
/**
* @program: design-pattern-learning
* @author: zgr
* @create: 2021-09-08 10:24
**/
public class Buyer {
private TaoBaoProxy taoBao;
public Buyer(){
taoBao = new TaoBaoProxy();
}
public void buyGloves(){
System.out.println("想要买手套");
System.out.println(taoBao.sellGloves());
}
public void buySocks(){
System.out.println("想要买袜子");
System.out.println(taoBao.sellSocks());
}
public void buyShoes(){
System.out.println("想要买鞋子");
System.out.println(taoBao.sellShoes());
}
}
4.5 主函数MainClass
/**
* @program: design-pattern-learning
* @author: zgr
* @create: 2021-09-08 09:56
**/
public class MainClass {
public static void main(String[] args) {
Buyer buyer = new Buyer();
buyer.buyGloves();
System.out.println("-------------------------------------");
buyer.buySocks();
System.out.println("-------------------------------------");
buyer.buyShoes();
System.out.println("-------------------------------------");
}
}
4.6 运行结果
4.7 总结
静态代理模式在不改变目标对象的前提下,实现了对目标对象的功能扩展。不足之处是静态代理实现了目标对象的所有方法,一旦目标接口增加方法,代理对象和目标对象都要进行相应的修改,增加维护成本。
5 动态代理
代理类在程序运行时创建的代理方式被成为动态代理。 我们上面静态代理的例子中,代理类(TaoBaoProxy)是自己定义好的,在程序运行之前就已经编译完成。然而动态代理,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。相比于静态代理, 动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类中的方法。
本文介绍基于JDK的动态代理和基于CGLIB的动态代理。
5.1 基于JDK的动态代理
JDK动态代理,利用java.lang.reflect包下提供的一个Proxy类和一个InvocationHandler接口,Proxy用于动态生成代理类,InvocationHandler定义了实现代理类的方法。
代码实现中,业务与静态代理的业务相同,现在动态代理李宁旗下的鞋子袜子和手套。
5.1.1 卖货接口MySell
/**
* @program: design-pattern-learning
* @author: zgr
* @create: 2021-09-08 09:58
**/
public interface MySell {
/**
* 卖手套
*/
String sellGloves();
/**
* 卖袜子
*/
String sellSocks();
/**
* 卖鞋子
*/
String sellShoes();
}
5.1.2 李宁LiNing
/**
* @program: design-pattern-learning
* @author: zgr
* @create: 2021-09-08 10:08
**/
public class LiNing implements MySell {
@Override
public String sellGloves() {
return "卖李宁的手套";
}
@Override
public String sellSocks() {
return "卖李宁的袜子";
}
@Override
public String sellShoes() {
return "卖李宁的鞋子";
}
}
5.1.3 实现InvocationHandler
/**
* @program: design-pattern-learning
* @author: zgr
* @create: 2021-09-08 11:55
**/
public class MyInvocationHandler implements InvocationHandler {
private MySell mySell;
public MyInvocationHandler(MySell mySell){
this.mySell = mySell;
}
/**
*执行代理方法
*
* @param proxy 被动态代理的对象
* @param method 代理执行的方法
* @param args 执行的参数
* @return method方法执行返回的参数
* @throws Throwable 异常
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("我是JDK淘宝代理");
return method.invoke(mySell, args);
}
}
5.1.4 用户买Buyer
/**
* @program: design-pattern-learning
* @author: zgr
* @create: 2021-09-08 10:24
**/
public class Buyer {
MySell mySell = new LiNing();
public void buyGloves(){
MyInvocationHandler myInvocationHandler = new MyInvocationHandler(mySell);
MySell proxy = (MySell) Proxy.newProxyInstance(myInvocationHandler.getClass().getClassLoader(), mySell.getClass().getInterfaces(), myInvocationHandler);
Object result = proxy.sellGloves();
System.out.println(result);;
}
public void buySocks(){
MyInvocationHandler myInvocationHandler = new MyInvocationHandler(mySell);
MySell proxy = (MySell) Proxy.newProxyInstance(myInvocationHandler.getClass().getClassLoader(), mySell.getClass().getInterfaces(), myInvocationHandler);
Object result = proxy.sellSocks();
System.out.println(result);;
}
public void buyShoes(){
MyInvocationHandler myInvocationHandler = new MyInvocationHandler(mySell);
MySell proxy = (MySell) Proxy.newProxyInstance(myInvocationHandler.getClass().getClassLoader(), mySell.getClass().getInterfaces(), myInvocationHandler);
Object result = proxy.sellShoes();
System.out.println(result);;
}
}
5.1.5 主程序
public class MainClassJdk {
public static void main(String[] args) {
Buyer buyer = new Buyer();
buyer.buyGloves();
System.out.println("-------------------------------------");
buyer.buySocks();
System.out.println("-------------------------------------");
buyer.buyShoes();
System.out.println("-------------------------------------");
}
}
5.1.6 运行结果
5.2 基于CGLIB的动态代理
CGLIB是一个功能强大,高性能的代码生成包。它为没有实现接口的类提供代理,为JDK的动态代理提供了很好的补充。通常可以使用Java的动态代理创建代理,但当要代理的类没有实现接口或者为了更好的性能,CGLIB是一个好的选择。
代码实现的业务需求类似于JDK动态代理,现在买胜利的鞋子袜子和手套。
5.2.1 添加jar包
首先需要再idea添加cglib的jar包以及cglib依赖的asm jar包,jar包都可以在maven仓库中进行下载。
5.2.2 商铺类Victor
/**
* @program: design-pattern-learning
* @author: zgr
* @create: 2021-09-09 11:37
**/
public class Victor {
public String sellGloves() {
return "卖胜利的手套";
}
public String sellSocks() {
return "卖胜利的袜子";
}
public String sellShoes() {
return "卖胜利的鞋子";
}
}
5.2.3 CGLIB拦截器MyInterceptor
/**
* @program: design-pattern-learning
* @author: zgr
* @create: 2021-09-09 11:40
**/
public class MyInterceptor implements MethodInterceptor {
/**
* 拦截方法
*
* @param o 代理的目标对象
* @param method 目标对象的方法
* @param objects 参数
* @param methodProxy CGlib方法代理对象
* @return 代理方法返回值
* @throws Throwable 异常
*/
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("我是CGLIB淘宝代理");
return methodProxy.invokeSuper(o, objects);
}
}
5.2.4 买家Buyer
/**
* @program: design-pattern-learning
* @author: zgr
* @create: 2021-09-09 11:43
**/
public class Buyer {
public void buyGloves(){
Victor victor = getVictor();
System.out.println(victor.sellGloves());
}
public void buySocks(){
Victor victor = getVictor();
System.out.println(victor.sellSocks());
}
public void buyShoes(){
Victor victor = getVictor();
System.out.println(victor.sellShoes());
}
private Victor getVictor(){
Enhancer enhancer =new Enhancer();
enhancer.setSuperclass(Victor.class);
enhancer.setCallback(new MyInterceptor());
return (Victor) enhancer.create();
}
}
5.2.5 主程序
/**
* @program: design-pattern-learning
* @author: zgr
* @create: 2021-09-09 11:48
**/
public class MainClassCglib {
public static void main(String[] args) {
Buyer buyer = new Buyer();
buyer.buyGloves();
System.out.println("-------------------------------------");
buyer.buySocks();
System.out.println("-------------------------------------");
buyer.buyShoes();
System.out.println("-------------------------------------");
}
}
5.2.6 执行结果
6 总结
本文主要介绍了设计模式中的代理模式,通过实际例子,编写代码实现了Java的静态代理,基于JDK的动态代理与基于CGLIB的动态代理。本文算是一个引入文章,后面背后强大的原理与代码分析,欢迎大家一起研究,一起进步。
7 引用
1.《大话设计模式》
8 源码
https://github.com/airhonor/design-pattern-learning/tree/main/src/com/hz/design/pattern/proxy