Java研学-代理模式

一 概述

1 分类

  静态代理:在程序运行前就已经存在代理类的字节码文件,代理对象和真实对象的关系在运行前就确定了。(代理类及对象要自行创建)
  动态代理:代理类是在程序运行期间由 JVM 通过反射等机制动态的生成的,不存在代理类的字节码文件,动态生成字节码对象,代理对象和真实对象的关系是在程序运行时期才确定的。(代理类及对象不需要自行创建)

2 动态代理实现方式

  真实类有接口使用 JDK 动态代理;
  真实类没实现接口使用 CGLIB 或 Javassist 组件。

二 静态代理

1 介绍

  静态代理是一种代理模式,其中代理对象在编译时就确定下来,而不是在运行时动态创建。静态代理由业务实现类、业务代理类两部分组成。业务实现类负责实现主要的业务方法,业务代理类负责对调用的业务方法作拦截、过滤、预处理。

  静态代理的优点是可以实现对目标方法的扩展,控制真实对象的访问权限,避免创建大对象,以及增强真实对象功能。业务类只需要关注业务逻辑本身,保证了业务类的重用性。把真实对象隐藏起来了,保护真实对象。

  缺点是如果一个代理类只能对一个业务接口的实现类进行包装,如果有多个业务接口的话就要定义很多实现类和代理类才行。而且,如果代理类对业务方法的预处理、调用后操作都是一样的(比如:调用前输出提示、调用后自动关闭连接),则多个代理类就会有很多重复代码。
在这里插入图片描述

2 导入依赖

  <dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.0.8.RELEASE</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-test</artifactId><version>5.0.8.RELEASE</version><scope>test</scope></dependency><dependency><groupId>junit</groupId><artifactId>junit</artifactId><version>4.12</version><scope>test</scope></dependency></dependencies>

3 业务类

// 真实类与代理类需实现相同的接口
public interface EmployeeService {/*员工保存*/public int save(String username,String password);
}
// 真实类
public class EmployeeServiceImpl implements EmployeeService {@Overridepublic int save(String username, String password) {System.out.println("员工姓名:"+username+"密码:"+password);return 0;}
}

4 代理类

public class EmployeeServiceProxy implements EmployeeService {/*setter注入真实对象,增加事务操作*/private EmployeeServiceImpl employeeService;public void setEmployeeService(EmployeeServiceImpl employeeService) {this.employeeService = employeeService;}/*setter注入事务管理对象*/private TransactionManager transactionManager;public void setTransactionManager(TransactionManager transactionManager) {this.transactionManager = transactionManager;}/*执行真实对象的业务方法以及事务方法*/@Overridepublic int save(String username, String password) {try {/*开启事务*/transactionManager.begin();/*调用真实的业务方法*/employeeService.save(username,password);/*设置异常*/int num=10/0;/*提交事务*/transactionManager.commit();} catch (Exception e) {/*事务回滚*/transactionManager.rollback();e.printStackTrace();}return 0;}
}

5 事务类

public class TransactionManager {/*开启事务*/public void begin(){System.out.println("事务开启了.....");}/*提交事务*/public void commit(){System.out.println("事务提交了........");}/*回滚事务*/public void rollback(){System.out.println("事务回滚了.........");}
}

6 resources配置文件 – spring-core.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!--事务管理对象--><bean id="transactionManager" class="cn.tj.tx.TransactionManager"></bean><!--代理对象 为代理对象注入2Bean--><bean id="employeeServiceProxy" class="cn.tj.proxy.EmployeeServiceProxy"><property name="transactionManager" ref="transactionManager"></property><property name="employeeService" ><bean class="cn.tj.service.impl.EmployeeServiceImpl"></bean></property></bean>
</beans>

7 测试类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-core.xml")
public class EmployeeServiceTest {// employeeServiceProxy是bean的id 要一致@Autowiredprivate EmployeeService employeeServiceProxy;@Testpublic void emp_save() {//调用代理对象的方法employeeServiceProxy.save("叶凡","19216875523");}
}

