ToB企服应用市场:ToB评测及商务社交产业平台

标题: 【设计模式】署理模式详解 [打印本页]

作者: 张裕    时间: 2024-7-30 17:45
标题: 【设计模式】署理模式详解
1.简介

署理模式是常用的Java设计模式,该模式的特点是署理类与委托类共享雷同的接口。署理类重要负责预处理消息、过滤消息、将消息转发给委托类,并在过后处理消息等。署理类与委托类之间通常存在关联关系,一个署理类对象与一个委托类对象关联。署理类对象自己不真正实现服务,而是通过调用委托类对象的相关方法来提供特定的服务。
署理模式重要包括以下脚色:


2.静态署理

静态署理: 在编译时期确定署理类和目标类的关系,署理类和目标类都要实现同一个接口。
界说一个简单的例子:如果一个租客需要租房子,他可以直接通过房东(委托类) 去租房,也可以颠末中介(署理类) 去租房。房东(realsubject)和中介(proxy)都需要实现subject接口实现房子出租。
首先创建一个Person接口。这个接口是房东和中介的共同接口,租房活动可以被中介署理。
  1. public interface Person {
  2.     // 出租房子
  3.     void hire();
  4. }
复制代码
创建一个委托类,实现subject接口,并编写业务逻辑
  1. public class Landlord implements Person{
  2.         // 房东直售
  3.     @Override
  4.     public void hire() {
  5.         System.out.println("出租,收款1000元");
  6.     }
  7. }
复制代码
创建一个署理类,同样实现subject接口,对委托类的方法进行增强
  1. public class Agency implements Person{
  2.     private final Landlord landlord;
  3.     public Agency(Landlord landlord) {
  4.         this.landlord = landlord;
  5.     }
  6.         // 中介出租,额外收取费用
  7.     @Override
  8.     public void hire() {
  9.         System.out.println("开始办理租房手续");
  10.         landlord.hire();
  11.         System.out.println("额外收取中介费200元");
  12.     }
  13. }
复制代码
  1. public class Main {
  2.     public static void main(String[] args) {
  3.         // 获取代理对象
  4.         Landlord landlord = new Landlord();
  5.         Agency agency = new Agency(landlord);
  6.         // 使用代理方法
  7.         agency.hire();
  8.     }
  9. }
复制代码
输出结果如下,可以发现对方法进行了增强

3.动态署理

动态署理: 在程序运行时动态生成署理类(subject的实现类)。
相比于静态署理, 动态署理的上风在于其较高的灵活性和代码复用性。同一个动态署理处理器可以署理多个目标对象,而静态署理则需要创建大量的署理类。
在Java中,可以通过JDK和CGLIB实现动态署理。
3.1.JDK动态署理

实现原理

JDK动态署理:在java的java.lang.reflect包下提供了Proxy类和InvocationHandler接口,使用这两个类和接口,可以在运行时动态生成指定接口的实现类
   Proxy类就是用来创建一个署理对象的类,在JDK动态署理中我们需要使用其newProxyInstance方法。
  1. public static Object newProxyInstance(
  2.     ClassLoader loader,
  3.     Class<?>[] interfaces,
  4.     InvocationHandler h);
复制代码
这个方法的作用就是创建一个署理类对象,它接收以下三个参数:

   InvocationHandler接口提供了一个invoke方法,当署理对象调用方法时,invoke方法会被调用。通过实现这个接口,可以在方法调用前后添加自界说逻辑。
  1. /**
  2.     * proxy:代理类代理的真实代理对象com.sun.proxy.$Proxy0
  3.     * method:我们所要调用某个对象真实的方法的Method对象
  4.     * args:指代代理对象方法传递的参数
  5.     */
  6. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
复制代码
代码实现

这里我们设计两个接口
  1. public interface HouseServiceA {
  2.     // 出租房子
  3.     void hire();
  4. }
复制代码
  1. public interface HouseServiceB {
  2.     // 转租房子
  3.     void sublet();
  4. }
复制代码
委托类实现这两个接口,而且界说具体的业务逻辑
  1. public class HouseServiceImpl implements HouseServiceA, HouseServiceB {
  2.     @Override
  3.     public void hire() {
  4.         System.out.println("出租房子");
  5.     }
  6.     @Override
  7.     public void sublet() {
  8.         System.out.println("转租房子");
  9.     }
  10. }
