【Spring】(三)Spring框架代理模式

简介: 【Spring】(三)Spring框架代理模式

文章目录


一、Spring 框架代理模式

1、代理模式概述

2、代理模式之静态代理

3、代理模式之 JDK 动态代理

4、代理模式之 CGLIB 动态代理

二、Spring 代理模式原理区别


一、Spring 框架代理模式


1、代理模式概述


(1)代理(Proxy)是一种设计模式,提供了对目标对象另外的访问方式,即通过代理对象访问目标对象。这样做的好处是:可以在目标对象实现的基础上增强额外的功能操作,即扩展目标对象的功能。


(2)代理模式通常是通过持有目标对象的引用来实现的,为了便于描述我们将代理模式中被代理对象称为目标对象,负责进行代理的对象被称为代理对象。


20191102230220982.png


2、代理模式之静态代理


(1)静态代理概述


若代理类在程序运行前就已经存在,那么这种代理方式被成为静态代理,这种情况下的代理类通常都是我们在Java代码中定义的。 通常情况下,静态代理中的代理类和委托类会实现同一接口或是派生自相同的父类。


(2)静态代理步骤


① 新建业务层接口 ProductService 接口

public interface ProductService<E> {
  public void insertProduct() throws Exception;
  public void deleteProduct() throws Exception;
}


② 新建业务层 ProductServiceImp 实现类实现业务层接口,在业务实现类中完成业务逻辑代码

public class ProductServiceImp<E> implements ProductService<E>{
  @Override
  public void insertProduct() throws Exception {
  System.out.println("执行业务层新增操作代码");
  }
  @Override
  public void deleteProduct() throws Exception {
  System.out.println("执行业务层删除操作代码");
  }
}


③ 新建业务层代理类 ServiceProxy 实现业务层接口,在代理类中定义一个业务层接口的成员变量

public class ServiceProxy<E> implements ProductService<E>{
  private ProductService<Products> productService;
  public ServiceProxy(ProductService<Products> productService){
  this.productService = productService;
  }
  @Override
  public void insertProduct() throws Exception {
  try {
    System.out.println("执行获取连接,打开事务的代码");
    productService.insertProduct();
    System.out.println("执行关闭事务的代码");
  } catch(Exception e) {
    System.out.println("执行事务回滚的代码");
    throw e;
  } finally{
    System.out.println("关闭连接");
  }
  }
  @Override
  public void deleteProduct() throws Exception {
  try { 
    System.out.println("执行获取连接,打开事务的代码");
    productService.deleteProduct();
    System.out.println("执行关闭事务的代码");
  } catch(Exception e) {
    System.out.println("执行事务回滚的代码");
    throw e;
  } finally{
    System.out.println("关闭连接");
  }
  }
}


④ 测试类

public class Test {
  public static void main(String[] args) {
  ProductService<Products> productService = new ProductServiceImp<Products>();
  ServiceProxy<Products> proxy = new ServiceProxy<Products>(productService);
  try {
    proxy.insertProduct();
    proxy.deleteProduct();
  } catch (Exception e) {
    e.printStackTrace();
  }
  }
}


(3)静态代理缺点


静态代理适用性不强,代理对象和被代理对象之前是一种强耦合,代理对象必须知道被代理对象具体的变量或方法,从而进行调用,一旦被代理对象多起来,那就需要创建多个代理,工作量太大,同样不好维护和管理。


3、代理模式之 JDK 动态代理


(1)JDK 动态代理概述


代理类在程序运行时创建的代理方式被成为动态代理。


在JDK1.3之后加入了可协助开发的动态代理功能,不必为特定对象与方法编写特定的代理对象,使用动态代理,可以使得一个处理者(Handler)服务于各个对象,也就是说,这种情况下,代理类并不是在Java代码中定义的,而是在运行时根据我们在Java代码中的“指示”动态生成的。


相比于静态代理,动态代理的优势在于可以很方便的对代理类的函数进行统一的处理,而不用修改每个代理类的函数。


一个处理者的类设计必须实现 java.lang.reflect.InvocationHandler 接口,通过 InvocationHandler 接口实现的动态代理只能代理接口的实现类。


动态代理的基本思路都是在程序运行期间动态的生成了新的 class 文件,可以理解为是 java 根据我们的需求动态的编写了一个类。通过该类对我们的目标对象进行代理。


20191102232425896.png


(2)重要API


InvocationHandler:直译为调用处理器,使用该类可以封装我们需要执行的代码体,最后 Proxy 会根据该对象中包含的代码体动态的生成一个新的 class 文件,该 class 文件就是我们的代理对象类。


Proxy:代理类,通过该类生成动态代理对象


(3)JDK 动态代理步骤:


① 新建业务层接口

public interface ProductsService {
  public void insert();
}


② 新建业务层实现类实现业务层接口,在业务实现类中完成业务逻辑代码