三 JDK 动态代理

1 API

① java.lang.reflect.Proxy
  Java 动态代理机制生成的所有动态代理类的父类,它提供了一组静态方法来为一组接口动态地生成代理类及其对象。

// 主要方法
public static Object newProxyInstance(ClassLoader loader,
Class<?>[] interfaces, InvocationHandler hanlder)// 参数
loader :类加载器,一般传递真实对象的类加载器;
interfaces:代理类需要实现的接口;
handler:代理执行处理器,说人话就是生成代理对象帮你要做什么。
返回:创建的代理对象。// 作用
为指定类加载器、一组接口及调用处理器生成动态代理类实例。

② java.lang.reflect.InvocationHandler
  由代理类实例的调用处理程序实现的接口

// 主要方法
public Object invoke(Object proxy, Method method, Object[] args)// 参数
proxy :生成的代理对象;
method:当前调用的真实方法对象;
args :当前调用方法的实参。
返回:真实方法的返回结果。// 作用
负责集中处理动态代理类上的所有方法调用,让使用者自定义做什么事情,对原来方法增强(加什么功能)。

2 代理类 – TransactionInvocationhandler

// jdk动态代理执行器 实现 InvocationHandler 接口,实现 invoke 方法,实现增强操作
public class TransactionInvocationhandler implements InvocationHandler {/*注入真实对象:程序运行起来之后才会知道是什么对象*/private Object object;public void setObject(Object object) {this.object = object;}public Object getObject() {return object;}/*注入扩展对象:事务对象*/private TransactionManager transactionManager;public void setTransactionManager(TransactionManager transactionManager) {this.transactionManager = transactionManager;}/*将真实业务方法和扩展方法组合执行*/@Overridepublic Object invoke(Object proxy,Method method,Object[] args) throws Throwable {Object o=null;try {/*开启事务*/transactionManager.begin();/*执行真实对象的方法:反射调用类成员方法*/o= method.invoke(object,args);/*设置异常*/// int num=10/0;/*提交事务*/transactionManager.commit();} catch (Exception e) {e.printStackTrace();/*回滚事务*/transactionManager.rollback();}return o;}
}

3 配置文件 – spring-core.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://www.springframework.org/schema/beanshttp://www.springframework.org/schema/beans/spring-beans.xsd"><!--事务管理对象--><bean id="transactionManager" class="cn.tj.tx.TransactionManager"></bean><!--代理执行器对象--><bean id="transactionInvocationhandler" class="cn.tj.proxy.TransactionInvocationhandler"><property name="transactionManager" ref="transactionManager"></property><property name="object" ><bean class="cn.tj.service.impl.EmployeeServiceImpl"></bean></property></bean>
</beans>

4 真实类接口实现类与事务类不变

5 测试类

@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-core.xml")
public class EmployeeServiceTest {@Autowiredprivate TransactionInvocationhandler transactionInvocationhandler;@Testpublic void emp_save() {//创建代理对象 (静态方法) 返回值应为service 默认为Object 故强转EmployeeService employePproxy = (EmployeeService) Proxy.newProxyInstance(transactionInvocationhandler.getObject().getClass().getClassLoader(),//通过执行器对象获取到真实对象的类加载器transactionInvocationhandler.getObject().getClass().getInterfaces(),//通过执行器对象获取真实对象实现的接口transactionInvocationhandler);System.out.println(employePproxy);//调用代理对象的方法
//        employePproxy.save("李四","333333");}
}

四 CGLIB 动态代理

1 API

① org.springframework.cglib.proxy.Enhancer

  Enhancer是CGLIB中用于字节码增强的类,类似于JDK动态代理中的Proxy类。它能够代理普通Java类和接口,通过创建一个被代理类的子类来拦截所有的方法调用。Enhancer的主要方法包括setSuperclass设置代理类的父类,setCallback设置回调函数等。

② org.springframework.cglib.proxy.InvocationHandler

