【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之间转换


目录
相关文章
|
19天前
|
缓存 Java 开发工具
Spring是如何解决循环依赖的?从底层源码入手,详细解读Spring框架的三级缓存
三级缓存是Spring框架里,一个经典的技术点,它很好地解决了循环依赖的问题,也是很多面试中会被问到的问题,本文从源码入手,详细剖析Spring三级缓存的来龙去脉。
Spring是如何解决循环依赖的?从底层源码入手,详细解读Spring框架的三级缓存
|
19天前
|
缓存 安全 Java
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
从底层源码入手,通过代码示例,追踪AnnotationConfigApplicationContext加载配置类、启动Spring容器的整个流程,并对IOC、BeanDefinition、PostProcesser等相关概念进行解释
Spring框架中Bean是如何加载的?从底层源码入手,详细解读Bean的创建流程
|
10天前
|
人工智能 开发框架 Java
重磅发布!AI 驱动的 Java 开发框架:Spring AI Alibaba
随着生成式 AI 的快速发展,基于 AI 开发框架构建 AI 应用的诉求迅速增长,涌现出了包括 LangChain、LlamaIndex 等开发框架,但大部分框架只提供了 Python 语言的实现。但这些开发框架对于国内习惯了 Spring 开发范式的 Java 开发者而言,并非十分友好和丝滑。因此,我们基于 Spring AI 发布并快速演进 Spring AI Alibaba,通过提供一种方便的 API 抽象,帮助 Java 开发者简化 AI 应用的开发。同时,提供了完整的开源配套,包括可观测、网关、消息队列、配置中心等。
528 8
|
7天前
|
XML 前端开发 Java
控制spring框架注解介绍
控制spring框架注解介绍
|
7天前
|
存储 NoSQL Java
Spring Session框架
Spring Session 是一个用于在分布式环境中管理会话的框架,旨在解决传统基于 Servlet 容器的会话管理在集群和云环境中的局限性。它通过将用户会话数据存储在外部介质(如数据库或 Redis)中,实现了会话数据的跨服务器共享,提高了应用的可扩展性和性能。Spring Session 提供了无缝集成 Spring 框架的 API,支持会话过期策略、并发控制等功能,使开发者能够轻松实现高可用的会话管理。
Spring Session框架
|
15天前
|
Java 应用服务中间件 开发者
深入探索并实践Spring Boot框架
深入探索并实践Spring Boot框架
25 2
|
14天前
|
机器学习/深度学习 数据采集 JavaScript
ADR智能监测系统源码,系统采用Java开发,基于SpringBoot框架,前端使用Vue,可自动预警药品不良反应
ADR药品不良反应监测系统是一款智能化工具,用于监测和分析药品不良反应。该系统通过收集和分析病历、处方及实验室数据,快速识别潜在不良反应,提升用药安全性。系统采用Java开发,基于SpringBoot框架,前端使用Vue,具备数据采集、清洗、分析等功能模块,并能生成监测报告辅助医务人员决策。通过集成多种数据源并运用机器学习算法,系统可自动预警药品不良反应,有效减少药害事故,保障公众健康。
ADR智能监测系统源码,系统采用Java开发,基于SpringBoot框架,前端使用Vue,可自动预警药品不良反应
|
24天前
|
运维 NoSQL Java
SpringBoot接入轻量级分布式日志框架GrayLog技术分享
在当今的软件开发环境中,日志管理扮演着至关重要的角色,尤其是在微服务架构下,分布式日志的统一收集、分析和展示成为了开发者和运维人员必须面对的问题。GrayLog作为一个轻量级的分布式日志框架,以其简洁、高效和易部署的特性,逐渐受到广大开发者的青睐。本文将详细介绍如何在SpringBoot项目中接入GrayLog,以实现日志的集中管理和分析。
100 1
|
28天前
|
缓存 Java 应用服务中间件
随着微服务架构的兴起,Spring Boot凭借其快速开发和易部署的特点,成为构建RESTful API的首选框架
【9月更文挑战第6天】随着微服务架构的兴起,Spring Boot凭借其快速开发和易部署的特点,成为构建RESTful API的首选框架。Nginx作为高性能的HTTP反向代理服务器,常用于前端负载均衡,提升应用的可用性和响应速度。本文详细介绍如何通过合理配置实现Spring Boot与Nginx的高效协同工作,包括负载均衡策略、静态资源缓存、数据压缩传输及Spring Boot内部优化(如线程池配置、缓存策略等)。通过这些方法,开发者可以显著提升系统的整体性能,打造高性能、高可用的Web应用。
58 2
|
29天前
|
Cloud Native 安全 Java
Micronaut对决Spring Boot:谁是微服务领域的王者?揭秘两者优劣,选对框架至关重要!
【9月更文挑战第5天】近年来,微服务架构备受关注,Micronaut和Spring Boot成为热门选择。Micronaut由OCI开发,基于注解的依赖注入,内置多种特性,轻量级且启动迅速;Spring Boot则简化了Spring应用开发,拥有丰富的生态支持。选择框架需考虑项目需求、团队经验、性能要求及社区支持等因素。希望本文能帮助您选择合适的微服务框架,助力您的软件开发项目取得成功!
97 2
下一篇
无影云桌面