public class ProductsServiceImp implements ProductsService{
  @Override
  public void insert(){
  System.out.println("正在执行新增操作");
  }
}


③ 新建调用处理器封装代理类中的方法体

public class TransactionHander implements InvocationHandler{
  private Object target;
  public TransactionHander(Object target) {
  this.target = target;
  }
  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
  Object result = null;
  try {
    System.out.println("获取连接打开事务");
    result = method.invoke(target, args);
    System.out.println("提交事务");
  }catch (Exception e) {
    System.out.println("事务回滚");
  }
  return result;
  }
}


TransactionHander 是一个实现了 InvocationHandler 接口的调用处理器类,成员变量 object 表示的是需要被代理的目标对象,核心方法 invoke 中的 method 参数表示的是目标对象执行的目标方法,args 数组表示方法执行时所需要的参数。通过 method 可以执行目标对象中的方法,在执行该方法前后都可以执行一些其他的代码来对功能进行增强。


当我们调用代理类对象的方法时,这个“调用”会转送到invoke方法中,代理类对象作为proxy参数传入,参数method标识了我们具体调用的是代理类的哪个方法,args为这个方法的参数。这样一来,我们对代理类中的所有方法的调用都会变为对invoke的调用,这样我们可以在invoke方法中添加统一的处理逻辑(也可以根据method参数对不同的代理类方法做不同的处理)。


④ 使用Proxy类创建代理类对象

public class Test {
  public static void main(String[] args) {
  System.getProperties().put("sun.misc.ProxyGenerator.saveGeneratedFiles", "true");
  //创建目标对象
  ProductsService productsService = new ProductsServiceImp();
  //创建调用处理器对象
  TransactionHander handler = new TransactionHander(productsService);
  //创建代理对象
  ProductsService serviceProxy = (ProductsService) Proxy.newProxyInstance(productsService.getClass().getClassLoader(), productsService.getClass().getInterfaces(),handler);
  serviceProxy.insert();
  }
}


Proxy.newProxyInstance() 方法用于创建代理对象,其中第一个参数表示动态代理对象的class文件生成完毕之后交给哪一个类加载器来加载。通常使用目标对象类对应的加载器。第二个参数表示的是在生成class文件时这个代理类需要实现的接口,由于最后生成的代理对象必须是可以强转为目标对象的,所以代理对象实现的接口必须和目标对象保持一致,第三个参数是调用处理器对象,调用处理器对象封装了代理对象执行的方法体。


⑤ 创建代理对象工厂类(可选)


JDK动态代理已经足够优秀,可以使代理方式更加灵活更易于程序的拓展,但是美中不足的是,JDK动态代理只能代理实现了接口的类,如果要创建没有实现接口的类,需要使用第三方提供的CGLIB代理。


4、代理模式之 CGLIB 动态代理


(1)CGLIB 动态代理概述


CGLIB(Code Generation Library)是一个开源项目,是一个强大的,高性能,高质量的Code生成类库,它可以在运行期扩展Java类与实现Java接口,通俗说CGLIB可以在运行时动态生成字节码。


(2)重要步骤


① 新建业务层接口

public interface CGLIBService {
  public void insert();
}


② 新建业务层实现类实现业务层接口,在业务实现类中完成业务逻辑代码

public class CGLIBServiceImp implements CGLIBService{
  public void insert() {
  System.out.println("执行业务层新增操作");
  }
}


③ 新建方法拦截器 CGLIBInterceptor 类,通过该类的interceptor方法对目标方法进行增强处理

public class CGLIBInterceptor implements MethodInterceptor{
  private Object target;
  public Object getTarget() {
  return target;
  }
  public void setTarget(Object object) {
  this.target = object;
  }
  @Override
  public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
  Object result = null;;
  try {
    System.out.println("获取连接,开启事务");
    result = method.invoke(target, args);
    System.out.println("提交事务");
  } catch (Exception e) {
    System.out.println("事务回滚");
  }
  return result;
  }
}


④ 在方法拦截器 CGLIBInterceptor 类中自定义 getProxy() 方法用于生成代理对象

public Object getProxy(Object object) {
  //将目标对象保存到成员变量中
  this.target = object;
  //增强器,动态代码生成器,创建代理对象
  Enhancer enhancer = new Enhancer();
  //设置回调对象
  enhancer.setCallback(this);
  //设置类的父类类型,其实就是目标对象的类型,生成的代理对象汇集成目标对象
  //设置目标对象的类信息,代理类汇集成目标类
  enhancer.setSuperclass(object.getClass());
  //动态生成字节码,并返回代理对象
  return enhancer.create();
  }


⑤ 新建测试类,调用方法拦截器类的 getProxy 方法生成代理对象

public class CGLIBTest {
  public static void main(String[] args) {
  CGLIBService service = new CGLIBServiceImp();
  CGLIBInterceptor interceptor = new CGLIBInterceptor();
  CGLIBService proxy = (CGLIBService) interceptor.getProxy(service);
  proxy.insert();
  }
}


