当前位置:   article > 正文

[SSM]GoF之代理模式

[SSM]GoF之代理模式

目录

十四、GoF之代理模式

14.1对代理模式的理解

14.2静态代理

14.3动态代理

14.3.1JDK动态代理

14.3.2CGLIB动态代理


十四、GoF之代理模式

14.1对代理模式的理解

  • 场景:拍电影的时候,替身演员去代理演员完成表演。这就是一个代理模式。

  • 演员为什么要找替身呢?(为什么要使用代理模式?)

    • 怕自己受伤。(保护自己)

    • 自己完成不了高难度动作。(功能增强)

  • 在java程序中代理模式的作用:

    • 当一个对象需要受到保护的时候,可以考虑使用代理对象去完成某个行为。

    • 需要给某个对象的功能进行增强的时候,可以考虑找一个代理进行增强。

    • A对象无法和B对象直接交互,也可以使用代理模式来解决。

  • 代理模式中有三个角色:

    • 目标对象(演员)

    • 代理对象(替身演员)

    • 目标对象和代理对象的公共接口。(演员和替身演员应该具有相同的行为动作)

  • 为什么演员和替身演员要有相同的行为动作呢?

    • 不想让观众知道是替身演员,这里的观众其实就是“客户端程序”。

  • 使用代理模式,对于客户端程序来说,客户端是无法察觉到的,客户端在使用代理对象的时候就像在使用目标对象。

  • 代理模式是GoF23种设计模式之一,属于结构化设计模式。

 

  • 代理模式在代码实现上,包括两种形式:

    • 静态代理

    • 动态代理

14.2静态代理

OrderService接口

  1. package com.hhb.proxy.service;
  2. //订单业务接口
  3. public interface OrderService {//代理对象和目标对象的公共接口
  4.    //生产订单
  5.    void generate();
  6.    //修改订单信息
  7.    void modify();
  8.    //查看订单详情
  9.    void detail();
  10. }

OrderServiceImpl

  1. package com.hhb.proxy.service;
  2. public class OrderServiceImpl implements OrderService {
  3.    /**
  4.     * 问题:统计所有业务接口的每一个业务方法的耗时。
  5.     * 解决方案一:硬编码,在每一个业务接口中的每一个业务方法中直接添加统计耗时的程序
  6.     * 缺点:1.违背OCP开闭原则 2.代码没有得到复用
  7.     * 解决方案二:编写业务类的子类,让子类继承业务类,对每个业务方法进行重写
  8.     * 缺点:1.虽然解决了OCP开闭原则,但是代码耦合度很高,因为采用了继承关系。
  9.     *     2.代码没有得到复用
  10.     * 解决方案三:代理模式
  11.     * 优点:1.解决了OCP问题 2.采用代理模式的has a,可以降低耦合度
  12.     *
  13.     * 目前使用的是静态代理,这个静态代理的缺点是:类爆炸
  14.     * 解决方法:使用动态代理模式来解决这个问题。
  15.     * 动态代理还是代理模式,只不过添加了字节码生成技术,可以在内存中为我们动态生成一个class字节码,这个字节码就是代理类
  16.     * 在内存中动态生成字节码代理类的技术叫做:动态代理
  17.     */
  18.    @Override
  19.    public void generate() {//目标方法
  20.        //模拟生成订单的耗时
  21.        try {
  22.            Thread.sleep(2000);
  23.       } catch (InterruptedException e) {
  24.            e.printStackTrace();
  25.       }
  26.        System.out.println("订单已生成");
  27.   }
  28.    @Override
  29.    public void modify() {//目标方法
  30.        //模拟修改订单的耗时
  31.        try {
  32.            Thread.sleep(1000);
  33.       } catch (InterruptedException e) {
  34.            e.printStackTrace();
  35.       }
  36.        System.out.println("订单已修改");
  37.   }
  38.    @Override
  39.    public void detail() {//目标方法
  40.        //模拟查询订单的耗时
  41.        try {
  42.            Thread.sleep(500);
  43.       } catch (InterruptedException e) {
  44.            e.printStackTrace();
  45.       }
  46.        System.out.println("请看订单详情");
  47.   }
  48. }

