当前位置: 首页 > news >正文

spring-第十二章 GoF代理模式

spring


文章目录

  • spring
  • 前言
  • 1.代理模式理解
  • 2.静态代理
  • 3.动态代理
    • 3.1JDK动态代理
      • 3.1.1公共接口
      • 3.1.2目标对象
      • 3.1.3客户端
      • 3.1.4额外功能代码
    • 3.2CGLIB动态代理
  • 总结


前言

在学习AOP之前,我们得先学习一下代理模式,以帮助我们理解AOP思想。


1.代理模式理解

代理模式是AOP的实现思想。所以这里有必要先了解代理模式原理。
首先,按照字面意思,代理模式就是找个人来帮自己做事,这样做有两个原因:

  • 防止自己受伤——保护自己
  • 自己无法完成这个工作——功能增强

在代理模式中,我们有三种角色:

  • 目标对象:需要代理的对象
  • 代理对象:为目标对象执行代理功能的对象
  • 目标对象和代理对象的公共接口:目标对象和代理对象都需要实现的接口。

这里要明白,我们设置目标对象和代理对象的公共接口的目的是为了统一目标对象和代理对象间的行为。
比如,如果我的目标对象中有一个删除数据的功能,需要另一个代理对象来对该功能进行增强。那么首先要保证——目标对象和代理对象中都有这个删除数据的功能(或方法),才能让目标对象把这个任务交给代理对象去做。而这一点,可以通过设置一个两者共用的接口来完成。
以图的方式表示如下:
请添加图片描述

可以看出,由代理对象来实际对接用户完成实际任务,但对于用户而言只会觉得调用的是目标对象中的delete()功能.
而在具体的代码实现中,我们有两种方式:

  • 静态代理:手动完成代理对象的创建
  • 动态代理:由字节码生成机在程序运行时自动生成代理对象

2.静态代理

静态代理就是由我们自己手动的创建目标对象、代理对象、目标对象和代理对象公共接口。
下面以一个案例说明如何编写。
假设我们已经有了Service层实现类及其接口——OrderService、OrderServiceImpl
其代码如下:
OrderService

package com.powernode.mall.service;  /**  * 订单接口   **/  
public interface OrderService {  /**  * 生成订单  */  void generate();  /**  * 查看订单详情  */  void detail();  /**  * 修改订单  */  void modify();  
}

OrederServiceImpl

package com.powernode.mall.service.impl;  import com.powernode.mall.service.OrderService;  public class OrderServiceImpl implements OrderService {  @Override  public void generate() {  try {  Thread.sleep(1234);  } catch (InterruptedException e) {  e.printStackTrace();  }  System.out.println("订单已生成");  }  @Override  public void detail() {  try {  Thread.sleep(2541);  } catch (InterruptedException e) {  e.printStackTrace();  }  System.out.println("订单信息如下:******");  }  @Override  public void modify() {  try {  Thread.sleep(1010);  } catch (InterruptedException e) {  e.printStackTrace();  }  System.out.println("订单已修改");  }  
}

该service功能正常,但是现在需要对其功能进行增强——需要计算每个方法的执行时间
直接的想法可能是去修改实现类中的代码,但是这样违反了OCP原则,我们不应该为了增加新功能而去修改已经完成的正常运行的代码。

所以我们会考虑使用代理模式进行工作增强。
在这种情况下,我们可以分析出如下关系:

OrderService——目标对象和代理对象公共接口
OrderServiceImpl——目标对象

所以我们还需要手动为创建代理对象

package com.powernode.mall.service;  public class OrderServiceProxy implements OrderService{ // 代理对象  // 目标对象  private OrderService orderService;  // 通过构造方法将目标对象传递给代理对象  public OrderServiceProxy(OrderService orderService) {  this.orderService = orderService;  }  @Override  public void generate() {  long begin = System.currentTimeMillis();  // 执行目标对象的目标方法  orderService.generate();  long end = System.currentTimeMillis();  System.out.println("耗时"+(end - begin)+"毫秒");  }  @Override  public void detail() {  long begin = System.currentTimeMillis();  // 执行目标对象的目标方法  orderService.detail();  long end = System.currentTimeMillis();  System.out.println("耗时"+(end - begin)+"毫秒");  }  @Override  public void modify() {  long begin = System.currentTimeMillis();  // 执行目标对象的目标方法  orderService.modify();  long end = System.currentTimeMillis();  System.out.println("耗时"+(end - begin)+"毫秒");  }  
}