二、Spring 代理模式原理区别


java 动态代理是利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用 InvokeHandler 来处理。而 cglib 动态代理是利用 asm 开源包,对代理对象类的 class 文件加载进来,通过修改其字节码生成子类来处理。


1、如果目标对象实现了接口,默认情况下会采用JDK的动态代理实现AOP


2、如果目标对象实现了接口,可以强制使用CGLIB实现AOP


3、如果目标对象没有实现了接口,必须采用CGLIB库,spring会自动在JDK动态代理和CGLIB之间转换


目录
相关文章
|
29天前
|
数据采集 监控 前端开发
二级公立医院绩效考核系统源码,B/S架构,前后端分别基于Spring Boot和Avue框架
医院绩效管理系统通过与HIS系统的无缝对接,实现数据网络化采集、评价结果透明化管理及奖金分配自动化生成。系统涵盖科室和个人绩效考核、医疗质量考核、数据采集、绩效工资核算、收支核算、工作量统计、单项奖惩等功能,提升绩效评估的全面性、准确性和公正性。技术栈采用B/S架构,前后端分别基于Spring Boot和Avue框架。
|
2月前
|
Java API 数据库
构建RESTful API已经成为现代Web开发的标准做法之一。Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐。
【10月更文挑战第11天】本文介绍如何使用Spring Boot构建在线图书管理系统的RESTful API。通过创建Spring Boot项目,定义`Book`实体类、`BookRepository`接口和`BookService`服务类,最后实现`BookController`控制器来处理HTTP请求,展示了从基础环境搭建到API测试的完整过程。
48 4
|
2月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,包括版本兼容性、安全性、性能调优等方面。
158 1
|
2月前
|
Java API 数据库
Spring Boot框架因其简洁的配置、快速的启动特性及丰富的功能集而备受开发者青睐
本文通过在线图书管理系统案例,详细介绍如何使用Spring Boot构建RESTful API。从项目基础环境搭建、实体类与数据访问层定义,到业务逻辑实现和控制器编写,逐步展示了Spring Boot的简洁配置和强大功能。最后,通过Postman测试API,并介绍了如何添加安全性和异常处理,确保API的稳定性和安全性。
38 0
|
2月前
|
前端开发 Java 数据库连接
Spring 框架:Java 开发者的春天
Spring 框架是一个功能强大的开源框架,主要用于简化 Java 企业级应用的开发,由被称为“Spring 之父”的 Rod Johnson 于 2002 年提出并创立,并由Pivotal团队维护。
57 1
Spring 框架:Java 开发者的春天
|
26天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,帮助开发者提高开发效率和应用的可维护性。
44 2
|
2月前
|
人工智能 Java API
阿里云开源 AI 应用开发框架:Spring AI Alibaba
近期,阿里云重磅发布了首款面向 Java 开发者的开源 AI 应用开发框架:Spring AI Alibaba(项目 Github 仓库地址:alibaba/spring-ai-alibaba),Spring AI Alibaba 项目基于 Spring AI 构建,是阿里云通义系列模型及服务在 Java AI 应用开发领域的最佳实践,提供高层次的 AI API 抽象与云原生基础设施集成方案,帮助开发者快速构建 AI 应用。本文将详细介绍 Spring AI Alibaba 的核心特性,并通过「智能机票助手」的示例直观的展示 Spring AI Alibaba 开发 AI 应用的便利性。示例源
|
25天前
|
消息中间件 NoSQL Java
springboot整合常用中间件框架案例
该项目是Spring Boot集成整合案例,涵盖多种中间件的使用示例,每个案例项目使用最小依赖,便于直接应用到自己的项目中。包括MyBatis、Redis、MongoDB、MQ、ES等的整合示例。
81 1
|
2月前
|
人工智能 开发框架 Java
总计 30 万奖金,Spring AI Alibaba 应用框架挑战赛开赛
Spring AI Alibaba 应用框架挑战赛邀请广大开发者参与开源项目的共建,助力项目快速发展,掌握 AI 应用开发模式。大赛分为《支持 Spring AI Alibaba 应用可视化调试与追踪本地工具》和《基于 Flow 的 AI 编排机制设计与实现》两个赛道,总计 30 万奖金。
|
2月前
|
Java 数据库连接 开发者
Spring 框架:Java 开发者的春天
【10月更文挑战第27天】Spring 框架由 Rod Johnson 在 2002 年创建,旨在解决 Java 企业级开发中的复杂性问题。它通过控制反转(IOC)和面向切面的编程(AOP)等核心机制,提供了轻量级的容器和丰富的功能,支持 Web 开发、数据访问等领域,显著提高了开发效率和应用的可维护性。Spring 拥有强大的社区支持和丰富的生态系统,是 Java 开发不可或缺的工具。