OrderServiceProxy

  1. package com.hhb.proxy.service;
  2. //代理对象(代理对象和目标对象要具有相同的行为,就要实现同一个或同一些接口)
  3. //客户端在使用代理对象的时候就像在使用目标对象一样。
  4. public class OrderServiceProxy implements OrderService {
  5.    //将目标对象作为代理对象的一个属性,这种关系叫做关联关系,比继承关系的耦合度低
  6.    //代理对象中含有目标对象的引用。关联关系:has a
  7.    //注意:这里要写一个公共接口类型,因为公共接口耦合度低
  8.    private OrderService target;//这就是目标对象,目标对象一定实现了OrderService接口
  9.    //创建代理对象的时候,传一个目标对象给代理对象。
  10.    public OrderServiceProxy(OrderService target) {
  11.        this.target = target;
  12.   }
  13.    @Override
  14.    public void generate() {//代理方法
  15.        //增强
  16.        long begin = System.currentTimeMillis();
  17.        //调用目标对象的目标方法
  18.        target.generate();
  19.        long end = System.currentTimeMillis();
  20.        System.out.println("耗时" + (end - begin) + "");
  21.   }
  22.    @Override
  23.    public void modify() {
  24.        long begin = System.currentTimeMillis();
  25.        target.modify();
  26.        long end = System.currentTimeMillis();
  27.        System.out.println("耗时" + (end - begin) + "");
  28.   }
  29.    @Override
  30.    public void detail() {
  31.        long begin = System.currentTimeMillis();
  32.        target.detail();
  33.        long end = System.currentTimeMillis();
  34.        System.out.println("耗时" + (end - begin) + "");
  35.   }
  36. }

客户端

  1. package com.hhb.proxy.client;
  2. import com.hhb.proxy.service.OrderServiceImpl;
  3. import com.hhb.proxy.service.OrderServiceProxy;
  4. public class Test {
  5.    public static void main(String[] args) {
  6.        //创建目标对象
  7.        OrderServiceImpl target = new OrderServiceImpl();
  8.        //创建代理对象
  9.        OrderServiceProxy proxy = new OrderServiceProxy(target);
  10.        //调用代理对象的代理方法
  11.        proxy.generate();
  12.        proxy.modify();
  13.        proxy.detail();
  14.   }
  15. }
  • 以上就是代理模式中的静态代理,其中OrderService接口是代理类和目标类的共同接口。

  • OrderServiceImpl是目标类,OrderServiceProxy是代理类。

14.3动态代理

  • 在程序运行阶段,在内存中动态生成代理类,被称为动态代理,目的是为了减少代理类的数量,解决代码复用的问题。

  • 在内存当中动态生成类的技术常见的包括:

    • JDK动态代理技术:只能代理接口。

    • CGLIB动态代理技术:CGLIB是一个开源项目,是一个强大的、高性能、高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口。它既可以代理接口,又可以代理类,底层是通过继承的方式实现的。性能比JDK动态代理要好。(底层有一个小而快的字节码处理框架ASM。)

    • Javassist动态代理技术。

14.3.1JDK动态代理

OrderService接口

  1. package com.hhb.proxy.service;
  2. //订单业务接口
  3. public interface OrderService {//代理对象和目标对象的公共接口
  4.    //生产订单
  5.    void generate();
  6.    //修改订单信息
  7.    void modify();
  8.    //查看订单详情
  9.    void detail();
  10. }

OrderServiceImpl

  1. package com.hhb.proxy.service;
  2. public class OrderServiceImpl implements OrderService {
  3.    @Override
  4.    public void generate() {//目标方法
  5.        //模拟生成订单的耗时
  6.        try {
  7.            Thread.sleep(2000);
  8.       } catch (InterruptedException e) {
  9.            e.printStackTrace();
  10.       }
  11.        System.out.println("订单已生成");
  12.   }
  13.    @Override
  14.    public void modify() {//目标方法
  15.        //模拟修改订单的耗时
  16.        try {
  17.            Thread.sleep(1000);
  18.       } catch (InterruptedException e) {
  19.            e.printStackTrace();
  20.       }
  21.        System.out.println("订单已修改");
  22.   }
  23.    @Override
  24.    public void detail() {//目标方法
  25.        //模拟查询订单的耗时
  26.        try {
  27.            Thread.sleep(500);
  28.       } catch (InterruptedException e) {
  29.            e.printStackTrace();
  30.       }
  31.        System.out.println("请看订单详情");
  32.   }
  33. }
  • 在动态代理中,OrderServiceProxy代理类是可以动态生成的,这个类不需要写,直接写客户端程序即可。

