深入理解JDK动态代理原理,使用javassist动手写一个动态代理框架
文章目录
- 一、动手实现一个动态代理框架
- 1、初识javassist
- 2、使用javassist实现一个动态代理框架
- 二、JDK动态代理
- 1、编码实现
- 2、基本原理
- (1)getProxyClass0方法
- (2)总结
- 写在后面
一、动手实现一个动态代理框架
1、初识javassist
Java操纵字节码,最底层一般是使用ASM进行操作的,但是ASM上手难度很大,我们可以使用javassist对字节码进行操作,相对来说简单一些。
下面这个实例,就是我们使用javassist对接口动态生成一个实现类:
import javassist.ClassPool;
import javassist.CtClass;
import javassist.CtMethod;
import javassist.CtNewMethod;public class JavassistDemo {public static void main(String[] args) throws Exception {TestService proxy = createProxy();proxy.sayHello("zhangsan"); // hello:zhangsan}/*** 生成一个TestService的实现类*/public static TestService createProxy() throws Exception {// javassist 底层是ASM,ASM底层是编辑JVM指令码ClassPool classPool = new ClassPool();// 添加classLoaderclassPool.appendSystemPath();// 1.创建一个类CtClass class1 = classPool.makeClass("TestServiceImpl");class1.addInterface(classPool.get(TestService.class.getName()));// 2.创建一个方法CtMethod satHelloMethod = CtNewMethod.make(CtClass.voidType, // void返回值"sayHello", // 方法名new CtClass[]{classPool.get(String.class.getName())}, // 方法参数new CtClass[0], // 异常类型"{System.out.println(\"hello:\"+$1);}", // 方法体内容,$1表示第一个参数class1 // 指定类);class1.addMethod(satHelloMethod);// 3.实例化这个对象Class aClass = classPool.toClass(class1);// 强制转换return (TestService) aClass.newInstance();}public interface TestService {void sayHello(String name);}
}
2、使用javassist实现一个动态代理框架
import javassist.*;
import java.lang.reflect.Method;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.Arrays;
import java.util.stream.Collectors;public class Javassist3Demo {public static void main(String[] args) throws Exception {TestService proxy = createProxy(TestService.class, new InvocationHandler() {@Overridepublic Object invoke(String methodName, Object[] args) {// 根据方法判断逻辑if(methodName.equals("sayHello3")) {System.out.println("hello" + args[0]);return "aa";} else {System.out.println("hello2" + args[0]);return "aa2";}}});proxy.sayHello("zhangsan"); // hellozhangsanproxy.sayHello2("zz", 1);System.out.println(proxy.sayHello3("qq"));}static int count = 0;public static <T> T createProxy(Class<T> classInterface, InvocationHandler handler) throws Exception {ClassPool classPool = new ClassPool();classPool.appendSystemPath();// 1.创建一个类CtClass impl = classPool.makeClass("$Proxy" + count ++);impl.addInterface(classPool.get(classInterface.getName()));// 2.impl类中 添加属性handlerCtField fie = CtField.make("public com.mydemo.Javassast3Demo.InvocationHandler handler=null;", impl);impl.addField(fie);// 有返回值类型和无返回值类型的源码String src = "return ($r)this.handler.invoke(\"%s\", $args);"; // $args获取所有参数String voidSrc = "this.handler.invoke(\"%s\",$args);";for (Method method : classInterface.getMethods()) {CtClass returnType = classPool.get(method.getReturnType().getName());String name = method.getName();CtClass[] parameters = toCtClass(classPool, method.getParameterTypes());CtClass[] errors = toCtClass(classPool, method.getExceptionTypes());// 2.创建一个方法CtMethod newMethod = CtNewMethod.make(returnType, // 返回值name, // 方法名parameters, // 方法参数errors, // 异常类型method.getReturnType().equals(Void.class) ? String.format(voidSrc, method.getName()) : String.format(src, method.getName()), // 方法体内容impl // 指定类);impl.addMethod(newMethod);}// 生成字节码(辅助学习用)//byte[] bytes = impl.toBytecode();//Files.write(Paths.get(System.getProperty("user.dir") + "/target/" + impl.getName() + ".class"), bytes);// 3.实例化这个对象Class aClass = classPool.toClass(impl);T t = (T) aClass.newInstance();aClass.getField("handler").set(t, handler); // 初始化赋值// 强制转换return t;}private static CtClass[] toCtClass(ClassPool pool, Class[] classes) {return Arrays.stream(classes).map(c -> {try {return pool.get(c.getName());} catch (NotFoundException e) {throw new RuntimeException(e);}}).collect(Collectors.toList()).toArray(new CtClass[0]);}public interface InvocationHandler {Object invoke(String methodName, Object args[]);}public class InvocationHandlerImpl implements InvocationHandler {@Overridepublic Object invoke(String methodName, Object[] args) {System.out.println("hello");return null;}}public interface TestService {void sayHello(String name);void sayHello2(String name, Integer id);String sayHello3(String name);}
}
这样,我们只要实现InvocationHandler 接口,就可以自定义代理类的核心逻辑了,我们对生成的代理类进行反编译:
public class $Proxy0 implements TestService {public InvocationHandler handler = null;public String sayHello3(String var1) {return (String)this.handler.invoke("sayHello3", new Object[]{var1});}public void sayHello(String var1) {this.handler.invoke("sayHello", new Object[]{var1});}public void sayHello2(String var1, Integer var2) {this.handler.invoke("sayHello2", new Object[]{var1, var2});}public $Proxy0() {}
}
实现了我们的接口,并且重写了接口的所有方法,最终执行的是InvocationHandler的invoke方法。
这也正是JDK动态代理的基本思想。
二、JDK动态代理
1、编码实现
public interface EchoService {String echo(String message) throws NullPointerException;
}public class DefaultEchoService implements EchoService {@Overridepublic String echo(String message) {return "[ECHO] " + message;}
}import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;/*** JDK动态代理实例*/
public class JDKDynamicProxyDemo {public static void main(String[] args) {ClassLoader classLoader = Thread.currentThread().getContextClassLoader();// 真实的对象DefaultEchoService realObj = new DefaultEchoService();// 代理的对象Object proxy = Proxy.newProxyInstance(classLoader, new Class[]{EchoService.class}, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {System.out.println("动态前置");Object obj = null;if (EchoService.class.isAssignableFrom(method.getDeclaringClass())) {// 执行真实方法obj = method.invoke(realObj, args);}System.out.println("动态后置");return obj;}});EchoService echoService = (EchoService) proxy;System.out.println(echoService.echo("Hello,World"));}
}
我们发现,使用JDK动态代理,基本逻辑和我们上面使用javassist手写的工具类差不多,只不过JDK动态代理底层是使用更复杂的方式实现的,我们这里取巧,使用javassist实现。
注意!这里需要DefaultEchoService 实现EchoService接口,代理的其实是EchoService接口而不是DefaultEchoService 类。
动态代理对原代码没有侵入性,通常可以动态加载。
2、基本原理
为什么 Proxy.newProxyInstance 会生成新的字节码?
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();Object proxy = Proxy.newProxyInstance(classLoader, new Class[]{EchoService.class}, new InvocationHandler() {@Overridepublic Object invoke(Object proxy, Method method, Object[] args) throws Throwable {return null;}
});System.out.println(proxy.getClass());// com.sun.proxy.$Proxy0Object proxy2 = Proxy.newProxyInstance(classLoader, new Class[]{Comparable.class}, (proxy1, method, args1) -> {return null;});System.out.println(proxy2.getClass());// com.sun.proxy.$Proxy1
上面代码我们会发现,Java动态代理每生成一个代理,它的class总是com.sun.proxy包下的$Proxy*,从0开始累加,它是如何实现的呢?
我们来分析一下Proxy的newProxyInstance方法:
// java.lang.reflect.Proxy#newProxyInstance
@CallerSensitive
public static Object newProxyInstance(ClassLoader loader,Class<?>[] interfaces,InvocationHandler h)throws IllegalArgumentException
{Objects.requireNonNull(h);// 对象克隆final Class<?>[] intfs = interfaces.clone();final SecurityManager sm = System.getSecurityManager();if (sm != null) {checkProxyAccess(Reflection.getCallerClass(), loader, intfs);}/** Look up or generate the designated proxy class.*/// 先从缓存获取(见 (1))Class<?> cl = getProxyClass0(loader, intfs);/** Invoke its constructor with the designated invocation handler.*/try {if (sm != null) {checkNewProxyPermission(Reflection.getCallerClass(), cl);}// 获取代理对象的构造方法,带着InvocationHandler参数的构造方法final Constructor<?> cons = cl.getConstructor(constructorParams);final InvocationHandler ih = h;if (!Modifier.isPublic(cl.getModifiers())) {AccessController.doPrivileged(new PrivilegedAction<Void>() {public Void run() {cons.setAccessible(true);return null;}});}// 返回Proxy对象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);}} catch (NoSuchMethodException e) {throw new InternalError(e.toString(), e);}
}
(1)getProxyClass0方法
在getProxyClass0方法中,从proxyClassCache缓存中获取了这个代理类:
// java.lang.reflect.Proxy#getProxyClass0
private static Class<?> getProxyClass0(ClassLoader loader,Class<?>... interfaces) {if (interfaces.length > 65535) {throw new IllegalArgumentException("interface limit exceeded");}// If the proxy class defined by the given loader implementing// the given interfaces exists, this will simply return the cached copy;// otherwise, it will create the proxy class via the ProxyClassFactoryreturn proxyClassCache.get(loader, interfaces);
}
而proxyClassCache在初始化时,自动创建了KeyFactory和ProxyClassFactory
private static final WeakCache<ClassLoader, Class<?>[], Class<?>>proxyClassCache = new WeakCache<>(new KeyFactory(), new ProxyClassFactory());
ProxyClassFactory的核心方法apply,隐藏着代理接口的创建逻辑:
// java.lang.reflect.Proxy.ProxyClassFactory
private static final class ProxyClassFactoryimplements BiFunction<ClassLoader, Class<?>[], Class<?>>
{// prefix for all proxy class namesprivate static final String proxyClassNamePrefix = "$Proxy";// next number to use for generation of unique proxy class namesprivate static final AtomicLong nextUniqueNumber = new AtomicLong();@Overridepublic Class<?> apply(ClassLoader loader, Class<?>[] interfaces) {Map<Class<?>, Boolean> interfaceSet = new IdentityHashMap<>(interfaces.length);for (Class<?> intf : interfaces) { // 遍历我们传入的接口数组/** Verify that the class loader resolves the name of this* interface to the same Class object.*/Class<?> interfaceClass = null;try {// 通过classLoader加载我们的接口interfaceClass = Class.forName(intf.getName(), false, loader);} catch (ClassNotFoundException e) {}if (interfaceClass != intf) {throw new IllegalArgumentException(intf + " is not visible from class loader");}/** Verify that the Class object actually represents an* interface.*/if (!interfaceClass.isInterface()) {// 只能代理接口,非接口直接抛异常throw new IllegalArgumentException(interfaceClass.getName() + " is not an interface");}/** Verify that this interface is not a duplicate.*/if (interfaceSet.put(interfaceClass, Boolean.TRUE) != null) {throw new IllegalArgumentException("repeated interface: " + interfaceClass.getName());}}String proxyPkg = null; // package to define proxy class inint accessFlags = Modifier.PUBLIC | Modifier.FINAL;/** Record the package of a non-public proxy interface so that the* proxy class will be defined in the same package. Verify that* all non-public proxy interfaces are in the same package.*/for (Class<?> intf : interfaces) {int flags = intf.getModifiers();if (!Modifier.isPublic(flags)) {accessFlags = Modifier.FINAL;String name = intf.getName();int n = name.lastIndexOf('.');String pkg = ((n == -1) ? "" : name.substring(0, n + 1));if (proxyPkg == null) {proxyPkg = pkg;} else if (!pkg.equals(proxyPkg)) {throw new IllegalArgumentException("non-public interfaces from different packages");}}}if (proxyPkg == null) { // 包名就是com.sun.proxy// if no non-public proxy interfaces, use com.sun.proxy packageproxyPkg = ReflectUtil.PROXY_PACKAGE + ".";}/** Choose a name for the proxy class to generate.*/long num = nextUniqueNumber.getAndIncrement();String proxyName = proxyPkg + proxyClassNamePrefix + num; // 依次递增/** Generate the specified proxy class.*/// 代理类生成器,返回字节数组,就是字节码byte[] proxyClassFile = ProxyGenerator.generateProxyClass(proxyName, interfaces, accessFlags);try {// classLoader加载类,是一个native方法,返回一个Class对象return defineClass0(loader, proxyName,proxyClassFile, 0, proxyClassFile.length);} catch (ClassFormatError e) {/** A ClassFormatError here means that (barring bugs in the* proxy class generation code) there was some other* invalid aspect of the arguments supplied to the proxy* class creation (such as virtual machine limitations* exceeded).*/throw new IllegalArgumentException(e.toString());}}
}
(2)总结
vm options参数设置-Dsun.misc.ProxyGenerator.saveGeneratedFiles=true,就可以把生成的代理类的源码保存在com.sun.proxy目录下面,或者用arthas来查看运行中的类信息。
JDK动态代理生成的代理类,我们通过反编译,发现其实是这个样子的:
package com.sun.proxy;import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.lang.reflect.UndeclaredThrowableException;
import com.demo.EchoService;public final class $Proxy0
extends Proxy
implements EchoService {private static Method m1;private static Method m3;private static Method m2;private static Method m0;public $Proxy0(InvocationHandler invocationHandler) {super(invocationHandler);}static {try {m1 = Class.forName("java.lang.Object").getMethod("equals", Class.forName("java.lang.Object"));m3 = Class.forName("com.demo.EchoService").getMethod("echo", Class.forName("java.lang.String"));m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);return;}catch (NoSuchMethodException noSuchMethodException) {throw new NoSuchMethodError(noSuchMethodException.getMessage());}catch (ClassNotFoundException classNotFoundException) {throw new NoClassDefFoundError(classNotFoundException.getMessage());}}public final boolean equals(Object object) {try {return (Boolean)this.h.invoke(this, m1, new Object[]{object});}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}public final String toString() {try {return (String)this.h.invoke(this, m2, null);}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}public final int hashCode() {try {return (Integer)this.h.invoke(this, m0, null);}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}public final String echo(String string) throws NullPointerException {try {return (String)this.h.invoke(this, m3, new Object[]{string});}catch (Error | RuntimeException throwable) {throw throwable;}catch (Throwable throwable) {throw new UndeclaredThrowableException(throwable);}}
}
InvocationHandler就是Proxy.newProxyInstance传入的最后一个参数。
当调用代理对象的方法时,会执行InvocationHandler的invoke方法。
注意,JDK生成的代理类的包名不总是com.sun.proxy,只有当接口为Public时是这样的,当接口为非public时,生成的代理类与接口所在包名相同。
写在后面
如果本文对你有帮助,请点赞收藏关注一下吧 ~
相关文章:

深入理解JDK动态代理原理,使用javassist动手写一个动态代理框架
文章目录一、动手实现一个动态代理框架1、初识javassist2、使用javassist实现一个动态代理框架二、JDK动态代理1、编码实现2、基本原理(1)getProxyClass0方法(2)总结写在后面一、动手实现一个动态代理框架 1、初识javassist Jav…...

一、策略模式的使用
1、策略模式定义: 策略模式(Strategy Pattern)定义了一组策略,分别在不同类中封装起来,每种策略都可以根据当前场景相互替换,从而使策略的变化可以独立于操作者。比如我们要去某个地方,会根据距…...

Verilog使用always块实现时序逻辑
这篇文章将讨论 verilog 中一个重要的结构---- always 块(always block)。verilog 中可以实现的数字电路主要分为两类----组合逻辑电路和时序逻辑电路。与组合逻辑电路相反,时序电路电路使用时钟并一定需要触发器等存储元件。因此,…...

面向对象设计模式:行为型模式之迭代器模式
一、迭代器模式,Iterator Pattern aka:Cursor Pattern 1.1 Intent 意图 Provide a way to access the elements of an aggregate object sequentially without exposing its underlying representation. 提供一种按顺序访问聚合对象的元素而不公开其基…...

如何快速在企业网盘中找到想要的文件
现在越来越多的企业采用企业网盘来存储文档和资料,而且现在市面上的企业网盘各种各样。在使用企业网盘过程中,很多用户会问到企业网盘中如何快速搜索文件的问题。但是无论是“标签”功能还是普通的“关键词搜索”功能,都是单层级的࿰…...

香橙派5使用NPU加速yolov5的实时视频推理(二)
三、将best.onnx转为RKNN格式 这一步就需要我们进入到Ubuntu20.04系统中了,我的Ubuntu系统中已经下载好了anaconda,使用anaconda的好处就是可以方便的安装一些库,而且还可以利用conda来配置虚拟环境,做到环境与环境之间相互独立。…...
算法练习-二分查找(一)
算法练习-二分查找 1 代码实现 1.1 非递归实现 public int bsearch(int[] a, int n, int value) {int low 0;int high n - 1;while (low < high) {int mid (low high) / 2;if (a[mid] value) {return mid;} else if (a[mid] < value) {low mid 1} else {high …...

通用业务平台设计(五):预警平台建设
前言 在上家公司,随着业务的不断拓展(从支持单个国家单个主体演变成支持多个国家多个主体),对预警的诉求越来越紧迫;如何保障业务的稳定性那?预警可以帮我们提前甄别风险,从而让我们可以在风险来临前将其消灭ÿ…...

Windows openssl-1.1.1d vs2017编译
工具: 1. perl(https://strawberryperl.com/) 2. nasm(https://nasm.us/) 3. openssl源码(https://www.openssl.org/) 可以自己去下载 或者我的网盘提供下载: 链接:…...

【深蓝学院】手写VIO第2章--IMU传感器--笔记
0. 内容 1. 旋转运动学 角速度的推导: 左ω∧\omega^{\wedge}ω∧,而ω\omegaω是在z轴方向运动,θ′[0,0,1]T\theta^{\prime}[0,0,1]^Tθ′[0,0,1]T 两边取模后得到结论: 线速度大小半径 * 角速度大小 其中,对旋转矩…...

网络基础(二)之HTTP与HTTPS
应用层 再谈 "协议" 协议是一种 "约定". socket api的接口, 在读写数据时, 都是按 "字符串" 的方式来发送接收的. 如果我们要传输一些"结构化的数据" 怎么办呢? 为什么要转换呢? 如果我们将struct message里面的信息…...

Python每日一练(20230306)
目录 1. 翻转二叉树 ★★ 2. 最长公共前缀 ★★ 3. 2的幂 ★ 1. 翻转二叉树 翻转一棵二叉树。 示例 1: 输入: 4/ \2 7/ \ / \ 1 3 6 9 输出: 4/ \7 2/ \ / \ 9 6 3 1示例 2: 输入: 1…...

C/C++每日一练(20230305)
目录 1. 整数分解 ☆ 2. 二叉树的最小深度 ★★ 3. 找x ★★ 1. 整数分解 输入一个正整数,将其按7进制位分解为各乘式的累加和。 示例 1: 输入:49 输出:497^2示例 2: 输入:720 输出:720…...
SAS字典的应用
数据字典中常用信息检索DICTIONARY.COLUMNS、DICTIONARY.TABLES以及DICTIONARY.MEMBERS等字典表的内容。在编程实践中,如何以SAS字典表来提高效率。 1、DICTIONARY.COLUMNS 对于当前SAS任务的全部数据集,表格DICTIONARY.COLUMNS包含了诸如变量的名称、类…...
Mysql中的函数和触发器
函数函数是什么?多用于查询语句,实现了某种功能;用途与存储过程不同,但语法是类似的;函数语法create function 函数名([参数列表]) returns 数据类型 begin DECLARE 变量; sql 语句; return 值; end; 设置函…...

分布式架构之(Zookeeper原理)
Zookeeper是一个典型的分布式数据一致性的结局方案,分布式应用程序可以基于它实现注入数据发布、订阅、负载均衡、命名服务、分布式协调/通知、集群管理、Master选举、分布式锁和分布式队列等功能, Zookeeper可以保证如下分布式一致性特性: 顺…...
Java框架学习 | MyBatis
问题导向学习MyBatis 为什么要有MyBatis框架? 避免Java开发者直接使用 JDBC重复做数据库操作,同时更便捷地实现想要的数据库相关功能,让Java专注于开发业务。 MyBatis框架如何实现该目的? MyBatis是半自动化持久层ORM框架&#x…...

Cookie+Session详解
文章目录批量删除会话技术简介CookieCookie 查看Cookie 的删除Cookie 使用页面获取 cookie 信息cookie 特点Sessionsession 的使用Session 登录权限验证过滤器简介过滤器的使用WebFilter 注解过滤放行登录权限验证批量删除 servlet 类 dao 层 会话技术 简介 在计算机领域…...

CAPL脚本要注意区分elcount和strlen求数组长度的区别,不然要吃大亏
🍅 我是蚂蚁小兵,专注于车载诊断领域,尤其擅长于对CANoe工具的使用🍅 寻找组织 ,答疑解惑,摸鱼聊天,博客源码,点击加入👉【相亲相爱一家人】🍅 玩转CANoe&…...

CSS常用选择器
目录 1.CSS是什么 2.CSS的三种写法 2.1内部样式 2.2内联样式 2.3外部样式 3.CSS选择器 3.1标签选择器 3.2类选择器(更好的选择) 3.3ID选择器 3.4后代选择器 3.5子选择器 3.6并集选择器 3.7伪类选择器(复合选择器的特殊用法) 1.CSS是什么 CSS全称Cascding Style Sh…...
synchronized 学习
学习源: https://www.bilibili.com/video/BV1aJ411V763?spm_id_from333.788.videopod.episodes&vd_source32e1c41a9370911ab06d12fbc36c4ebc 1.应用场景 不超卖,也要考虑性能问题(场景) 2.常见面试问题: sync出…...

IT供电系统绝缘监测及故障定位解决方案
随着新能源的快速发展,光伏电站、储能系统及充电设备已广泛应用于现代能源网络。在光伏领域,IT供电系统凭借其持续供电性好、安全性高等优势成为光伏首选,但在长期运行中,例如老化、潮湿、隐裂、机械损伤等问题会影响光伏板绝缘层…...
#Uniapp篇:chrome调试unapp适配
chrome调试设备----使用Android模拟机开发调试移动端页面 Chrome://inspect/#devices MuMu模拟器Edge浏览器:Android原生APP嵌入的H5页面元素定位 chrome://inspect/#devices uniapp单位适配 根路径下 postcss.config.js 需要装这些插件 “postcss”: “^8.5.…...

安全突围:重塑内生安全体系:齐向东在2025年BCS大会的演讲
文章目录 前言第一部分:体系力量是突围之钥第一重困境是体系思想落地不畅。第二重困境是大小体系融合瓶颈。第三重困境是“小体系”运营梗阻。 第二部分:体系矛盾是突围之障一是数据孤岛的障碍。二是投入不足的障碍。三是新旧兼容难的障碍。 第三部分&am…...
现有的 Redis 分布式锁库(如 Redisson)提供了哪些便利?
现有的 Redis 分布式锁库(如 Redisson)相比于开发者自己基于 Redis 命令(如 SETNX, EXPIRE, DEL)手动实现分布式锁,提供了巨大的便利性和健壮性。主要体现在以下几个方面: 原子性保证 (Atomicity)ÿ…...
日常一水C
多态 言简意赅:就是一个对象面对同一事件时做出的不同反应 而之前的继承中说过,当子类和父类的函数名相同时,会隐藏父类的同名函数转而调用子类的同名函数,如果要调用父类的同名函数,那么就需要对父类进行引用&#…...

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement
Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...

[论文阅读]TrustRAG: Enhancing Robustness and Trustworthiness in RAG
TrustRAG: Enhancing Robustness and Trustworthiness in RAG [2501.00879] TrustRAG: Enhancing Robustness and Trustworthiness in Retrieval-Augmented Generation 代码:HuichiZhou/TrustRAG: Code for "TrustRAG: Enhancing Robustness and Trustworthin…...

GraphRAG优化新思路-开源的ROGRAG框架
目前的如微软开源的GraphRAG的工作流程都较为复杂,难以孤立地评估各个组件的贡献,传统的检索方法在处理复杂推理任务时可能不够有效,特别是在需要理解实体间关系或多跳知识的情况下。先说结论,看完后感觉这个框架性能上不会比Grap…...
CppCon 2015 学习:REFLECTION TECHNIQUES IN C++
关于 Reflection(反射) 这个概念,总结一下: Reflection(反射)是什么? 反射是对类型的自我检查能力(Introspection) 可以查看类的成员变量、成员函数等信息。反射允许枚…...