Java常用设计模式-代理模式

简介: Java常用设计模式-代理模式

代理模式是一种设计模式,其意义在于生成一个占位,来代替真实的对象,从而控制对真实对象的访问。 其实在现实生活中,代理很容易理解。假设这样一个场景:你的公司是一家服装公司,客户来定制服装,肯定不是直接找公司的裁缝谈的,而是去找商务谈,这时客户就会认为商务就代表公司。

网络异常,图片无法展示
|

可见这是一个间接的过程,那么商务(代理对象)的意义何在?商务可以进行一些额外逻辑例如谈判、价格等等,也就是说,代理的作用是:在委托对象访问之前或者之后加入对应逻辑,或者根据其它条件判断是否要访问委托对象,这个场景很显然商务控制了客户对裁缝的访问。

那么我们的代码示例也将以此为场景,一一讲解各个代理模式。

在讲解之前,我们先定义衣服实体类衣服制作接口裁缝类(实现衣服制作接口) 这几个基本类:

衣服实体类:

packagecom.example.javaproxy.model;
importlombok.Getter;
importlombok.Setter;
/*** 衣服类*/@Getter@SetterpublicclassClothing {
/*** 衣服名*/privateStringname;
/*** 尺码(S,M,L,XL,XXL)*/privateStringsize;
}

衣服制作接口:

packagecom.example.javaproxy.service;
importcom.example.javaproxy.model.Clothing;
/*** 衣服制作接口*/publicinterfaceClothesMaking {
/*** 衣服制作* @param name 衣服名* @param size 衣服大小* @return 衣服实例*/ClothingmakeClothing(Stringname, Stringsize);
}

裁缝类(实现衣服接口,为委托对象):

packagecom.example.javaproxy.service.impl;
importcom.example.javaproxy.model.Clothing;
importcom.example.javaproxy.service.ClothesMaking;
/*** 裁缝(委托类)*/publicclassTailorimplementsClothesMaking {
@OverridepublicClothingmakeClothing(Stringname, Stringsize) {
Clothingclothing=newClothing();
clothing.setName(name);
clothing.setSize(size);
returnclothing;
   }
}

1,静态代理

静态代理其实很简单,相当于直接创建代理人类,间接地访问裁缝类。

网络异常,图片无法展示
|

我们直接上代码,代理人(类):

packagecom.example.javaproxy.proxy.staticproxy;
importcom.example.javaproxy.model.Clothing;
importcom.example.javaproxy.service.ClothesMaking;
importcom.example.javaproxy.service.impl.Tailor;
/*** 裁缝代理人(代理类,静态代理)*/publicclassTailorStaticAgentimplementsClothesMaking {
/*** 代理人的裁缝实例(委托对象)*/privateClothesMakingtailor=newTailor();
/*** 向代理人定制衣服** @param name   衣服名字* @param size 客户身高* @return 衣服*/@OverridepublicClothingmakeClothing(Stringname, Stringsize) {
System.out.println("[进入静态代理逻辑]");
System.out.println("裁缝代理人已接到需求!");
returntailor.makeClothing(name, size);
   }
}

然后在主方法测试:

ClothesMakingagent=newTailorStaticAgent();
ClothingcustomClothing=agent.makeClothing("衬衫", "XL");
System.out.println("得到衣服名:"+customClothing.getName() +";得到衣服尺码:"+customClothing.getSize());

结果:

网络异常,图片无法展示
|

可见静态代理非常容易理解,就是新建一个代理类,利用这个代理类(裁缝的代理人) 去访问 委托类(裁缝),客户只需要找代理人即可间接地访问裁缝。

可见静态代理中,代理类和委托类需要实现同一个接口,且静态代理是一种编译后再代理的模式,不太利于扩展。

2,动态代理

上述例子可见,静态代理的代理类编译后也变为class文件,而动态代理是在运行时动态生成代理对象的,在运行时动态生成类字节码,并加载到JVM中。

(1) JDK动态代理

利用JDK自带的API实现在内存中创建动态的代理对象,我们无需向上面静态代理一样建立代理类,只需要建立一个代理逻辑类,这个逻辑类需要使用InvocationHandler接口,在逻辑类里面写下代理方法逻辑,并将代理对象和委托对象建立联系即可,然后这个代理逻辑类就可以生成动态代理对象。

网络异常,图片无法展示
|

这里贴上代理逻辑类的代码:

packagecom.example.javaproxy.proxy.jdkproxy;
importcom.example.javaproxy.service.ClothesMaking;
importjava.lang.reflect.InvocationHandler;
importjava.lang.reflect.Method;
importjava.lang.reflect.Proxy;
/*** JDK动态代理逻辑实现*/publicclassJDKProxyBindimplementsInvocationHandler {
/*** 代理人的裁缝代理实例(委托对象)*/privateClothesMakingtailor;
/*** 将委托对象和代理对象建立关系** @param origin 委托对象* @return 代理对象*/publicObjectbind(ClothesMakingorigin) {
this.tailor=origin;
returnProxy.newProxyInstance(tailor.getClass().getClassLoader(), tailor.getClass().getInterfaces(), this);
   }
/*** 代理逻辑方法** @param proxy  代理对象* @param method 当前调度方法* @param args   当前方法参数* @return 代理结果* @throws Throwable 异常*/@OverridepublicObjectinvoke(Objectproxy, Methodmethod, Object[] args) throwsThrowable {
System.out.println("[进入JDK动态代理逻辑]");
System.out.println("裁缝代理人已接到需求!");
// 调用裁缝(委托类)的方法Objectresult=method.invoke(tailor, args);
System.out.println("衣服定制完成!");
returnresult;
   }
}

然后去主方法测试:

// 首先创建JDK代理逻辑实现类的实例,用于将代理对象和委托对象联系起来JDKProxyBindjdkProxyBind=newJDKProxyBind();
// 新建裁缝实例,绑定关系ClothesMakingtailor=newTailor();
ClothesMakingagent= (ClothesMaking) jdkProxyBind.bind(tailor);
// 这个时候agent对象已经是一个代理对象了,它会进入代理的逻辑方法invoke中去ClothingcustomClothing=agent.makeClothing("衬衫", "XL");
System.out.println("得到衣服名:"+customClothing.getName() +";得到衣服尺码:"+customClothing.getSize());

结果:

网络异常,图片无法展示
|

可见JDK动态代理,并没有手动建立代理类,只是写了代理人的代理逻辑部分。其主要分为两大步:

  1. 建立代理对象和委托对象的关系:在上述我们建立了代理逻辑类,其中定义了bind方法,方法中首先用类的属性tailor保存了委托对象,然后通过Proxy类的newProxyInstance方法,建立代理对象。这个方法有三个参数:
  • 第一个是类加载器,使用委托类的加载器
  • 第二个是要把生成的动态代理对象挂载至哪个接口之下,这里很显然就是要挂在委托类的接口下
  • 第三个是定义实现方法逻辑的代理类,this表示当前对象。代理逻辑类必须实现InvocationHandler接口的invoke方法,这个方法就是代理逻辑方法的实现方法。 我们就将代理逻辑放在本类
  1. 实现代理方法:在代理逻辑类的invoke方法中实现代理逻辑,该方法三个参数意义如下:
  • proxy 代理对象,就是bind方法生成的对象
  • method 当前调度的方法
  • args 调度方法的参数

我们使用了代理方法调度方法之后,就会进入到invoke方法中去。

实现了代理逻辑类,在主方法中使用时,可见只需要使用代理逻辑类将裁缝(委托类)绑定关系,就可以直接生成代理对象,执行代理逻辑。

(2) CGLib动态代理

我们发现,无论是静态代理还是JDK动态代理,都需要提供接口。若在不能提供接口的环境中,我们可以使用CGLib代理。它只需要提供一个委托类就能实现动态生成代理对象实现代理。

首先我们需要在Maven中引入CGLib的依赖:

<!-- CGLib代理 --><dependency><groupId>cglib</groupId><artifactId>cglib</artifactId><version>3.3.0</version></dependency>

然后也是创建一个代理逻辑类,这个和JDK动态代理有点类似。

网络异常,图片无法展示
|

还是贴上代理逻辑类的代码:

packagecom.example.javaproxy.proxy.cglibproxy;
importnet.sf.cglib.proxy.Enhancer;
importnet.sf.cglib.proxy.MethodInterceptor;
importnet.sf.cglib.proxy.MethodProxy;
importjava.lang.reflect.Method;
/*** CGLib动态代理逻辑实现*/publicclassCGLibProxyBindimplementsMethodInterceptor {
/*** 生成CGLib代理对象** @param cls 真实对象的类* @return 代理对象*/publicObjectgetProxy(Classcls) {
// CGLib增强类型对象Enhancerenhancer=newEnhancer();
// 设定委托类为父类enhancer.setSuperclass(cls);
// 设定代理逻辑类为本类enhancer.setCallback(this);
// 生成并返回代理对象returnenhancer.create();
   }
/*** 代理逻辑方法** @param proxy       代理对象* @param method      方法* @param args        方法参数* @param methodProxy 方法代理* @return 代理逻辑返回* @throws Throwable 异常*/@OverridepublicObjectintercept(Objectproxy, Methodmethod, Object[] args, MethodProxymethodProxy) throwsThrowable {
System.out.println("[进入CGLib动态代理逻辑]");
System.out.println("裁缝代理人已接到需求!");
// 调用裁缝(委托类)的方法Objectresult=methodProxy.invokeSuper(proxy, args);
System.out.println("衣服定制完成!");
returnresult;
   }
}

然后在主方法测试:

// 首先创建CGLib代理逻辑类实例,把代理对象和委托对象联系起来CGLibProxyBindcgLibProxyBind=newCGLibProxyBind();
// 生成裁缝的代理对象Tailoragent= (Tailor) cgLibProxyBind.getProxy(Tailor.class);
// 这个时候agent已经是一个代理对象了,尽管它是Tailor类ClothingcustomClothing=agent.makeClothing("衬衫", "XL");
System.out.println("得到衣服名:"+customClothing.getName() +";得到衣服尺码:"+customClothing.getSize());

结果:

网络异常,图片无法展示
|

可见这里用了CGLib的加强者Enhancer,通过设定超类为委托类,并设定本类为代理逻辑类(代理逻辑类需要实现MethodInterceptor接口),来生成动态代理对象。

可见在使用上面,CGLib动态代理和JDK动态代理还是很相似的。

不过这里需要提醒一下,使用JDK9及其以上版本执行CGLib动态代理会抛出异常java.lang.ExceptionInInitializerError,因为在JDK高版本中,--illegal-access选项默认为deny,这会导致深度反射失败。

可以使用JDK8版本来实现CGLib代理。

若要使用JDK9及其以上版本,那么就修改运行JVM参数--illegal-access的值为warn(不过这样还是会弹出警告)。

命令行运行加上参数即可:

java --illegal-access=warn -jar xxx.jar

使用IDEA的话,在运行配置中设定运行参数:

网络异常,图片无法展示
|

网络异常,图片无法展示
|

网络异常,图片无法展示
|

网络异常,图片无法展示
|

最后应用、确定即可!

3,总结

代理模式就是一种间接访问的模式,在很多地方其实都有着应用。

静态代理很简单,不过需要手动定义代理类,不利于业务扩展。

动态代理只需要实现代理方法逻辑即可,在运行过程中就会动态地生成代理对象,常用的就是JDK动态代理和CGLib动态代理。

而JDK动态代理和CGLib动态代理最大的区别就是,前者需要依赖接口而后者不需要。JDK动态代理的实质是通过反射代理方法,而CGLib是通过生成类字节码实现代理。

示例程序仓库地址

相关文章
|
20天前
|
设计模式 消息中间件 搜索推荐
Java 设计模式——观察者模式:从优衣库不使用新疆棉事件看系统的动态响应
【11月更文挑战第17天】观察者模式是一种行为设计模式,定义了一对多的依赖关系,使多个观察者对象能直接监听并响应某一主题对象的状态变化。本文介绍了观察者模式的基本概念、商业系统中的应用实例,如优衣库事件中各相关方的动态响应,以及模式的优势和实际系统设计中的应用建议,包括事件驱动架构和消息队列的使用。
|
1月前
|
设计模式 Java 数据库连接
Java编程中的设计模式:单例模式的深度剖析
【10月更文挑战第41天】本文深入探讨了Java中广泛使用的单例设计模式,旨在通过简明扼要的语言和实际示例,帮助读者理解其核心原理和应用。文章将介绍单例模式的重要性、实现方式以及在实际应用中如何优雅地处理多线程问题。
36 4
|
2月前
|
设计模式 Java 程序员
[Java]23种设计模式
本文介绍了设计模式的概念及其七大原则,强调了设计模式在提高代码重用性、可读性、可扩展性和可靠性方面的作用。文章还简要概述了23种设计模式,并提供了进一步学习的资源链接。
52 0
[Java]23种设计模式
|
1月前
|
设计模式 JavaScript Java
Java设计模式:建造者模式详解
建造者模式是一种创建型设计模式,通过将复杂对象的构建过程与表示分离,使得相同的构建过程可以创建不同的表示。本文详细介绍了建造者模式的原理、背景、应用场景及实际Demo,帮助读者更好地理解和应用这一模式。
|
2月前
|
设计模式 监控 算法
Java设计模式梳理:行为型模式(策略,观察者等)
本文详细介绍了Java设计模式中的行为型模式,包括策略模式、观察者模式、责任链模式、模板方法模式和状态模式。通过具体示例代码,深入浅出地讲解了每种模式的应用场景与实现方式。例如,策略模式通过定义一系列算法让客户端在运行时选择所需算法;观察者模式则让多个观察者对象同时监听某一个主题对象,实现松耦合的消息传递机制。此外,还探讨了这些模式与实际开发中的联系,帮助读者更好地理解和应用设计模式,提升代码质量。
Java设计模式梳理:行为型模式(策略,观察者等)
|
3月前
|
存储 设计模式 安全
Java设计模式-备忘录模式(23)
Java设计模式-备忘录模式(23)
|
3月前
|
设计模式 存储 算法
Java设计模式-命令模式(16)
Java设计模式-命令模式(16)
|
3月前
|
设计模式 存储 缓存
Java设计模式 - 解释器模式(24)
Java设计模式 - 解释器模式(24)
|
3月前
|
设计模式 安全 Java
Java设计模式-迭代器模式(21)
Java设计模式-迭代器模式(21)
|
3月前
|
设计模式 缓存 监控
Java设计模式-责任链模式(17)
Java设计模式-责任链模式(17)