  类似 JDK 中 InvocationHandler,CGLIB 库中的一个接口,用于拦截目标对象的代理方法调用。当一个代理对象被调用时,会先调用该接口的实现类中的invoke方法,然后才会调用目标对象的方法

2 代理类 – TransactionInvocationhandler

// 导入cglib包的InvocationHandler 
public class TransactionInvocationhandler implements InvocationHandler {/*注入真实对象:程序运行起来之后才会知道是什么对象*/private Object object;public void setObject(Object object) {this.object = object;}public Object getObject() {return object;}/*注入扩展对象:事务对象*/private TransactionManager transactionManager;public void setTransactionManager(TransactionManager transactionManager) {this.transactionManager = transactionManager;}/*将真实业务方法和扩展方法组合执行*/@Overridepublic Object invoke(Object proxy,Method method,Object[] args) throws Throwable {Object o=null;try {/*开启事务*/transactionManager.begin();/*执行真实对象的方法:反射调用类成员方法*/o= method.invoke(object,args);/*设置异常*/
//            int num=10/0;/*提交事务*/transactionManager.commit();} catch (Exception e) {e.printStackTrace();/*回滚事务*/transactionManager.rollback();}return o;}
}

3 测试类

// 只须修改代理类和测试类 其他与jdk代理相同
@RunWith(SpringJUnit4ClassRunner.class)
@ContextConfiguration("classpath:spring-core.xml")
public class EmployeeServiceTest {@Autowiredprivate TransactionInvocationhandler transactionInvocationhandler;@Testpublic void emp_save() {//创建代理对象的生成类对象 通过他创建代理对象Enhancer enhancer=new Enhancer();//设置代理对象和真实对象的继承关系 将真实对象设为代理对象的父类enhancer.setSuperclass(transactionInvocationhandler.getObject().getClass());//设置代理对象需要执行的操作 形参为callback 实际传递为他的继承类的实现类enhancer.setCallback(transactionInvocationhandler);//创建代理对象EmployeeServiceImpl employeeService = (EmployeeServiceImpl) enhancer.create();//调用代理对象的方法employeeService.save("陈汉生","admin");}
}

五 动态代理小结

1 JDK 动态代理

  Java 动态代理是使用 java.lang.reflect 包中的 Proxy 类与 InvocationHandler 接口这两个来完成的。要使用 JDK 动态代理,真实类必须实现接口。JDK 动态代理将会拦截所有 pubic 的方法(因为只能调用接口中定义的方法),这样即使在接口中增加了新的方法,不用修改代码也会被拦截。
  动态代理的最小单位是类(类中某些方法都会被处理),如果只想拦截一部分方法,可以在 invoke 方法中对要执行的方法名进行判断。(代理类与真实类共同实现相同接口)

2 CGLIB 动态代理

  CGLIB 可以生成真实类的子类,并重写父类非 final 修饰符的方法。要求类不能是 final 的,要拦截的方法要是非 final、非 static、非 private 的。
  动态代理的最小单位是类(类中某些方法都会被处理),如果只想拦截一部分方法,可以在 invoke 方法中对要执行的方法名进行判断。(代理类是继承真实类)

3 选择