复制代码
署理工厂负责在运行时动态生成署理类,需要实现InvocationHandler接口重写invoke方法来做方法增强,使用Proxy类创建署理对象。
  1. /**
  2. * JDK动态代理实现InvocationHandler接口
  3. */
  4. public class HouseFactory implements InvocationHandler {
  5.     private Object target;
  6.     //定义获取代理对象的方法(将目标对象传入进行代理)
  7.     public Object getJDKProxy(Object target){
  8.         //为目标对象target赋值
  9.         this.target = target;
  10.         //JDK动态代理只能针对实现了接口的类进行代理,因此需要传递接口的class
  11.         return Proxy.newProxyInstance(
  12.                 target.getClass().getClassLoader(),
  13.                 new Class<?>[]{HouseServiceA.class, HouseServiceB.class},
  14.                 this);
  15.     }
  16.     @Override
  17.     public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  18.         System.out.println("JDK动态代理开始");
  19.         // 调用invoke方法,result存储该方法的返回值
  20.         Object result = method.invoke(target, args);
  21.         System.out.println("JDK动态代理结束");
  22.         return result;
  23.     }
  24. }
复制代码
在实际使用时,只需要将工厂类生成的署理对象转为需要的署理类,即可实现同时署理多个接口的方法。
  1. public class Main {
  2.     public static void main(String[] args) {
  3.         // 创建代理类工厂
  4.         HouseFactory factory = new HouseFactory();
  5.         // 动态生成接口A的代理类
  6.         HouseServiceA houseProxyA = (HouseServiceA) factory.getJDKProxy(new HouseServiceImpl());
  7.         houseProxyA.hire();
  8.         System.out.println("=====================");
  9.         // 动态生成接口B的代理类
  10.         HouseServiceB houseProxyB = (HouseServiceB) factory.getJDKProxy(new HouseServiceImpl());
  11.         houseProxyB.sublet();
  12.     }
  13. }
复制代码
返回结果:

3.2.CGLIB动态署理

实现原理

CGLIB动态署理:依靠于ASM下的Enhancer类和MethodInterceptor接口,可以在运行时动态生成目标类的子类
   Enhancer类是用来创建署理对象的类。在CGLIB动态署理中,我们需要使用其create方法。
  1. public class Enhancer {
  2.     public Object create();
  3.     // 其他方法
  4. }
复制代码
这个方法的作用是创建一个署理类对象,通常还需要设置以下几个属性:

   MethodInterceptor接口提供了一个intercept方法,当署理对象调用方法时,intercept方法会被调用。通过实现这个接口,可以在方法调用前后添加自界说逻辑。
  1. /**
  2.     * obj: 代理对象
  3.     * method: 被代理的方法
  4.     * args: 方法的参数
  5.     * proxy: 用于调用父类方法的代理
  6.     */
  7. public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable;
复制代码
与JDK动态署理不同,CGLIB署理不需要目标类实现接口。CGLIB通过生成目标类的子类并重写方法来实现署理,因此它可以署理没有实现接口的类。
代码实现

  1. <dependency>
  2.     <groupId>cglib</groupId>
  3.     <artifactId>cglib</artifactId>
  4.     <version>3.3.0</version>
  5. </dependency>
复制代码
  1. public class HouseServiceImpl {
  2.     public void hire() {
  3.         System.out.println("出租房子");
  4.     }
  5.     public void sublet() {
  6.         System.out.println("转租房子");
  7.     }
  8. }
复制代码
  1. public class Main {
  2.     public static void main(String[] args) {
  3.         HouseFactory factory = new HouseFactory();
  4.         HouseServiceImpl cglibProxy = (HouseServiceImpl) factory.getCglibProxy(new HouseServiceImpl());
  5.         cglibProxy.hire();
  6.         System.out.println("=====================");
  7.         cglibProxy.sublet();
  8.     }
  9. }
复制代码
返回结果:

4.总结

静态署理

实现方式:

优点:

缺点:

JDK动态署理

实现方式:

优点:

缺点:

CGLIB动态署理

实现方式:

优点:

缺点:

   实用场景:
  

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4