当前位置:   article > 正文

GoF之代理模式_gof代理模式

gof代理模式
在java程序中的代理模式的作用:
第一个作用: 当一个对象需要受到保护的时候,可以考虑使用代理对象去完成某个行为。
第二个作用: 需要给某个对象的功能进行功能增强的时候,可以考虑找一个代理进行增强。
第三个作用: A对象无法和B对象直接交互时,也可以使用代理模式来解决。

代理模式的类图:

拓展: UML中常见的几种关联关系(依赖、泛化、实现、关联、组合、聚合)

什么是静态代理?

一:创建一个代理对象,这个代理对象要实现目标对象所实现的接口。也就是说,目标对象实现了哪些接口,这个目标对象的代理对象就要实现这些接口。

二:将目标对象传递给代理对象(如下代码)

  1. // 目标对象
  2. private OrderService orderService;
  3. // 通过构造方法将目标对象传递给代理对象
  4. public OrderServiceProxy(OrderService orderService) {
  5. this.orderService = orderService;
  6. }

如果系统中业务接口很多,一个接口对应一个代理类,显然也是不合理的,会导致类爆炸。怎么解决这个问题?动态代理可以解决。因为在动态代理中可以在内存中动态的为我们生成代理类的字节码。代理类不需要我们写了。类爆炸解决了,而且代码只需要写一次,代码也会得到复用。

什么是动态代理?

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

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

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

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

  • Javassist动态代理技术:Javassist是一个开源的分析、编辑和创建Java字节码的类库。是由东京工业大学的数学和计算机科学系的 Shigeru Chiba (千叶 滋)所创建的。它已加入了开放源代码JBoss 应用服务器项目,通过使用Javassist对字节码操作为JBoss实现动态"AOP"框架。

(spring框架主要用的是前两种代理技术)

JDK动态代理:

OrderService接口:

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

OrderService接口的实现类:

  1. public class OrderServiceImpl implements OrderService{ //目标对象
  2. @Override
  3. public void generate() { //目标方法
  4. try {
  5. Thread.sleep(1234);
  6. } catch (InterruptedException e) {
  7. e.printStackTrace();
  8. }
  9. System.out.println("订单已生成");
  10. }
  11. @Override
  12. public void detail() { //目标方法
  13. try {
  14. Thread.sleep(2541);
  15. } catch (InterruptedException e) {
  16. e.printStackTrace();
  17. }
  18. System.out.println("订单信息如下:******");
  19. }
  20. @Override
  21. public void modify() { //目标方法
  22. try {
  23. Thread.sleep(1010);
  24. } catch (InterruptedException e) {
  25. e.printStackTrace();
  26. }
  27. System.out.println("订单已修改");
  28. }
  29. }

我们在静态代理的时候,除了以上一个接口和一个实现类之外,是不是要写一个代理类UserServiceProxy呀!在动态代理中UserServiceProxy代理类是可以动态生成的。这个类不需要写。我们直接写客户端程序即可:

  1. public class Client { //客户端程序
  2. public static void main(String[] args) {
  3. //创建目标对象
  4. OrderService target = new OrderServiceImpl();
  5. //创建代理对象
  6. /*
  7. 1. newProxyInstance 翻译为:新建代理对象
  8. 也就是说,通过调用这个方法可以创建代理对象。
  9. 本质上,这个Proxy.newProxyInstance()方法的执行,做了两件事:
  10. 第一件事:在内存中动态的生成了一个代理类的字节码class。
  11. 第二件事: new对象了。通过内存中生成的代理类这个代码,实例化了代理对象。
  12. 2.关于newProxyInstance()方法的三个重要的参数,每一个什么含义,有什么用?
  13. 第一个参数:ClassLoader loader
  14. 类加载器。这个类加载器有什么用呢?
  15. 在内存当中生成的字节码也是class文件,要执行也得先加载到内存当中。加载类就需要类加载器。
  16. 所以这里需要指定类加载器。并且JDK要求,目标类的类加载器必须和代理类的类加载器使用同一个。
  17. 第二个参数: Class<?>[] interfaces
  18. 代理类和目标类要实现同一个接口或同一些接口。
  19. 在内存中生成代理类的时候,这个代理类是需要你告诉它实现哪些接口的。
  20. 第三个参数: InvocationHandler h
  21. InvocationHandler被翻译为:调用处理器。是一个接口。
  22. 在调用处理器接口中编写的就是:增强代码。
  23. 因为具体要增强什么代码,JDK动态代理技术它是猜不到的。没有那么神。
  24. 既然是接口,就要写接口的实现类。
  25. 可能会有疑问?
  26. 自己还要动手写调用处理器接口的实现类,这不会类爆炸吗?不会。
  27. 因为这种调用处理器写一次就好。
  28. 注意:代理对象和目标对象实现的接口一样,所以可以向下转型。
  29. */
  30. OrderService proxyObj = (OrderService)Proxy.newProxyInstance(
  31.                                                     target.getClass().getClassLoader(),
  32. target.getClass().getInterfaces(),
  33. new TimerInvocationHandler(target)
  34.                                                      );
  35. //代理对象调用代理方法
  36. proxyObj.generate();
  37. /*proxyObj.modify();
  38. proxyObj.detail();*/
  39. }
  40. }
  1. /**
  2. *专门负责计时的一个调用处理器对象。
  3. * 在这个调用处理器当中编写计时相关的增强代码。
  4. * 这个调用处理器只需要写一个就行了。
  5. */
  6. public class TimerInvocationHandler implements InvocationHandler {
  7. private Object target;
  8. public TimerInvocationHandler(Object target) {
  9. this.target = target;
  10. }
  11. /*
  12. 1.为什么强行要求你必须实现InvocationHandler接口?
  13. 因为一个类实现接口就必须实现接口中的方法。
  14. 以下这个方法必须是invoke(),因为JDK在底层调用invoke()方法的程序已经提前写好了。
  15. 注意: invoke方法不是我们程序员负责调用的,是JDK负责调用的。
  16. 2. invoke方法什么时候被调用呢?
  17. 当代理对象调用代理方法的时候,注册在InvocationHandler调用处理器当中的invoke()方法被调用。
  18. 3. invoke方法的三个参数:
  19. invoke方法是JDK负责调用的,所以JDK调用这个方法的时候会自动给我们传过来这三个参数。
  20. 我们可以在invoke方法的大括号中直接使用。
  21. 第一个参数:Object proxy 代理对象的引用。这个参数使用较少。
  22. 第二个参数:Method method目标对象上的目标方法。(要执行的目标方法就是它。)
  23. 第三个参数:Object[ ] args目标方法上的实参。
  24. */
  25. @Override
  26. public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  27. long begin = System.currentTimeMillis();
  28. //System.out.println("增强1");
  29. //调用目标对象的目标方法
  30. Object retValue = method.invoke(target, args);
  31. //System.out.println("增强2");
  32. long over = System.currentTimeMillis();
  33. System.out.println("程序所用时间:"+(over-begin));
  34. //注意这个invoke方法的返回值,如果代理对象调用代理方法之后,需要返回结果的话,invoke方法必须将目标对象的目标方法执行结果继续返回。
  35. return retValue;
  36. }
  37. }

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/笔触狂放9/article/detail/562327
推荐阅读
相关标签
  

闽ICP备14008679号