7.代理模式(Proxy Pattern)
古朗月行
- 代理模式
- JDK动态代理
- 代码示例
- 原码分析
- cglib动态代理
- 代码示例
- 源码分析
- JDK cglib动态代理对比
- ClassLoader
- 类的生命周期:
- 参考资料
唐 李白
小时不识月,呼作白玉盘。
又疑瑶台镜,飞在青云端。
仙人垂两足,桂树何团团。
白兔捣药成,问言与谁餐?
蟾蜍蚀圆影,大明夜已残。
羿昔落九乌,天人清且安。
阴精此沦惑,去去不足观。
忧来其如何,凄怆摧心肝
代理模式
In computer programming, the proxy pattern is a software design pattern. A proxy, in its most general form, is a class functioning as an interface to something else. The proxy could interface to anything: a network connection, a large object in memory, a file, or some other resource that is expensive or impossible to duplicate. In short, a proxy is a wrapper or agent object that is being called by the client to access the real serving object behind the scenes. Use of the proxy can simply be forwarding to the real object, or can provide additional logic. In the proxy, extra functionality can be provided, for example caching when operations on the real object are resource intensive, or checking preconditions before operations on the real object are invoked. For the client, usage of a proxy object is similar to using the real object, because both implement the same interface.
在计算机编程中,代理模式是一个软件设计模式。
JDK动态代理
动态代理的本质就是在运行时动态的生成一个代理类,这个代理类的加载同样遵循JVM类加载机制那一套东西,涉及到动态生成代理类的字节码并将其加载到JVM中。
代码示例
package org.example.proxy.jdk;public interface IService {void myselfMethod();
}
package org.example.proxy.jdk.impl;import org.example.proxy.jdk.IService;/*** 真的对象服务** @author Samson Bruce* @since 2024/11/26:11:33*/
public class RealObjectService implements IService {@Overridepublic void myselfMethod() {System.out.println("RealObjectService test .......");}
}
/*** 动态代理处理类** @author samson bruce* @since 2024/11/26:11:34* 1.拿到被代理对象的引用,然后获取它的接口* 2.jdk代理重新生成一个类,同时实现我们个额的代理对象所实现的接口* 3.把被代理对象的引用也拿到了* 4.重新动态生成一个class字节码* 5.然后编译*/
@Slf4j
public class DynamicInvocationHandler implements InvocationHandler {private Object target;public Object getInstance(IService target) throws Exception {this.target = target;Class<?> clazz = target.getClass();log.info("被代理对象的class是:{}", clazz);//一旦代理类被加载和链接,就可以使用 newProxyInstance 方法返回的 Constructor 对象来创建代理实例。// 这个构造器的 newInstance 方法接收一个 InvocationHandler 实例作为参数,并返回代理对象。return Proxy.newProxyInstance(clazz.getClassLoader(), clazz.getInterfaces(), this);}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {log.info("开始进行方法增强,(记录日志、校验、性能统计等)……");Object result = method.invoke(target, args);log.info("方法增强完毕,(记录日志、校验、性能统计等)……");return result;}
}
package org.example.proxy.jdk;import org.example.proxy.jdk.impl.RealObjectService;import java.io.FileOutputStream;
import java.io.IOException;
import java.lang.reflect.Executable;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;/*** @FileName: MainTest* @Description:* @Author: liulianglin* @Date: 2024/11/26:12:10*/
public class JDKProxyExampleMain {public static void printClassInfo(Executable[] targets) {for (Executable target : targets) {// 构造器/方法名称String name = target.getName();StringBuilder sBuilder = new StringBuilder(name);// 拼接左括号sBuilder.append('(');Class<?>[] clazzParams = target.getParameterTypes();// 拼接参数for (Class<?> clazzParam : clazzParams) {sBuilder.append(clazzParam.getName()).append(',');}//删除最后一个参数的逗号if (clazzParams.length != 0) {sBuilder.deleteCharAt(sBuilder.length() - 1);}//拼接右括号sBuilder.append(')');//打印 构造器/方法System.out.println(sBuilder.toString());}}/*** 将动态代理生成的代理类的字节码保存到本地磁盘,方便调试查看* @param path* @throws ClassNotFoundException* @throws NoSuchMethodException* @throws InvocationTargetException* @throws IllegalAccessException* @throws IOException*/public static void saveProxyClassFile(String path) {Class cl = null;FileOutputStream fileOutputStream = null;try {// Java11开始ProxyGenerator,不再public,改为了private,无法直接使用,所以采用反射的方式获取它cl = Class.forName("java.lang.reflect.ProxyGenerator");Method m =cl.getDeclaredMethod("generateProxyClass",String.class,Class[].class);m.setAccessible(true);byte[] $proxy1 = (byte[]) m.invoke(null, "$proxy1", RealObjectService.class.getInterfaces());System.out.println($proxy1.length);fileOutputStream = new FileOutputStream((path + "$Proxy.class"));fileOutputStream.write($proxy1);}catch (ClassNotFoundException e) {throw new RuntimeException(e);} catch (IOException e) {throw new RuntimeException(e);} catch (Exception e){e.printStackTrace();} finally {if (fileOutputStream !=null) {try {fileOutputStream.flush();fileOutputStream.close();} catch (IOException e) {throw new RuntimeException(e);}}}}public static void main(String[] args) {try {IService iService = (IService) new DynamicInvocationHandler().getInstance(new RealObjectService());iService.myselfMethod();// 将生成的动态代理类的字节码文件保存到本地saveProxyClassFile("./");} catch (Exception e) {throw new RuntimeException(e);}}
}
原码分析
- 确定类加载器
首先,Proxy.newProxyInstance 方法接收一个 ClassLoader 参数,这个参数指定了用于加载代理类的类加载器。通常,这个类加载器是被代理对象的类加载器,但也可以是其他任意的类加载器。
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h) {Objects.requireNonNull(h);final Class<?> caller = System.getSecurityManager() == null? null: Reflection.getCallerClass();/** Look up or generate the designated proxy class and its constructor.* Returns the Constructor object of a proxy class that takes a single argument of type InvocationHandler, given a class loader and an array of interfaces. * The returned constructor will have the accessible flag already set.*/Constructor<?> cons = getProxyConstructor(caller, loader, interfaces);return newProxyInstance(caller, cons, h);}
Returns the Constructor object of a proxy class that takes a single argument of type InvocationHandler, given a class loader and an array of interfaces.
private static Constructor<?> getProxyConstructor(Class<?> caller,ClassLoader loader,Class<?>... interfaces){// optimization for single interfaceif (interfaces.length == 1) {//如果只有一个接口,那么首先获取该接口Class<?> intf = interfaces[0];if (caller != null) {checkProxyAccess(caller, loader, intf);}//使用 proxyCache 缓存来获取或生成代理类的构造器。//proxyCache 是一个多层次的缓存结构,其中 sub(intf) 获取或创建与接口相关的子缓存,//computeIfAbsent 方法确保对于给定的类加载器 loader,只生成一次代理类。return proxyCache.sub(intf).computeIfAbsent(loader,(ld, clv) -> new ProxyBuilder(ld, clv.key()).build());} else {// interfaces clonedfinal Class<?>[] intfsArray = interfaces.clone();if (caller != null) {checkProxyAccess(caller, loader, intfsArray);}final List<Class<?>> intfs = Arrays.asList(intfsArray);return proxyCache.sub(intfs).computeIfAbsent(loader,(ld, clv) -> new ProxyBuilder(ld, clv.key()).build());}}
private static Object newProxyInstance(Class<?> caller, // null if no SecurityManagerConstructor<?> cons,InvocationHandler h) {/** Invoke its constructor with the designated invocation handler.*/try {if (caller != null) {checkNewProxyPermission(caller, cons.getDeclaringClass());}return cons.newInstance(new Object[]{h});} catch (IllegalAccessException | InstantiationException e) {throw new InternalError(e.toString(), e);} catch (InvocationTargetException e) {Throwable t = e.getCause();if (t instanceof RuntimeException) {throw (RuntimeException) t;} else {throw new InternalError(t.toString(), t);}}}
- 生成代理类的字节码(核心)
JDK动态代理通过以下关键步骤生成代理类的字节码:
- 创建代理类名称:代理类的名称是由 “$Proxy” + 一个数字序号 构成的,例如 P r o x y 0 、 Proxy0、 Proxy0、Proxy1 等。这个序号是为了保证代理类的名称唯一性。
- 实现接口:代理类实现了传入 newProxyInstance 方法的所有接口。
- 添加构造方法:代理类有一个私有的构造方法,它接受一个 InvocationHandler 类型的参数。
- 生成方法实现:对于每个接口方法,代理类都会生成一个实现,该实现在调用目标方法之前和之后会调用 InvocationHandler 的 invoke 方法。
- 字节码生成:JDK内部使用 ProxyBuilder 类(在 JDK 内部,不是公开API)来生成代理类的字节码。
- 加载代理类(JVM的那一套)
生成的字节码被传递给类加载器,由类加载器负责将字节码加载到JVM中,创建代理类的 Class 对象。这个过程包括:
- 定义类:类加载器使用 defineClass 方法将字节码定义为一个类。
- 链接类:链接过程包括验证字节码、准备类、解析符号引用等步骤。
- 初始化类:对于非抽象的代理类,JVM会执行类的初始化,包括执行类构造器 ()。
-
创建代理实例(使用过程)
一旦代理类被加载和链接,就可以使用 newProxyInstance 方法返回的 Constructor 对象来创建代理实例。这个构造器的 newInstance 方法接收一个 InvocationHandler 实例作为参数,并返回代理对象。 -
方法调用和拦截(使用过程)
当代理对象的方法被调用时,JVM会跳转到代理类中的方法实现,这些实现会委托给 InvocationHandler 的 invoke 方法来处理。invoke 方法负责执行实际的逻辑,包括调用目标对象的方法和添加额外的处理。
cglib动态代理
代码示例
源码分析
JDK cglib动态代理对比
JDK动态代理和CGLIB动态代理是Java中常用的两种代理技术,它们有以下几个主要区别:
- 代理方式的不同
JDK动态代理:JDK动态代理依赖于接口,它只能对实现了接口的类进行代理。通过 java.lang.reflect.Proxy 类生成代理对象。即被代理类必须实现至少一个接口。
CGLIB动态代理:CGLIB(Code Generation Library)是通过继承被代理类来创建代理对象的。CGLIB可以对没有实现接口的类进行代理。它使用字节码生成技术,在运行时动态地生成代理类。 - 是否需要接口
JDK动态代理:要求被代理的类必须实现接口,因此不能代理没有实现接口的类。
CGLIB动态代理:不需要接口,可以代理没有实现接口的类。 - 代理类的生成方式
JDK动态代理:通过 Proxy.newProxyInstance() 方法生成代理类,该方法使用反射机制来创建代理对象。
CGLIB动态代理:通过继承被代理类,并通过字节码技术生成代理类。CGLIB通过修改类的字节码实现代理,代理类是被继承的。 - 性能
JDK动态代理:因为它使用反射机制,性能相对较低,尤其是在大量调用代理方法时。
CGLIB动态代理:因为它是通过继承生成代理类,性能通常比JDK动态代理高。 - 代理目标的限制
JDK动态代理:只能代理接口,不能代理类。如果目标类没有接口,JDK动态代理不能使用。
CGLIB动态代理:能够代理没有接口的类,但它有一些限制。例如,如果目标类的方法是 final 或 private,CGLIB就无法代理该方法,因为CGLIB通过继承生成代理类,而 final 或 private 方法不能被重写。 - 使用场景
JDK动态代理:适用于目标类实现了接口的场景,特别是当接口较多时,JDK动态代理更加清晰和灵活。
CGLIB动态代理:适用于目标类没有接口或目标类的方法较多且没有接口的场景,CGLIB在这些场景中更具优势。 - Spring框架中的使用
在Spring框架中,Spring默认使用JDK动态代理进行AOP代理(前提是目标类实现了接口)。如果目标类没有实现接口,Spring则使用CGLIB来创建代理对象。
ClassLoader
类加载是Java中将类的字节码读入内存并准备好供程序使用的过程,这个过程由类加载器(Class Loader)负责。
类的生命周期:
- 加载 Loading 找到class文件
- 链接 Linking 链接是指将编译后生成的一个或多个目标文件(以及所需的库文件)合并为一个可执行文件的过程。这个过程由链接器(Linker)负责执行。
验证 Verification 检查字节码的正确性和安全性
准备 Preparation 分配内存并初始化类变量(静态变量)
解析 Analysis 将符号引用转换为直接引用,解决类间的依赖关系 - 初始化 Initialization 执行类的初始化方法,初始化类变量
- 使用 Using
- 卸载 Unloading
Java中的类加载器有多种类型,包括:
- 引导类加载器(Bootstrap Class Loader):加载Java核心类库。
- 扩展类加载器(Extension Class Loader):加载Java扩展库。
- 应用类加载器(Application Class Loader):加载应用程序的类。
类加载机制使得Java具有动态性和灵活性,可以在运行时加载类,从而支持多种应用场景。
参考资料
添加链接描述
Spring AOP
【深度思考】聊聊CGLIB动态代理原理
相关文章:
7.代理模式(Proxy Pattern)
古朗月行 代理模式JDK动态代理代码示例原码分析 cglib动态代理代码示例源码分析 JDK cglib动态代理对比ClassLoader类的生命周期: 参考资料 唐 李白 小时不识月,呼作白玉盘。 又疑瑶台镜,飞在青云端。 仙人垂两足,桂树何团团。…...
【效果】回到顶部功能实现
实现效果: 相关代码: <template><div class"cats" :style"{ top: catsTop }" ref"cats" click"catTop"></div> </template> 样式: /* 回到顶部 - 小猫咪 */ .cats {posi…...
项目搭建+修改
一 : 在列表成功回调函数,追加数据中,添加修改的按钮 for (let x of res) {//追加数据$("#table").append(<tr><td><input type"checkbox" class"ck" value"\${x.uid}"></td><td>\${x.uid}</td>…...
GD库如何根据颜色生成纯色背景图
GD库是一个用于图像处理的PHP扩展模块,它提供了一系列函数来创建、编辑和操作图像。要使用GD库根据颜色生成纯色背景图,可以按照以下步骤进行: 一、检查并安装GD库 检查GD库是否已安装: 可以通过运行phpinfo();或在命令行中使用p…...
Python 网络爬虫入门全知道
一、引言 在当今数字化时代,网络上的数据量呈爆炸式增长。无论是进行数据分析、市场调研,还是开发智能应用,获取网络数据都变得极为重要。而 Python 网络爬虫就是一把打开网络数据宝库的利器。它能够自动地从网页中抓取我们需要的信息&#…...
MATLAB期末复习笔记(下)
目录 五、数据和函数的可视化 1.MATLAB的可视化对象 2.二维图形的绘制 3.图形标识 4.多子图绘图 5.直方图的绘制 (1)分类 (2)垂直累计式 (3)垂直分组式 (4)水平分组式 &…...
基于大数据爬虫数据挖掘技术+Python的网络用户购物行为分析与可视化平台(源码+论文+PPT+部署文档教程等)
#1024程序员节|征文# 博主介绍:CSDN毕设辅导第一人、全网粉丝50W,csdn特邀作者、博客专家、腾讯云社区合作讲师、CSDN新星计划导师、Java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和学生毕业项目实战,高校老…...
【go】只读通道和只写通道
文章目录 概述1. 通道的方向2. 为什么会有只读通道和只写通道?3. 总结 概述 在 Go 中,只读通道和只写通道的概念通过通道的方向来实现。Go 语言允许你在函数参数中指定通道的方向,从而限制通道的使用方式,这样可以确保代码的清晰…...
带Burst AOT Settings移植问题
报错 burst问题 Burst AOT Settings 是 Unity 的 Burst Compiler 的一部分,用于预编译程序集(AOT,Ahead-Of-Time Compilation),以便在不支持 JIT(即时编译)的平台上运行,例如 iOS 和…...
Debezium日常分享系列之:Debezium Engine
Debezium日常分享系列之:Debezium Engine 依赖打包项目在代码中输出消息格式消息转换消息转换谓词高级记录使用引擎属性异步引擎属性数据库模式历史属性处理故障 Debezium连接器通常通过部署到Kafka Connect服务来运行,并配置一个或多个连接器来监视上游…...
运行 GreatSQL 时为什么要求关闭透明大页
在大部分运维规范中,一般都会要求在运行 GreatSQL/MySQL 的环境中要关闭透明大页,那么到底什么是透明大页,为什么要关闭,打开有什么风险吗? 在此之前,我也是有点懵的,本文试着回答这个疑问&…...
【Rive】Rive在Android上的简单应用
1 前言 Rive 是一款强大的矢量图编辑器,可以设计图形、也可以制作动画。Rive 提供了矩形、圆形、三角形、多边形、星形、钢笔、文字等工具来绘制各式各样的矢量图形;提供了平移、旋转、缩放等工具对矢量图形进行各种变换;提供了骨骼、约束、时…...
Base 崛起,SynFutures 或成生态系统中最具潜力应用
10月份的 Unchained Crypto 采访中,Solana 联合创始人 Anatoly 表示,通过观察活跃地址数、TVL、DeFi 版块、Meme 热潮和开发者生态等多个关键指标,察觉到 Base 势头正猛,成为以太坊生态最强劲的 L2。 11月下旬,小狐狸创…...
探索Go语言中的循环双向链表
简介 循环双向链表将双向链表的灵活性与循环结构相结合,使得每个节点都有一个指向前一个节点和后一个节点的指针,并且最后一个节点的Next指针指向头节点,形成一个闭环。本文将深入探讨如何在Go语言中实现和操作这种数据结构。 循环双向链表…...
Leetcode617.合并二叉树(HOT100)+Leetcode79. 单词搜索(HOT100)
链接 代码: class Solution { public:TreeNode* mergeTrees(TreeNode* root1, TreeNode* root2) {if(!root1)return root2;if(!root2)return root1;root1->valroot2->val;root1->left mergeTrees(root1->left,root2->left);root1->right merg…...
亚马逊云(AWS)使用root用户登录
最近在AWS新开了服务器(EC2),用于学习,遇到一个问题就是默认是用ec2-user用户登录,也需要密钥对。 既然是学习用的服务器,还是想直接用root登录,下面开始修改: 操作系统是࿱…...
使用Docker在Ubuntu 22.04上部署MySQL数据库的完整指南
使用Docker在Ubuntu 22.04上部署MySQL数据库的完整指南 在现代应用开发中,使用Docker来部署数据库已成为一种流行的做法。本文将详细介绍如何在Ubuntu 22.04系统上使用Docker部署最新版本的MySQL数据库,包括关键注意事项、详细步骤、闭坑指南以及总结。…...
算法笔记:力扣15、三数之和
思路: 实现代码 class Solution {public List<List<Integer>> threeSum(int[] nums) {List<List<Integer>> result new ArrayList<>(); Arrays.sort(nums); // 先对数组进行排序 for (int i 0; i < nums.length - 2; i) { /…...
perf list PMU 缓存事件
事件标识事件解释PMU事件路径l1d_cacheL1数据缓存的访问次数,L1缓存是CPU内部最快的缓存,位于距离CPU核心非常近的位置。armv8_pmuv3/l1d_cache/l1d_cache_lmiss_rd表示从L1数据缓存读取数据时发生缓存未命中的次数。armv8_pmuv3/l1d_cache_lmiss_rd/l1d…...
使用C#开发VTK笔记(一)-VTK开发环境搭建
一.使用C#开发VTK的背景 因为C#开发的友好性,一直都比较习惯于从C#开发程序。而长期以来,都希望有一个稳定可靠的三位工程数模的开发演示平台,经过多次对比之后,感觉VTK和OpenCasCade这两个开源项目是比较好的,但它们都是用C++编写的,我用C#形式开发,只能找到发布的C#组…...
2024Selenium自动化常见问题!
"NoSuchElementException"异常: 确保使用了正确的选择器来定位元素。可以使用id、class、XPath或CSS选择器等。 可以尝试使用find_elements方法来查找元素列表,并检查列表的长度来判断元素是否存在。 使用显式等待(WebDriverWait…...
考研英语翻译与大小作文
名词动化词 1 持有 harbor2 2 反映 mirror 3 缩短 bridge 4 使用 harness 5 掩饰 mask/veil 6 修改 tailor 7 汇集 pool 8 控制 curb 9 想象 picture 10 激发 trigger 拉丁…...
详解Rust异步编程
文章目录 多线程编程与异步编程对比并发模型对比分析异步编程基础概念及用法 Rust的异步编程通过async/await语法和Future特性提供了一种高效的方式来处理并发任务,尤其在I/O密集型操作中表现出色。async/await异步编程模型性能高,还能支持底层编程&…...
Vue + Element UI 实战技巧:如何实现 el-table 重新加载数据后折叠所有展开行
在 Vue 中使用 Element UI 的 el-table 组件时,如果你想要在数据重新加载后折叠所有行的展开状态,你可以通过维护一个数据属性来追踪哪些行是展开的,并在数据更新时重置这个属性。 以下是一个简单的示例来说明如何实现这个功能: …...
linux静态链接和动态链接
静态链接的特点 程序独立性高 静态链接是在程序编译时,将所有需要的目标文件以及它们所依赖的库文件中的代码和数据链接成一个可执行文件。一旦链接完成,这个可执行文件就包含了运行所需的全部内容,不依赖外部的库文件。例如,一个…...
计算机网络学习资料全攻略
计算机网络是计算机科学中一个非常重要的分支,它涉及到数据在计算机系统之间的传输和通信。随着互联网的快速发展,对计算机网络知识的掌握变得越来越重要。本文将为您提供一份全面的计算机网络学习资料指南,帮助您从基础到高级逐步深入学习。…...
第七课 Unity编辑器创建的资源优化_UI篇(UGUI)
上期我们学习了简单的Scene优化,接下来我们继续编辑器创建资源的UGUI优化 UI篇(UGUI) 优化UGUI应从哪些方面入手? 可以从CPU和GPU两方面考虑,CPU方面,避免触发或减少Canvas的Rebuild和Rebatch,…...
Go的简单问题问答
基础问题回答 Go 的主要特点是什么? 简洁:语法简化,减少复杂性。并发:内置 Goroutine 和 Channel,支持轻量级并发。静态类型:强类型语言,编译时检查错误。跨平台:编译生成独立的二进…...
SVN迁移至Git,保留commit提交记录
SVN迁移至Git 如何将 SVN 仓库迁移到 Git 并保留提交记录一、生成userinfo.txt二. 使用 git svn clone 命令迁移 SVN 到 Git2.1. 基本命令格式2.2. 示例:从 SVN 克隆到 Git参数说明:2.3 执行的过程遇到的窗口2.4. 迁移过程 三. 将 Git 仓库推送到远程 Gi…...
一站式指导:在Neo4j与PostgreSQL间实现高效数据同步
作者:后端小肥肠 🍇 我写过的文章中的相关代码放到了gitee,地址:xfc-fdw-cloud: 公共解决方案 🍊 有疑问可私信或评论区联系我。 🥑 创作不易未经允许严禁转载。 姊妹篇: 数据同步的艺术&#…...
linux-安全-iptables防火墙基础笔记
目录 一、 iptables链结构 五链 二、 iptables表结构 四表 三、 匹配流程 四、 语法 五、 匹配 1. 通用匹配 2. 隐含匹配 3. 显示匹配 六、 SNAT 七、 DNAT 八、 规则备份及还原 1. 备份 2. 还原 这篇将讲解iptables防火墙的基础知识 一、 iptables链结构 规则…...
Redis——主从复制原理
Redis的主从复制原理是其高可用性和分布式读取能力的重要基础。以下是Redis主从复制原理的详细解释: 一、主从复制的基本概念 Redis的主从复制是一种数据复制和备份的方式,它允许一个主节点(Master)将其所有的数据同步到一个或多…...
vue2 虚拟DOM 和 真实DOM (概念、作用、Diff 算法)
虚拟 DOM 和 真实DOM(概念、作用、Diff 算法) 1.1 概念 真实 DOM(Document Object Model):是浏览器中用于表示文档结构的树形结构。 <h2>你好</h2>虚拟DOM:用 JavaScript 对象来模拟真实 DOM…...
王道考研编程题总结
我还在完善中,边复习边完善(这个只是根据我自身总结的) 一、 线性表 1. 结构体 #define MaxSize 40 typedef struct{ElemType data[MaxSize];int length; }SqList 2. 编程题 1. 删除最小值 题意 :从顺序表中删除…...
手机租赁系统开发全攻略 创新服务助力企业智能转型
内容概要 在当今数字化飞速发展的时代,“手机租赁系统开发”正逐渐成为企业智能转型的必然选择。这一过程并不简单,但关键流程的解析将帮助企业理清思路。首先,了解需求和目标是基础,之后制定详细计划和流程图,让整件…...
git回退到某个版本git checkout和git reset命令的区别
文章目录 1. git checkout <commit>2. git reset --hard <commit>两者的区别总结推荐使用场景* 在使用 Git 回退到某个版本时, git checkout <commit> 和 git reset --hard <commit> 是两种常见的方式,但它们的用途和影响有很…...
如何使用Spring Boot进行Web开发?
Spring Boot 是一个基于 Java 的框架,它简化了新 Spring 应用的初始设置和开发过程。使用 Spring Boot 进行 Web 开发可以让你快速创建独立的、生产级别的基于 Spring 的应用。下面是使用 Spring Boot 进行 Web 开发的基本步骤: 文章目录 1. 环境准备2. …...
error=‘null‘], commandType=io.lettuce.core.RedisPublisher$SubscriptionCommand]
问题 查看java应用启动日志输出下面错误: errornull], commandTypeio.lettuce.core.RedisPublisher$SubscriptionCommand] Completing command LatencyMeteredCommand [typeINFO, outputStatusOutput [output# Server redis_version:4.0.14 redis_git_sha1:000…...
AI PC处理器ARM架构-引入NPU和大模型
AI PC处理器架构变化:ARM低功耗、引入NPU和大模型 AI进化加速端侧落地,新一轮浪潮蓄势待发(2024)”。ARM(Advanced RISC Machine)架构和x86架构是两种主要的处理器架构,它们在设计理念、应用场景和性能特点等方面有显著的差异。 ARM架构是一…...
python之opencv库Haar级联分类器检测人脸--‘haarcascade_frontalface_default.xml‘
python之opencv库Haar级联分类器检测人脸–‘haarcascade_frontalface_default.xml’ opencv库: 它由 Intel 公司发起并参与开发,其初衷是为了提供高效的计算机视觉算法实现。随着计算机视觉领域的发展,OpenCV不断更新和完善,吸引…...
「Mac畅玩鸿蒙与硬件37」UI互动应用篇14 - 随机颜色变化器
本篇将带你实现一个随机颜色变化器应用。用户点击“随机颜色”按钮后,界面背景会随机变化为淡色系颜色,同时显示当前的颜色代码,页面还会展示一只猫咪图片作为装饰,提升趣味性。 关键词 UI互动应用随机颜色生成状态管理用户交互…...
确定 POST 请求中的数据字段
在使用 requests 进行 HTTP 请求时,data 和 params 是两种常见的参数,用于传递不同类型的数据。以下是它们的作用和区别: 1. data 的作用 用于 POST 请求的主体。通常传递表单数据或 JSON 数据。在 HTTP 请求中,data 中的内容会…...
Linux - DNS服务器
六、DNS服务器 1、简介 DNS(Domain Name System)是互联网上的一项服务,它作为将域名和IP地址相互映射的一个分布式 数据库,能够使人更方便的访问互联网。 DNS系统使用的是网络的查询,那么自然需要有监听的port。DNS使…...
探究 SpringBoot 结合 MVC 高校办公室行政事务管理系统的设计与应用实现
摘 要 身处网络时代,随着网络系统体系发展的不断成熟和完善,人们的生活也随之发生了很大的变化,人们在追求较高物质生活的同时,也在想着如何使自身的精神内涵得到提升,而读书就是人们获得精神享受非常重要的途径。为了…...
蓝桥杯-扫雷
这题不难,就是麻烦一点,这里暴力求解了直接 题目链接: 扫雷 AC代码: import java.util.Scanner; // 1:无需package // 2: 类名必须Main, 不可修改public class Main {public static void main(String[] args) {Scanner scan ne…...
Hive高可用配置
在hive的商用上没有集群一说,而且它本身也不是数据库,只是hadoop的数据sql化工具,但是hive可以配置高可用,通常业内对元数据服务会开5个,而HS2服务开3个,来保证hive服务的高可用 配置方式也很简单…...
探索AI新世界!热门工具与学习资源免费获取
抖知书老师推荐: 人工智能技术的迅速发展让人们既充满期待又有些迷茫。有人担忧被AI技术取代,有人却积极拥抱这场科技浪潮。无论你处于哪种心态,人工智能已经深入到我们生活的方方面面。如果你希望轻松掌握最新的AI工具与动态,…...
MAUI APP开发蓝牙协议的经验分享:与跳绳设备对接
在开发MAUI应用程序时,蓝牙协议的应用是一个重要的环节,尤其是在需要与外部设备如智能跳绳进行数据交换的场景中。以下是我在开发过程中的一些经验和心得,希望能为你的项目提供帮助。 1. 蓝牙协议基础 蓝牙协议是无线通信的一种标准&#x…...
常见Linux命令(详解)
文章目录 常见Linux命令文件目录类命令pwd 打印当前目录的绝对路径ls 列出目录内容cd 切换路径mkdir 建立目录rmdir 删除目录touch 创建空文件cp 复制文件或目录rm 移除文件或者目录mv 移动文件与目录或重命名cat 查看文件内容more 文件分屏查看器less 分屏显示文件内容head 显…...
LeetCode763. 划分字母区间(2024冬季每日一题 23)
给你一个字符串 s 。我们要把这个字符串划分为尽可能多的片段,同一字母最多出现在一个片段中。 注意,划分结果需要满足:将所有划分结果按顺序连接,得到的字符串仍然是 s 。 返回一个表示每个字符串片段的长度的列表。 示例 1&a…...