代理对象中会引入目标对象作为属性,然后在调用目标对象前后进行额外的功能补强。
这么一来,我们在实际使用时,调用的是代理对象中的方法,就能够使用原有的功能以及新增的功能了。

3.动态代理

动态代理中我们不用自己手动创建代理对象,而是由在代码运行时动态的创建代理对象。
能够动态生成类的技术常见的有:JDK动态代理、CGLIB动态代理。

3.1JDK动态代理

由jdk反射包中的Proxy类来完成,但是只能代理接口。
下面我们使用JDK动态代理完成前面静态代理中相同的案例。
首先,我们的OrderService接口和OrderServiceImpl的代码不变:

3.1.1公共接口

OrderService

package com.powernode.mall.service;  public interface OrderService {  /**  * 生成订单  */  void generate();  /**  * 查看订单详情  */  void detail();  /**  * 修改订单  */  void modify();  
}

3.1.2目标对象

OrderServiceImpl

package com.powernode.mall.service.impl;  import com.powernode.mall.service.OrderService;  public class OrderServiceImpl implements OrderService {  @Override  public void generate() {  try {  Thread.sleep(1234);  } catch (InterruptedException e) {  e.printStackTrace();  }  System.out.println("订单已生成");  }  @Override  public void detail() {  try {  Thread.sleep(2541);  } catch (InterruptedException e) {  e.printStackTrace();  }  System.out.println("订单信息如下:******");  }  @Override  public void modify() {  try {  Thread.sleep(1010);  } catch (InterruptedException e) {  e.printStackTrace();  }  System.out.println("订单已修改");  }  
}

3.1.3客户端

这里我们不需要手动创建一个类作为代理对象,而是使用Proxy类来创建代理对象

public class Client {  public static void main(String[] args) {  
//        创建目标对象  OrderService orderService = new OrderServiceImpl();  
//        创建代理对象  OrderService o = (OrderService) Proxy.newProxyInstance(orderService.getClass().getClassLoader(),  orderService.getClass().getInterfaces(), new TimeInvocationHandle());  
//        调用代理方法  o.generate();  }  
}

从上面的代码可知,我们使用newProxyInstance()方法来动态的创建代理对象。
所以我们先介绍一下这个方法。
以下为源码:
请添加图片描述

发现该方法需要三个参数:

  • loader:类加载器,因为在内存中也是要生成代理对象的字节码的,所以需要使用类加载器加载。需要传入和目标对象一样的类加载器,所以使用时使用反射机制获取目标对象的类加载器作为参数即可。
  • interfaces:一开始说过,这种代理模式是为接口进行代理,所以这里的第二个参数就是我们需要代理的接口,也是我们案例中的目标对象和代理对象公共接口,使用时候也通过目标对象使用反射机制获取即可。
  • h:可以看出该参数是一个InvocationHandler接口,该接口是我们写增强代码的地方,我们需要自己创建该接口的实现类对象然后作为参数传入。

3.1.4额外功能代码

前面说过newProxyInstance()的第三个参数需要的InvocationHandler接口是我们写增强代码的地方,那么现在我们需要手动创建一个InvocationHandler的实现类,并在里面编写额外的功能代码。
创建出的实现类基本结构如下:

package org.example.proxy;  import java.lang.reflect.InvocationHandler;  
import java.lang.reflect.Method;  public class TimeInvocationHandle implements InvocationHandler {  @Override  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  System.out.println("invoke执行");  return null;  }  
}