Client

  1. package com.hhb.proxy.client;
  2. import com.hhb.proxy.service.OrderService;
  3. import com.hhb.proxy.service.OrderServiceImpl;
  4. import com.hhb.proxy.service.TimerInvocationHandler;
  5. import com.hhb.proxy.util.ProxyUtil;
  6. import java.lang.reflect.Proxy;
  7. public class Client {
  8.    //客户端程序
  9.    public static void main(String[] args) {
  10.        //创建目标对象
  11.        OrderService target = new OrderServiceImpl();
  12.        //创建代理对象
  13.       /* OrderService proxyObj = (OrderService) Proxy.newProxyInstance(target.getClass().getClassLoader(),
  14.                target.getClass().getInterfaces(),
  15.                new TimerInvocationHandler(target));*/
  16.        //使用工具类
  17.        OrderService proxyObj = (OrderService) ProxyUtil.newProxyInstance(target);
  18.        //调用代理对象的代理方法
  19.        proxyObj.modify();
  20.        proxyObj.detail();
  21.        proxyObj.generate();
  22.        String name = proxyObj.getName();
  23.        System.out.println(name);
  24.   }
  25. }
  • 创建代理对象

    • newProxyInstance翻译为:新建代理对象,也就是说,通过调用这个方法可以创建代理对象。本质上,这个Proxy.newProxyInstance()方法的执行做了两件事:

      • 在内存中动态的生成了一个代理类的字节码class。

      • new对象了,通过内存中生成的代理类这个代码,实例化了代理对象。

    • 关于newProxyInstance()方法的三个重要的参数:

      • 第一个参数:ClassLoader loader

        • 它是类加载器。在内存中生成的字节码也是class文件,要执行也得先加载到内存当中。加载类就需要类加载器,所以这里需要指定类加载器。并且JDK要求,目标类的类加载器必须和代理类的类加载器使用同一个。

      • 第二个参数:Class<?>[] interfaces

        • 代理类和目标类要实现同一个接口或同一些接口。

        • 在内存中生成代理类的时候,这个代理类是需要你告诉它实现哪些接口的。

      • 第三个参数:InvocationHandler h

        • InvocationHandler 被翻译为:调用处理器,是一个接口。

        • 在调用处理器接口中编写的就是:增强代码。

        • 既然是接口,就要写接口的实现类。

TimerInvocationHandler

  1. package com.hhb.proxy.service;
  2. import java.lang.reflect.InvocationHandler;
  3. import java.lang.reflect.Method;
  4. public class TimerInvocationHandler implements InvocationHandler {
  5.    //目标对象
  6.    private Object target;
  7.    public TimerInvocationHandler(Object target) {
  8.        this.target = target;
  9.   }
  10.    @Override
  11.    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  12.       //这个接口的作用是写增强代码
  13.        long begin = System.currentTimeMillis();
  14.        //调用目标对象上的目标方法
  15.        //方法四要素:哪个对象,哪个方法,传什么参数,返回什么值
  16.        Object retValue = method.invoke(target, args);
  17.        long end = System.currentTimeMillis();
  18.        System.out.println("耗时" + (end - begin) + "毫秒");
  19.        //注意:这个invoke方法的返回值,如果代理对象调用代理方法之后,需要返回结果的话,invoke方法必须将目标对象的目标方法执行结果继续返回
  20.        return retValue;
  21.   }
  22. }
  • 为什么要强行要求你必须实现InvocationHandler接口?

    • 因为一个类实现接口就必须实现接口中的方法。

    • 方法必须是invoke(),因为JDK在底层调用invoke()方法的程序已经提前写好了。

    • 注意:invoke方法不是程序员调用的,是JDK负责调用。

  • invoke方法什么时候被调用?

    • 当代理对象调用代理方法的时候,注册在InvocationHandler调用处理器当中的invoke()方法被调用。

  • invoke方法的三个参数

    • invoke方法是JDK负责调用的,所以JDK调用这个方法的时候会自动给我们传过来这三个参数,可以在invoke方法的大括号中直接使用。

    • 第一个参数:Object proxy 代理对象的引用,这个参数使用较少。

    • 第二个参数:Method method 目标对象上的目标方法。

    • 第三个参数:Object[] args 目标方法上的实参。

    • invoke方法执行过程中,使用method来调用目标对象的目标方法。