  从性能上看:Javassit 优于 CGLIB 优于 JDK,JDK 动态代理是基于实现接口的,CGLIB 和 Javassit 是基于继承真实类的。
  真实类实现了接口,优先选用 JDK 动态代理。若真实类没实现任何接口,使用 Javassit 和 CGLIB 动态代理。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.rhkb.cn/news/246119.html

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

朴素贝叶斯分类算法

1.分类算法 分类算法是有监督学习的一个核心问题&#xff0c;他从数据中学习一个分类决策函数或分类模型&#xff0c;对新的输入进行预测&#xff0c;输出变量取有限个离散值。 &#x1f30d;分类算法的内容是要求给定特征&#xff0c;让我们得出类别。 那么如何由指定特征&…

Asp.Net Core 获取应用程序相关目录

在ASP.NET Core中&#xff0c;可以通过以下三种方式获取应用程序所在目录&#xff1a; 1、使用AppContext.BaseDirectory属性&#xff1a; string appDirectory AppContext.BaseDirectory; 例如&#xff1a;D:\后端项目\testCore\test.WebApi\bin\Debug\net6.0\ 2、使用…

Leetcode刷题笔记题解(C++):LCR 153. 二叉树中和为目标值的路径

思路&#xff1a;利用回溯的思想&#xff0c;回溯的退出条件为当前节点为空&#xff0c;是符合路径的判断条件为路径和为目标值且叶子节点包含了&#xff0c;代码如下&#xff1a; /*** Definition for a binary tree node.* struct TreeNode {* int val;* TreeNode *…

【C++】入门基础

前言&#xff1a;C是在C的基础之上&#xff0c;容纳进去了面向对象编程思想&#xff0c;并增加了许多有用的库&#xff0c;以及编程范式等。熟悉C语言之后&#xff0c;对C学习有一定的帮助&#xff0c;因此从今天开始们将进入&#xff23;的学习。 &#x1f496; 博主CSDN主页:…

《动手学深度学习(PyTorch版)》笔记4.5

注&#xff1a;书中对代码的讲解并不详细&#xff0c;本文对很多细节做了详细注释。另外&#xff0c;书上的源代码是在Jupyter Notebook上运行的&#xff0c;较为分散&#xff0c;本文将代码集中起来&#xff0c;并加以完善&#xff0c;全部用vscode在python 3.9.18下测试通过。…

ES文档索引、查询、分片、文档评分和分析器技术原理

技术原理 索引文档 索引文档分为单个文档和多个文档。 单个文档 新建单个文档所需要的步骤顺序&#xff1a; 客户端向 Node 1 发送新建、索引或者删除请求。节点使用文档的 _id 确定文档属于分片 0 。请求会被转发到 Node 3&#xff0c;因为分片 0 的主分片目前被分配在 …

微信小程序(十七)自定义组件生命周期(根据状态栏自适配)

注释很详细&#xff0c;直接上代码 上一篇 新增内容&#xff1a; 1.获取手机状态栏的高度 2.验证attached可以修改数据 3.动态绑定样式数值 源码&#xff1a; myNav.js Component({lifetimes:{//相当于vue的created,因为无法更新数据被打入冷宫created(){},//相当于vue的mount…

【JS基础】事件对象event、环境对象this、事件的高级操作

文章目录 一、事件对象1.1 事件对象是什么&#xff1f;1.2 使用方法 二、环境对象this以及回调函数2.1 它是什么&#xff1f;2.2 演示示例 三、事件的高级操作3.1 事件流3.2 事件捕获3.3 事件冒泡以及阻止冒泡3.4 事件解绑3.5 mouseover和mouseenter事件的区别3.6 事件委托它是…

HTML新手教程

HTML入门 教程&#xff1a;【狂神说Java】HTML5完整教学通俗易懂_哔哩哔哩_bilibili 一.初识HTML HyperTextMarkupLanguage&#xff08;超文本标记语言&#xff09; 超文本包括&#xff1a;文字、图片、音频、视频、动画。 HTML5的优势 世界知名浏览器厂商对HTML5的支持市场的…

解决WinForms跨线程操作控件的问题

解决WinForms跨线程操作控件的问题 介绍 在构建Windows窗体应用程序时&#xff0c;我们通常会遇到需要从非UI线程更新UI元素的场景。由于WinForms控件并不是线程安全的&#xff0c;直接这样做会抛出一个异常&#xff1a;“控件’control name’是从其他线程创建的&#xff0c;…

每日OJ题_算法_二分查找⑦_力扣153. 寻找旋转排序数组中的最小值

目录 力扣153. 寻找旋转排序数组中的最小值 解析代码 力扣153. 寻找旋转排序数组中的最小值 153. 寻找旋转排序数组中的最小值 - 力扣&#xff08;LeetCode&#xff09; 难度 中等 已知一个长度为 n 的数组&#xff0c;预先按照升序排列&#xff0c;经由 1 到 n 次 旋转 后…

node学习过程中的终端命令

冷的哥们手真tm冷&#xff0c;打字都是僵的&#xff0c;屮 目录 一、在学习nodejs过程中用到的终端命令总结 一、在学习nodejs过程中用到的终端命令 node -v nvm install 20.11.0 nvm list nvm list available nvm on nvm -v nvm use 20.11.0 node加要运行的js文件路径 ps&a…

Keycloak - docker 运行 前端集成

Keycloak - docker 运行 & 前端集成 这里的记录主要是跟我们的项目相关的一些本地运行/测试&#xff0c;云端用的 keycloak 版本不一样&#xff0c;不过本地我能找到的最简单的配置是这样的 docker 配置 & 运行 keycloak keycloak 有官方(Red Hat Inc.)的镜像&#…

搭建Redis集群

一 应用场景 为什么需要redis集群&#xff1f; 当主备复制场景&#xff0c;无法满足主机的单点故障时&#xff0c;需要引入集群配置。 一般数据库要处理的读请求远大于写请求 &#xff0c;针对这种情况&#xff0c;我们优化数据库可以采用读写分离的策略。我们可以部 署一台…

数据结构与算法——队列

概述 计算机科学中&#xff0c;queue 是以顺序的方式维护的一组数据集合&#xff0c;在一端添加数据&#xff0c;从另一端移除数据。添加的一端称为尾&#xff0c;移除的一端称为头。 功能 插入offer(value : E) : boolean  取值并移除poll() : E  取值peek() : E  判断…

项目中日历管理学习使用

一些项目中会有日历或日期设置&#xff0c;最基本的会显示工作日&#xff0c;休息日&#xff0c;节假日等等&#xff0c;下面就是基于项目中的日历管理功能&#xff0c;要显示工作日&#xff0c;休息日&#xff0c;节假日 效果图 获取国家法定节假日工具类 public class Holi…

「QT」QString类的详细说明

✨博客主页何曾参静谧的博客📌文章专栏「QT」QT5程序设计📚全部专栏「VS」Visual Studio「C/C++」C/C++程序设计「UG/NX」BlockUI集合「Win」Windows程序设计「

Qt编写手机端视频播放器/推流工具/Onvif工具

一、视频播放器 同时支持多种解码内核&#xff0c;包括qmedia内核&#xff08;Qt4/Qt5/Qt6&#xff09;、ffmpeg内核&#xff08;ffmpeg2/ffmpeg3/ffmpeg4/ffmpeg5/ffmpeg6&#xff09;、vlc内核&#xff08;vlc2/vlc3&#xff09;、mpv内核&#xff08;mpv1/mp2&#xff09;、…

第十七讲_HarmonyOS应用开发Stage模型应用组件

HarmonyOS应用开发Stage模型应用组件 1. 应用级配置2. Module级配置3. Stage模型的组件3.1 AbilityStage3.1.1 AbilityStage的创建和配置3.1.2 AbilityStage的生命周期回调3.1.3 AbilityStage的事件回调&#xff1a; 3.2 UIAbility3.2.1 UIAbility生命周期3.2.3 UIAbility启动模…

修复idea,eclipse ,clion控制台中文乱码

控制台乱码问题主要原因并不在编译器IDE身上&#xff0c;还主要是Windows的控制台默认编码问题。。。 Powershell&#xff0c;cmd等默认编码可能不是UTF-8&#xff0c;无需改动IDE的settings或者properties&#xff08;这治标不治本&#xff09;&#xff0c;直接让Windows系统…