其中invoke()方法就是我们的“额外功能”,该方法会在我们执行了目标对象中的方法后自动执行。
那么又有问题了,我们如何决定这些“额外功能”哪些在原方法前执行,哪些在原方法之后执行呢?
这些就需要了解一下invoke()方法的各个参数了:

  • proxy:代理对象。设计这个参数只是为了后期的方便,如果想在invoke方法中使用代理对象的话,尽管通过这个参数来使用。
  • method:目标方法,看类型可以看出是反射机制中的Method类,所以我们可以使用该参数并利用反射机制在invoke()方法中调用“目标方法”。
  • args:是一个数组,上一个参数允许我们调用“目标方法”,而这个参数允许我们向“目标方法”中传递参数。

上面的参数中我们可以获取到调用“目标方法”所需的Method类对象、参数、但是我们还要拿到目标对象。所以我们还需要在这个InvocationHandler实现类中设置一个目标对象类型的属性,然后我们可以使用有参构造器进行属性赋值。
于是我们可以在invoke()方法中调用原方法(“目标方法”),并自由决定在其前后添加额外的功能代码,最终我们的实现类如下:

package org.example.proxy;  import java.lang.reflect.InvocationHandler;  
import java.lang.reflect.Method;  public class TimerInvocationHandler implements InvocationHandler {  // 目标对象  private Object target;  // 通过构造方法来传目标对象  public TimerInvocationHandler(Object target) {  this.target = target;  }  @Override  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {  // 目标执行之前增强。  long begin = System.currentTimeMillis();  // 调用目标对象的目标方法  Object retValue = method.invoke(target, args);  // 目标执行之后增强。  long end = System.currentTimeMillis();  System.out.println("(end - begin)+"毫秒");  // 一定要记得返回哦。  return retValue;  }  
}

需要注意的是,如果原方法是有返回值的,那么我们实现类的invoke方法中也要记得设置返回值。
并且我们既然要用构造方法为属性赋值,则客户端的代码创建对象时也要把目标对象传入:

public class Client {  public static void main(String[] args) {  
//        创建目标对象  OrderService orderService = new OrderServiceImpl();  
//        创建代理对象  OrderService o = (OrderService) Proxy.newProxyInstance(orderService.getClass().getClassLoader(),  orderService.getClass().getInterfaces(), new TimeInvocationHandle(orderService));  
//        调用代理方法  o.generate();  }  
}

请添加图片描述

最后,我们使用Proxy创建出来的对象任意调用一个目标方法都会添加上额外功能的代码。
请添加图片描述

3.2CGLIB动态代理

是一个开源项目,它既可以代理接口,又可以代理类,底层是通过继承的方式实现的(所以需要代理的类不能被final修饰),底层有一个小而快的字节码处理框架ASM。


总结

我们简单学习了代理模式,这是我们学习AOP的前提。


http://www.mrgr.cn/news/60107.html

相关文章:

  • 【编程语言】正则表达式:POSIX 与 PCRE 的全面比较及应用
  • Redis 发布订阅 总结
  • 速盾:高防cdn怎么拦截恶意ip?
  • web相关知识学习笔记
  • React 中组件通信的几种主要方式
  • EJB项目如何升级SpringCloud
  • Bootstrap 5 弹出框
  • MSR寄存器独有的还是共享的
  • Java最全面试题->数据库/中间件->RocketMQ面试题
  • 后台管理系统的通用权限解决方案(三)SpringBoot整合Knife4j生成接口文档
  • 问:SQL中的通用函数及用法?
  • AI学习指南自然语言处理篇-Transformer模型的实践
  • fastjson解析null值问题: 解决 null的属性不显示问题
  • 如何从示波器上得到时间常数
  • Mybatis的关联关系-多对多
  • Python | Leetcode Python题解之第515题在每个树行中找最大值
  • 问:MySQL中的常用SQL函数整理?
  • jQuery Callback
  • 自由职业者的一天:作为小游戏开发者的真实工作日记
  • 栈和队列(上)-栈
  • 【skywalking 】监控 Spring Cloud Gateway 数据
  • 【c++高级篇】--多任务编程/多线程(Thread)
  • spring-第十一章 注解开发
  • C语言 | Leetcode C语言题解之第516题最长回文子序列
  • 《贪婪算法实战:从理论到面试题的全面解析》
  • Qt example---40000 Chips