工具类:ProxyUtil

  1. package com.hhb.proxy.util;
  2. import com.hhb.proxy.service.TimerInvocationHandler;
  3. import java.lang.reflect.Proxy;
  4. public class ProxyUtil {
  5.    /**
  6.     * 封装一个工具方法,可以通过这个方法获取代理对象
  7.     *
  8.     * @param target
  9.     * @return
  10.     */
  11.    public static Object newProxyInstance(Object target) {
  12.        return Proxy.newProxyInstance(target.getClass().getClassLoader(),
  13.                target.getClass().getInterfaces(),
  14.                new TimerInvocationHandler(target));
  15.   }
  16. }
14.3.2CGLIB动态代理
  • CGLIB既可以代理接口,又可以代理类。底层采用继承的方式实现,所以被代理的目标类不能使用final修饰。

引入依赖

  1. <dependency>
  2. <groupId>cglib</groupId>
  3. <artifactId>cglib</artifactId>
  4. <version>3.3.0</version>
  5. </dependency>

UserService

  1. package com.hhb.proxy.service;
  2. //目标类
  3. public class UserService {
  4.    //目标方法
  5.    public boolean login(String username, String password) {
  6.        System.out.println("系统正在验证身份");
  7.        if ("admin".equals(username) && "123".equals(password)) {
  8.            return true;
  9.       }
  10.        return false;
  11.   }
  12.    //目标方法
  13.    public void logout() {
  14.        System.out.println("系统正在退出");
  15.   }
  16. }
  • 使用CGLIB在内存中为UserService类生成代理类,并创建对象:

Client

  1. package com.hhb.proxy.client;
  2. import com.hhb.proxy.service.TimerMethodInterceptor;
  3. import com.hhb.proxy.service.UserService;
  4. import net.sf.cglib.proxy.Enhancer;
  5. public class Client {
  6.    public static void main(String[] args) {
  7.        //创建字节码增强器对象
  8.        //这个对象是CGLIB库当中的核心对象,就是依靠它来生成代理类
  9.        Enhancer enhancer = new Enhancer();
  10.        //告诉CGLIB父类是谁,告诉CGLIB目标类是谁
  11.        enhancer.setSuperclass(UserService.class);
  12.        //设置回调(等同于JDK动态代理当中的调用处理器。InvocationHandler)
  13.        //在CGLIB当中不是InvocatioHandler接口,是方法拦截器:MethodInterceptor
  14.        enhancer.setCallback(new TimerMethodInterceptor());
  15.        //创建代理对象
  16.        //1.在内存中生成UserService类的子类,其实就是代理类的字节码。
  17.        //2.创建代理对象
  18.        UserService userServiceProxy = (UserService) enhancer.create();
  19.        //调用代理对象的代理方法
  20.        boolean succes = userServiceProxy.login("admin", "123");
  21.        System.out.println(succes ? "登录成功" : "登录失败");
  22.        userServiceProxy.logout();
  23.   }
  24. }

编写MethodInterceptor接口实现类

  1. package com.hhb.proxy.service;
  2. import net.sf.cglib.proxy.MethodInterceptor;
  3. import net.sf.cglib.proxy.MethodProxy;
  4. import java.lang.reflect.Method;
  5. public class TimerMethodInterceptor implements MethodInterceptor {
  6.    @Override
  7.    public Object intercept(Object target, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
  8.        //前面增强
  9.        long begin = System.currentTimeMillis();
  10.        //调用目标对象的目标方法
  11.        Object retValue = methodProxy.invokeSuper(target, objects);
  12.        //后面增强
  13.        long end = System.currentTimeMillis();
  14.        System.out.println("耗时" + (end - begin) + "毫秒");
  15.        return retValue;
  16.   }
  17. }

本文内容由网友自发贡献,转载请注明出处:https://www.wpsshop.cn/w/Monodyee/article/detail/562306
推荐阅读
相关标签
  

闽ICP备14008679号