设计模式:代理模式 - 控制访问与增强功能的艺术
一、为什么使用代理模式?
在开发中,你是否遇到过以下问题:
• 某些功能调用需要权限校验,但不希望修改核心逻辑?
• 某些对象的创建开销过高,希望延迟加载以优化性能?
• 在不改变原始类的情况下,如何在调用前后插入日志或监控代码?
这些场景中,代理模式提供了一种优雅的解决方案。
二、什么是代理模式?
代理模式(Proxy Pattern)属于结构型设计模式,其核心思想是提供一种代理以控制对目标对象的访问。代理对象在客户端和目标对象之间起到中介的作用,并可以在不改变客户端代码的情况下增强或控制对目标对象的访问。从而有效提升系统的扩展性与灵活性。
代理模式的模型结构
1.抽象主题接口(Subject):定义目标对象和代理对象的公共方法。
2.真实主题(RealSubject):实现抽象主题接口,包含实际的业务逻辑。
3.代理类(Proxy):实现抽象主题接口,包含对真实主题的引用,从而操作真实主题并可提供访问控制和功能增强。
三、代理模式的主要类型及示例
代理模式主要类型
1.静态代理
特点:需要显示创建代理类,在编译期间确定代理类和目标类的绑定关系。
适用场景:功能明确、需求稳定的场景,如日志记录或权限校验等。
2.动态代理
特点:无需显示生成代理类,运行时会动态生成代理类。
适用场景:需要更高灵活性的场景
动态代理的2种实现机制:
• JDK 动态代理:通过 Java 反射机制实现,要求目标类实现接口。
• CGLib 动态代理:通过生成目标类的子类实现,无需目标类实现接口。
3.虚拟代理
特点:通过虚拟代理延迟实例化目标对象,仅在真正需要时才加载资源。 从而提高资源利用率和性能。
适用场景:对象的创建需要较大开销的场景 ,如大图片、视频加载等。
1.静态代理示例:对支付功能增加日志记录功能
第一步:定义支付接口 (目标功能)
// 定义公共接口
public interface Payment {void pay(double amount);
}
第二步:创建真实支付类
// 实现真实对象
public class RealPayment implements Payment {@Overridepublic void pay(double amount) {System.out.println("支付金额:" + amount + "元");}
}
第三步:创建代理支付类
实现支付接口,增加日志功能
// 静态代理类
public class PaymentProxy implements Payment {private final Payment realPayment;public PaymentProxy(Payment realPayment) {this.realPayment = realPayment;}@Overridepublic void pay(double amount) {// 在方法调用前后添加额外逻辑System.out.println("[代理日志] 支付前检查用户权限...");realPayment.pay(amount);System.out.println("[代理日志] 支付完成,记录支付日志。");}
}
第四步:客户端测试代码
通过代理对象调用真实对象的支付方法,隔离了客户端和真实对象。
// 客户端测试
public class ProxyTest {public static void main(String[] args) {Payment payment = new PaymentProxy(new RealPayment());payment.pay(100.0);}
}
输出结果:
[代理日志] 支付前检查用户权限...
支付金额:100.0元
[代理日志] 支付完成,记录支付日志。
2. 虚拟代理示例:图片延迟加载
通过虚拟代理延迟图片的创建,从而优化资源使用和性能。
第一步:定义图片接口
// 定义图片接口
public interface Image {void display(); // 显示图片
}
第二步:构建真实图片类
实现图片的加载和显示功能
// 真实图片类
public class RealImage implements Image {private String fileName;//构造函数public RealImage(String fileName) {this.fileName = fileName;loadFromDisk(); }//模拟从磁盘加载图片private void loadFromDisk() {System.out.println("Loading " + fileName);}@Overridepublic void display() {System.out.println("Displaying " + fileName);}
}
第三步:构建图片代理类
实现图片接口,并对真实图片类的创建进行控制。
// 图片代理类
public class ProxyImage implements Image {// 持有对真实图片对象的引用private RealImage realImage;// 保存图片文件名,用于真实图片对象的创建private String fileName;// 构造函数,初始化代理类时传入图片文件名public ProxyImage(String fileName) {this.fileName = fileName;}@Override// 实现 display 方法,增加延迟加载功能public void display() {// 检查 realImage 是否为 nullif (realImage == null) {// 只有realImage未创建时,才实例化。realImage = new RealImage(fileName);}// 调用真实图片对象的 display 方法realImage.display();}
}
第四步:测试代码,客户端调用
// 客户端调用
public class ProxyPatternDemo {public static void main(String[] args) {Image image1 = new ProxyImage("image1.jpg");Image image2 = new ProxyImage("image2.jpg");// 第一次调用 display,会触发图片加载image1.display();System.out.println("---");// 第二次调用 display,不会重复加载image1.display();System.out.println("---");// 另一张图片首次调用,会触发图片加载image2.display();}
}
运行结果:
Loading image1.jpg
Displaying image1.jpg
---
Displaying image1.jpg
---
Loading image2.jpg
Displaying image2.jpg
代码说明
1.Image 接口:定义图片的通用行为display()。
2.RealImage 类:真实图片类,实现了图片的真实加载和显示逻辑。
3.ProxyImage 类:代理图片类,控制真实图片类的加载和显示。
4.客户端代码:通过代理图片类调用真实图片类的display()方法,无需关心加载细节。
延迟加载机制:
• realImage在代理对象ProxyImage初始化时并未创建。
• ProxyImage会在首次显示图片时创建实例RealImage。
• 后续再调用display()方法时,因为真实图片类已创建,不会再次实例化加载图片。从而降低不必要的资源消耗。
如上示例代码已详细展示了如何在不同场景中应用代理模式,增强功能同时确保代码结构清晰。
3、动态代理 !!!!
动态代理通过“运行时动态生成代理类”,可帮助开发者快速解决功能增强和接口代理中的重复性问题,从而提升代码灵活性和开发效率。
注:
• AOP(面向切面编程):
Spring 的 AOP 框架,通过注解或配置实现功能增强,比动态代理更直观一些,尤其是在大型项目中。
• 模板模式:
通过定义模板方法,将重复逻辑抽象到父类中,也减少每个接口的重复实现。
动态代理模式是一种通过在运行时动态生成代理类来实现目标对象功能扩展的设计模式。它与静态代理的最大区别在于:动态代理无需手动编写代理类,代理类由框架或工具动态生成。
目前常见的动态代理实现方式有两种:
1.JDK 动态代理:基于 Java 反射机制,只能代理实现了接口的类。
2.CGLib 动态代理:基于字节码生成技术,可以代理没有实现接口的类。
JDK 动态代理是一种基于 Java 标准库java.lang.reflect包实现的代理方式,可以在运行时动态生成代理对象。代理对象实现和目标类同样的接口,并将方法调用转发给目标对象,同时可以在方法调用前后执行额外的增强处理。
生成流程:
1.创建代理类工厂
在调用Proxy.newProxyInstance方法时,动态生成代理类,该代理类会实现目标接口,并将方法调用委托给一个实现了InvocationHandler接口的实例。
2.定义 InvocationHandler 接口
InvocationHandler是 JDK 动态代理的核心接口。它只有一个invoke方法,当代理对象的方法被调用时,invoke方法会被自动触发。此方法接收以下三个参数:
• proxy:当前生成的代理对象;
• method:目标对象中被调用的方法;
• args:方法调用的参数数组。
3.调用代理对象的方法
当调用代理对象的方法时,InvocationHandler的invoke方法会被自动调用。开发者可以在invoke方法中添加增强逻辑(如日志记录、权限检查等),然后通过反射调用目标对象的方法。
4.通过反射执行目标方法
在invoke方法中,开发者可以使用反射机制调用目标对象的方法,并在调用前后插入额外的逻辑处理。最终,将目标方法的返回值返回给调用者。
JDK 动态代理通过这种机制实现了对目标方法调用的灵活控制,适合需要对实现了接口的类进行动态增强的场景。
JDK 动态代理实例
场景:在支付功能中动态添加日志。
步骤 1:定义接口
定义一个公共接口,供目标类和代理类实现。这是 JDK 动态代理的前提。
public interface Payment {void pay(int amount);
}
步骤 2:实现目标类
目标类实现接口,提供具体业务逻辑。
public class RealPayment implements Payment {@Overridepublic void pay(int amount) {System.out.println("Processing payment of $" + amount);}
}
步骤 3:实现 InvocationHandler
定义一个代理处理器类,实现InvocationHandler接口,用于自定义增强逻辑。
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;public class PaymentInvocationHandler implements InvocationHandler {private final Payment realPayment;// 构造方法,接收目标对象public PaymentInvocationHandler(Payment realPayment) {this.realPayment = realPayment;}@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {/*** @param proxy 动态生成的代理对象实例,通常很少直接使用。* @param method 目标对象中被调用的方法。* @param args 方法的参数数组。*/// 增强逻辑:方法调用前System.out.println("[Log] Starting payment...");// 调用目标方法Object result = method.invoke(realPayment, args);// 增强逻辑:方法调用后System.out.println("[Log] Payment finished!");return result;}
}
步骤 4:创建代理对象
通过Proxy.newProxyInstance方法生成代理对象。
import java.lang.reflect.Proxy;public class Main {public static void main(String[] args) {// 创建目标对象Payment realPayment = new RealPayment();// 创建代理对象Payment proxyPayment = (Payment) Proxy.newProxyInstance(realPayment.getClass().getClassLoader(), // 类加载器realPayment.getClass().getInterfaces(), // 目标对象实现的接口new PaymentInvocationHandler(realPayment) // 动态代理处理器);// 调用代理对象的方法proxyPayment.pay(100);}
}
以下是Proxy.newProxyInstance的三个参数及其含义:
1.realPayment.getClass().getClassLoader()
• 类加载器,传入目标对象的类加载器。
2.realPayment.getClass().getInterfaces()
• 目标对象实现的接口列表。
• 代理类会实现这些接口,从而与目标对象拥有相同的方法。
3.PaymentInvocationHandler(realPayment)
• 动态代理处理器,负责拦截代理对象的方法调用。
•InvocationHandler中定义了增强逻辑和目标方法的调用。
运行效果
运行上述代码,输出如下:
[Log] Starting payment...
Processing payment of $100
[Log] Payment finished!
CGLib(Code Generation Library)是一个基于 ASM(Java 字节码操作框架)实现的动态代理库。它的工作原理是,在程序运行时动态生成目标类的子类,并在子类中拦截目标方法的调用,从而实现代理功能。与 JDK 动态代理不同,CGLib 不需要目标类实现接口,而是直接通过继承目标类来创建代理类。所以可以代理没有实现接口的类。
生成流程:
1.创建Enhancer对象
CGLib 使用Enhancer类来生成代理类。通过创建Enhancer对象并设置目标类和拦截器等参数,最终生成代理类。
2.设置代理逻辑(回调拦截器)
实现MethodInterceptor接口,定义拦截逻辑。该接口的intercept方法会在每次调用代理类的方法时触发,开发者可以在intercept方法中添加增强功能,如日志记录、权限验证等。
3.动态生成代理类
CGLib 在运行时动态生成目标类的子类作为代理类。生成的代理类会继承目标类的所有方法,并在每个方法执行时触发intercept方法。
4.调用代理对象的方法
当调用代理对象的方法时,MethodInterceptor中的intercept方法会被触发,即增强逻辑(如日志记录、权限验证等)执行,目标类的实际方法通过MethodProxy.invokeSuper调用。
CGLib 动态代理实例
场景:动态代理一个没有实现接口的类,为其添加日志功能。
代码实现
步骤 1:定义目标类
目标类不需要实现接口,可以直接定义具体方法。
// 一个没有实现接口的普通类
public class UserService {// 模拟添加用户的操作public void addUser(String username) {System.out.println("添加用户:" + username);}// 模拟删除用户的操作public void deleteUser(String username) {System.out.println("删除用户:" + username);}
}
步骤 2:创建 CGLib 动态代理拦截器
创建一个类实现MethodInterceptor接口,用于定义增强逻辑。
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;import java.lang.reflect.Method;// 定义拦截器,为目标类的方法添加日志功能
public class LoggingInterceptor implements MethodInterceptor {/*** @param obj 代理对象本身(动态生成的代理类实例)* @param method 目标类的方法* @param args 目标方法的参数数组,按顺序包含每个参数值。* @param proxy 方法代理,用于调用目标类(父类)方法的代理工具* @return 方法执行结果* @throws Throwable 可能抛出的异常*/ @Overridepublic Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {// 在方法调用前添加日志System.out.println("[日志] 调用方法:" + method.getName() + ",参数:" + java.util.Arrays.toString(args));// 调用目标类的原始方法Object result = proxy.invokeSuper(obj, args);// 在方法调用后添加日志System.out.println("[日志] 方法执行完成:" + method.getName());return result;}
}
代码解析
1.proxy.invokeSuper(obj, args):调用目标类的原始方法。
2.intercept方法参数:
步骤3:代理工厂
import net.sf.cglib.proxy.Enhancer;// 创建代理对象的工厂类
public class CGLibProxyFactory {/*** 获取代理对象** @param clazz 需要代理的目标类* @return 代理对象*/public <T> T getProxy(Class<T> clazz) {// 创建 Enhancer 对象,用于生成代理类Enhancer enhancer = new Enhancer();// 设置目标类为代理类的父类enhancer.setSuperclass(clazz);// 设置回调拦截器,用于增强目标类的方法enhancer.setCallback(new LoggingInterceptor());// 创建代理对象return (T) enhancer.create();}
}
代码解析:
代理工厂 (CGLibProxyFactory):
• 使用 CGLib 的Enhancer创建动态代理对象。
• 设置目标类为代理类的父类,并将拦截器传递给Enhancer。
步骤4:测试代码
创建代理对象,调用目标方法时,会自动触发拦截器中的intercept方法。
public class Client {public static void main(String[] args) {// 使用代理工厂生成代理对象CGLibProxyFactory proxyFactory = new CGLibProxyFactory();UserService userServiceProxy = proxyFactory.getProxy(UserService.class);// 调用代理对象的方法userServiceProxy.addUser("Alice");System.out.println();userServiceProxy.deleteUser("Alice");}
}
CGLib实现的思路总结
1.定义目标类
目标类无需实现接口,直接定义业务逻辑。
2.创建拦截器
实现MethodInterceptor接口,编写增强逻辑。
3.生成代理对象
使用Enhancer设置目标类和拦截器,生成代理对象。
4.调用代理方法
代理对象的方法调用会触发拦截器,实现动态代理逻辑。
四、代理模式的价值
对开发的价值
1.单一职责:代理模式将对象的核心功能与附加功能(如延迟加载)解耦,优化代码结构。
2.扩展性强:通过代理类新增功能,无需修改核心功能类,符合开闭原则
代理模式有如下缺点:
1.增加复杂度:引入代理类,可能导致维护难度增加。
2.性能损耗:代理访问会增加额外开销。
动态代理:
动态代理的价值
1.提高代码复用性
动态代理可以统一处理多个类的公共逻辑,减少重复代码。
2.增强系统扩展性
在不修改目标类的情况下动态扩展功能,符合开闭原则。
3.简化代理类生成
通过运行时生成代理类,避免手动编写静态代理类。
4.便于模拟与隔离
动态代理可以轻松模拟接口行为,方便单元测试和集成测试。
动态代理的缺点
1.性能开销较大,依赖反射机制动态代理依赖反射机制来生成代理类、执行方法,尽管反射提供了强大的灵活性,但其性能开销较大。在高频次调用的场景下,相较于直接方法调用,动态代理的性能会有所下降,并可能引发类型安全问题。
2.增加代码复杂性,调试较困难动态代理虽然简化了代码编写,但也带来了额外的复杂性。在调试和追踪时,尤其是使用第三方库(如 Spring AOP 或 CGLib)时,代理的行为不容易直观理解,调试代理方法比直接调试目标方法更具挑战性。
3.可能导致内存泄漏如果代理对象未被正确释放,或者引用关系处理不当,可能导致内存泄漏。代理对象和目标对象之间的引用可能无法被垃圾回收器识别,导致内存无法及时释放。
五、代理模式的适用场景
1.远程代理:为远程对象提供本地代理,简化分布式系统中远程方法的调用。
2.虚拟代理:延迟加载高开销资源,优化性能。
3.安全代理:控制敏感资源的访问权限。
4.缓存代理:缓存高频访问的数据,减少重复计算或查询。
5.日志代理:自动记录操作日志。
6.增强功能代理:动态扩展事务、监控等功能。
动态代理
1.权限校验如用户角色验证、操作权限控制。
2.日志记录为方法调用添加日志,便于监控和调试。
3.事务管理在方法调用前后添加事务处理逻辑。
4.性能监控动态收集方法调用的性能数据。
5.接口适配动态创建接口实现,减少手动实现的工作量。
六、总结
代理模式的核心价值在于:通过引入代理对象,解耦目标对象与客户端,在保留系统灵活性的同时,为目标对象添加功能增强或行为控制,最终提升系统的可扩展性与稳定性。
动态代理是实现灵活功能扩展的有力工具,通过在运行时生成代理类,解决了静态代理代码冗余的问题。无论是JDK 动态代理还是CGLib 动态代理,它们都能够有效解耦客户端和目标对象,为系统提供更强的扩展性和灵活性。然而,在使用动态代理时,也需要权衡其可能带来的性能开销、代码复杂性和内存管理问题。因此,在设计时要根据具体场景谨慎选择代理方式,充分考虑这些潜在缺点。
实践建议:
• 避免代理类过多导致系统复杂度增加。
• 简单场景谨慎使用,避免过度设计。
相关文章:
设计模式:代理模式 - 控制访问与增强功能的艺术
一、为什么使用代理模式? 在开发中,你是否遇到过以下问题: • 某些功能调用需要权限校验,但不希望修改核心逻辑? • 某些对象的创建开销过高,希望延迟加载以优化性能? • 在不改变原始类的情…...
C++二分查找
一、模板①:向下取整(mid (l r) >> 1) while (l < r) {int mid l r >> 1; // 等价于 (l r) / 2(向下取整)if (check(mid)) r mid; // 保留左半区else l mid 1; // 舍弃左半区 } 适用场…...
一个Linux/Java乱码问题的解决
公司有个项目采用的是spring-boot启动方式,以前上线正常,前几天重新上线后,突然发现上传的文件名中文乱码了。我了解了一下具体情况,以前是正常的,而且测试环境也都是正常的,就是生产环境本次上线后突发从页…...
【Java设计模式】第11章 装饰者模式讲解
11.1 装饰者模式讲解 定义:动态扩展对象功能,替代继承。类型:结构型模式适用场景: 运行时动态添加功能避免类爆炸(如多层装饰)优点: 比继承更灵活符合开闭原则缺点: 增加类/对象数量调试复杂度高11.2 装饰者模式 Coding // 抽象组件 public abstract class BatterCake…...
通过AWS EKS 生成并部署容器化应用
今天给大家分享一个实战例子,如何在EKS上创建容器化应用并通过ALB来发布。先介绍一下几个基本概念: IAM, OpenID Connect (OIDC) 2014 年,AWS Identity and Access Management 增加了使用 OpenID Connect (OIDC) 的联合身份支持。此功能允许…...
nginx入门,部署静态资源,反向代理,负载均衡使用
Nginx在linux上部署静态资源 概念介绍 Nginx可以作为静态web服务器来部署静态资源。这里所说的静态资源是指在服务端真实存在,并且能够直接展示的一些文件,比如常见的html页面、css文件、js文件、图片、视频等资源。 相对于Tomcat,Nginx处理…...
智膳优选 | AI赋能的智慧食堂管理专家 —— 基于飞书多维表格和扣子(Coze)的智能解决方案
智膳优选 | AI赋能的智慧食堂管理专家 基于飞书多维表格和扣子(Coze)的智能解决方案 数据驱动餐饮管理,让每一餐都是营养与经济的完美平衡! “智膳优选”通过整合飞书与Coze,将数据智能引入校园餐饮管理࿰…...
深入解析 MySQL 中的日期时间函数:DATE_FORMAT 与时间查询优化、DATE_ADD、CONCAT
深入解析 MySQL 中的日期时间函数:DATE_FORMAT 与时间查询优化 在数据库管理和应用开发中,日期和时间的处理是不可或缺的一部分。MySQL 提供了多种日期和时间函数来满足不同的需求,其中DATE_FORMAT函数以其强大的日期格式化能力,…...
最新的es版本忘记密码,重置密码
刚刚安装了最新的es版本,就忘了密码,怎么重置密码呢? 一、进入es的斌目录 #进入es文件/bin 目录 ./elasticsearch-reset-password -u elastic 二 、输入对应的密码 然后再次访问 我的是去掉了ssl的访问 三、如果报错:解决 [main] WARN...
Compose Multiplatform+Kotlin Multiplatfrom 第五弹跨平台 截图
截图功能 Compose MultiplatformKotlin Multiplatfrom下实现桌面端的截图功能,起码搞了两星期,最后终于做出来了,操作都很流畅,截取的文件大小也正常,可参考支持讨论! 功能效果 代码实现 //在jvmMain下创…...
Elasticearch数据流向
Elasticearch数据流向 数据流向图 --- config: layout: elk look: classic theme: mc --- flowchart LR subgraph s1["图例"] direction TB W["写入流程"] R["读取流程"] end A["Logstash Pipeline"] -- 写入请求 --> B["Elas…...
在docker里装rocketmq-console
首先要到github下载(这个一般是需要你有梯子) GitHub - apache/rocketmq-externals at release-rocketmq-console-1.0.0 如果没有梯子,用下面这个百度网盘链接下 http://链接: https://pan.baidu.com/s/1x8WQVmaOBjTjss-3g01UPQ 提取码: fu…...
使用Python写入JSON、XML和YAML数据到Excel文件
在当今数据驱动的技术生态中,JSON、XML和YAML作为主流结构化数据格式,因其层次化表达能力和跨平台兼容性,已成为系统间数据交换的通用载体。然而,当需要将这类半结构化数据转化为具备直观可视化、动态计算和协作共享特性的载体时&…...
从零开始构建智能聊天机器人:Rasa与ChatGPT API实战教程
引言:AI对话系统的时代机遇 在数字化转型浪潮中,聊天机器人已成为连接用户与服务的关键纽带。无论是客服系统中的724小时即时响应,还是智能家居中的语音交互,聊天机器人正在重塑人机交互方式。本文将通过详细教程,手把…...
编码常见的 3类 23种设计模式——学习笔记
一、创建型(用于方便创建实例) 1. 单例模式 优点: 确保系统中只有一个实例存在,避免多个实例导致的资源冲突或数据不一致问题。例如,数据库连接池、线程池等全局资源管理器适合用单例实现。 减少频繁创建和销毁对象的开销,尤其适…...
# 实时人脸性别与年龄识别:基于OpenCV与深度学习模型的实现
实时人脸性别与年龄识别:基于OpenCV与深度学习模型的实现 在当今数字化时代,计算机视觉技术正以前所未有的速度改变着我们的生活与工作方式。其中,人脸检测与分析作为计算机视觉领域的重要分支,已广泛应用于安防监控、智能交互、…...
x-cmd install | Slumber - 告别繁琐,拥抱高效的终端 HTTP 客户端
目录 核心优势,一览无遗安装应用场景,无限可能示例告别 GUI,拥抱终端 还在为调试 API 接口,发送 HTTP 请求而苦恼吗?还在各种 GUI 工具之间切换,只为了发送一个简单的请求吗?现在,有…...
apijson 快速上手
apijson是强大的工具,简化了CRUD的操作,只要有数据库表,就能自动生成RESTFUL接口。但初次上手也是摸索了很长时间,尤其是部署与使用上,这里尝试以初学者角度来说下: 一、好处 1、对于简单的应用ÿ…...
3D激光轮廓仪知识整理
文章目录 1.原理和应用场景1.1 相机原理1.1.1 测量原理1.1.2 相机激光器1.1.3 沙姆镜头1.1.4 相机标定1.1.5 中心线提取 1.2 应用场景1.2.1 测量相关应用1.2.2 缺陷检测相关应用 2.相机参数介绍及选型介绍2.1 成像原理2.2 原始图成像2.3 生成轮廓图2.4 相机规格参数2.4.1 单轮廓…...
Stable Diffusion+Pyqt5: 实现图像生成与管理界面(带保存 + 历史记录 + 删除功能)——我的实验记录(结尾附系统效果图)
目录 🧠 前言 🧾 我的需求 🔧 实现过程(按功能一步步来) 🚶♂️ Step 1:基本图像生成界面 🗃️ Step 2:保存图片并显示历史记录 📏 Step 3:…...
使用WasmEdge将InternLM集成到Obsidian,打造本地智能笔记助手
本文来自社区投稿,作者Miley Fu,WasmEdge Runtime 创始成员。 本文将介绍如何通过 WasmEdge 将书生浦语(InternLM)大模型部署在本地,并与 Obsidian 笔记软件集成,从而在笔记软件中直接利用大模型实现文本总…...
深入理解Softmax函数及其在PyTorch中的实现
Softmax函数简介 Softmax函数在机器学习和深度学习中,被广泛用于多分类问题的输出层。它将一个实数向量转换为概率分布,使得每个元素介于0和1之间,且所有元素之和为1。 Softmax函数的定义 给定一个长度为 K K K的输入向量 z [ z 1 , z 2 …...
JGraphT 在 Spring Boot 中的应用实践
1. 引言 1.1 什么是 JGraphT JGraphT 是一个用于处理图数据结构和算法的 Java 库,提供了丰富的图类型和算法实现。 1.2 为什么使用 JGraphT 丰富的图类型:支持简单图、多重图、伪图等多种图类型。强大的算法库:提供最短路径、最小生成树、拓扑排序等多种算法。易于集成:…...
java导入excel更新设备经纬度度数或者度分秒
文章目录 一、背景介绍二、页面效果三、代码0.pom.xml1.ImportDevice.vue2.ImportDeviceError.vue3.system.js4.DeviceManageControl5.DeviceManageUserControl6.Repeater7.FileUtils8.ResponseModel9.EnumLongitudeLatitude10.词条 四、注意点本人其他相关文章链接 一、背景介…...
视频设备轨迹回放平台EasyCVR远程监控体系落地筑牢国土监管防线
一、背景概述 我国土地资源遭违法滥用的现象愈发严峻,各类土地不合理利用问题频发。不当的土地开发不仅加剧了地质危害风险,导致良田受损、森林资源的滥伐,还引发了煤矿无序开采、城市开发区违建等乱象,给国家宝贵的土地资源造成…...
tree-sitter 的 grammar.js 编写方法
tree-sitter 的 grammar.js 编写方法 一、grammar.js 的作用是什么?二、基本结构三、关键词解释四、编写小技巧1. 起点是 source_file2. 所有规则名(如 identifier, number)都是 $ > ...3. 正则表达式用于定义词法规则(终结符&…...
Git 实践笔记
这里写自定义目录标题 一、将当前改动追加到某次commit上二、git 强制修改分支位置 一、将当前改动追加到某次commit上 stash工作区中的当前改动 git stash假设需要修改的commit是 f744c32,将HEAD移动到需要改动的commit的父提交上 git rebase f744c32^ --interact…...
【特权FPGA】之数码管
case语句的用法: 计数器不断的计数,每一个num对应数码管一种数据的输出。实例通俗易懂,一目了然。 timescale 1ns / 1ps// Company: // Engineer: // // Create Date: // Design Name: // Module Name: // Project Name: //…...
Stable Diffusion 四重调参优化——项目学习记录
学习记录还原:在本次实验中,我基于 Stable Diffusion v1.5模型,通过一系列优化方法提升生成图像的质量,最终实现了图像质量的显著提升。实验从基础的 Img2Img 技术入手,逐步推进到参数微调、DreamShaper 模型和 Contro…...
遇到git提交报错:413
是因为提交文件过大导致内存溢出。 解决方法: 假设您的提交历史如下: Apply to .gitignore abcd123 当前提交 efgh456 包含node_modules的提交 ijkl789 较早的正常提交 您可以: 回退到添加node_modules之前的提交: bash App…...
