EventBus 开源库学习(三)
源码细节阅读
上一节根据EventBus的使用流程把实现源码大体梳理了一遍,因为精力有限,所以看源码都是根据实现过程把基本流程看下,中间实现细节先忽略,否则越看越深不容易把握大体思路,这节把一些细节的部分再看看。
注解函数查找源码逻辑
#EventBus public void register(Object subscriber) {Class<?> subscriberClass = subscriber.getClass();List<SubscriberMethod> subscriberMethods = subscriberMethodFinder.findSubscriberMethods(subscriberClass);synchronized (this) {for (SubscriberMethod subscriberMethod : subscriberMethods) {subscribe(subscriber, subscriberMethod);}}}
在进行注册的时候,我们使用subscriberMethodFinder
对象的findSubscriberMethods
方法来查找到所有该类中以@Subscribe
注解的函数(以下简称为注解函数)。让我们继续看下查找部分的逻辑。
List<SubscriberMethod> findSubscriberMethods(Class<?> subscriberClass) {List<SubscriberMethod> subscriberMethods = METHOD_CACHE.get(subscriberClass);if (subscriberMethods != null) {return subscriberMethods;}if (ignoreGeneratedIndex) {subscriberMethods = findUsingReflection(subscriberClass);} else {subscriberMethods = findUsingInfo(subscriberClass);}if (subscriberMethods.isEmpty()) {throw new EventBusException("Subscriber " + subscriberClass+ " and its super classes have no public methods with the @Subscribe annotation");} else {METHOD_CACHE.put(subscriberClass, subscriberMethods);return subscriberMethods;}}
METHOD_CACHE
是一个ConcurrentHashMap
类型的常量,用来保存subscriberClass
和对应SubscriberMethod
的集合,以提高注册效率,防止重复查找。
如果METHOD_CACHE
缓存中不存在,判断变量ignoreGeneratedIndex
的值,该值是在EventBusBuilder
中初始化的,表示是否忽略注解生成器,默认是false
。然后会进入到findUsingInfo
函数中进行查找(下面再看),最后将找到的subscriberMethods
添加到METHOD_CACHE
中。
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {FindState findState = prepareFindState();findState.initForSubscriber(subscriberClass);````return getMethodsAndRelease(findState);}
先看findUsingInfo
前两行,出现一个FindState
类,它是SubscriberMethodFinder
的内部类,用来辅助查找订阅事件的方法。
private FindState prepareFindState() {synchronized (FIND_STATE_POOL) {for (int i = 0; i < POOL_SIZE; i++) {FindState state = FIND_STATE_POOL[i];if (state != null) {FIND_STATE_POOL[i] = null;return state;}}}return new FindState();}static class FindState {```void initForSubscriber(Class<?> subscriberClass) {this.subscriberClass = clazz = subscriberClass;skipSuperClasses = false;subscriberInfo = null;}}
prepareFindState()
方法主要是返回一个FindState
对象,先从FIND_STATE_POOL
这个缓存池中获取一个现成的,如果没有就新建一个对象。获取对象后调用初始化方法,将subscriberClass
赋值给clazz
变量,subscriberInfo
对象赋值为null
。
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {FindState findState = prepareFindState();findState.initForSubscriber(subscriberClass);while (findState.clazz != null) {findState.subscriberInfo = getSubscriberInfo(findState);if (findState.subscriberInfo != null) {```} else {findUsingReflectionInSingleClass(findState);}findState.moveToSuperclass();}return getMethodsAndRelease(findState);}
再回到findUsingInfo
,因为findState.clazz
刚赋值过,所以不为空。再来看下getSubscriberInfo
方法。
private SubscriberInfo getSubscriberInfo(FindState findState) {if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();if (findState.clazz == superclassInfo.getSubscriberClass()) {return superclassInfo;}}if (subscriberInfoIndexes != null) {for (SubscriberInfoIndex index : subscriberInfoIndexes) {SubscriberInfo info = index.getSubscriberInfo(findState.clazz);if (info != null) {return info;}}}return null;}
前面执行initForSubscriber
的时候,subscriberInfo
赋值为null
,因此第一个if
不成立。第二个判断是对象subscriberInfoIndexes
,该变量也是在EventBusBuilder
中初始化的,默认是null
,因此该方法返回值null
。
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {FindState findState = prepareFindState();findState.initForSubscriber(subscriberClass);while (findState.clazz != null) {findState.subscriberInfo = getSubscriberInfo(findState);if (findState.subscriberInfo != null) {```} else {findUsingReflectionInSingleClass(findState);}findState.moveToSuperclass();}return getMethodsAndRelease(findState);}
再回到findUsingInfo
,由于getSubscriberInfo
返回为null
,逻辑会走到findUsingReflectionInSingleClass
方法中,使用反射来获取所有方法(下面再看)。
获取当前类所有的注解方法后,findState.moveToSuperclass()
表示修改findState.clazz
为subscriberClass
的父类Class
,继续遍历父类中的注解方法。
private static final int MODIFIERS_IGNORE = Modifier.ABSTRACT | Modifier.STATIC | BRIDGE | SYNTHETIC;private void findUsingReflectionInSingleClass(FindState findState) {Method[] methods;try {// This is faster than getMethods, especially when subscribers are fat classes like Activitiesmethods = findState.clazz.getDeclaredMethods();} catch (Throwable th) {// Workaround for java.lang.NoClassDefFoundError, see https://github.com/greenrobot/EventBus/issues/149```}for (Method method : methods) {int modifiers = method.getModifiers();if ((modifiers & Modifier.PUBLIC) != 0 && (modifiers & MODIFIERS_IGNORE) == 0) {Class<?>[] parameterTypes = method.getParameterTypes();if (parameterTypes.length == 1) {Subscribe subscribeAnnotation = method.getAnnotation(Subscribe.class);if (subscribeAnnotation != null) {Class<?> eventType = parameterTypes[0];if (findState.checkAdd(method, eventType)) {ThreadMode threadMode = subscribeAnnotation.threadMode();findState.subscriberMethods.add(new SubscriberMethod(method, eventType, threadMode,subscribeAnnotation.priority(), subscribeAnnotation.sticky()));}}} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {String methodName = method.getDeclaringClass().getName() + "." + method.getName();throw new EventBusException("@Subscribe method " + methodName +"must have exactly 1 parameter but has " + parameterTypes.length);}} else if (strictMethodVerification && method.isAnnotationPresent(Subscribe.class)) {String methodName = method.getDeclaringClass().getName() + "." + method.getName();throw new EventBusException(methodName +" is a illegal @Subscribe method: must be public, non-static, and non-abstract");}}}
上面代码流程如下:
- 通过方法
getDeclaredMethods()
获取该类中所有声明的方法列表; - 遍历方法列表,获取方法的修饰符,如果修饰符是
public
并且不是abstract
、static
等进入下一步,MODIFIERS_IGNORE
定义如第一行代码; - 获取方法的参数列表,如果参数的个数是1,并且使用了
Subscribe
注解,获取唯一的入参进行下一步; - 然后
checkAdd()
方法用来判断FindState的anyMethodByEventType map
是否已经添加过以当前eventType
为key
的键值对,没添加过则返回true
(就是看看当前类中有没有多个事件相同的注解函数); - 通过注解对象
subscribeAnnotation
获取threadMode
,然后新建SubscriberMethod
添加到findState.subscriberMethods
集合中。
分析完findUsingReflectionInSingleClass
这个方法后,我们回到findUsingInfo
。
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {FindState findState = prepareFindState();findState.initForSubscriber(subscriberClass);while (findState.clazz != null) {findState.subscriberInfo = getSubscriberInfo(findState);if (findState.subscriberInfo != null) {```} else {findUsingReflectionInSingleClass(findState);}findState.moveToSuperclass();}return getMethodsAndRelease(findState);}
遍历当前类及其父类,通过findUsingReflectionInSingleClass
方法找到所有注解方法后,通过getMethodsAndRelease
返回所有相关方法。
private List<SubscriberMethod> getMethodsAndRelease(FindState findState) {List<SubscriberMethod> subscriberMethods = new ArrayList<>(findState.subscriberMethods);findState.recycle();synchronized (FIND_STATE_POOL) {for (int i = 0; i < POOL_SIZE; i++) {if (FIND_STATE_POOL[i] == null) {FIND_STATE_POOL[i] = findState;break;}}}return subscriberMethods;}
通过上面的解析知道,所有的SubscriberMethod
都添加到findState.subscriberMethods
集合中,这个方法就是把findState.subscriberMethods
集合中的内容复制出来,然后释放findState
中的资源,并把findState
放到FIND_STATE_POOL
缓存中,POOL_SIZE
常量是4,这样下次查找的时候可以重复利用无需每次都新建对象。
以上为注解方法查找的过程。在上面分析的过程中出现了两个变量subscriberInfoIndexes
和ignoreGeneratedIndex
看样子都是关于索引的,分析的时候都是使用的EventBusBuilder
中的默认值。看下subscriberInfoIndexes
赋值的地方,看注释意思是:将EventBus
注解处理器生成索引添加添加进来。所以注解处理器需要再了解下。
/** Adds an index generated by EventBus' annotation preprocessor. */public EventBusBuilder addIndex(SubscriberInfoIndex index) {if (subscriberInfoIndexes == null) {subscriberInfoIndexes = new ArrayList<>();}subscriberInfoIndexes.add(index);return this;}
Subscriber Index注解处理器
通过上面分析可知,EventBus
注册事件时,主要是在项目运行时通过反射来查找注解方法信息,如果项目中有大量的注解方法,必然会对项目运行时的性能产生影响。
除了在项目运行时通过反射查找注解方法信息,EventBus
还提供了在项目编译时通过注解处理器查找注解方法信息的方式,生成一个辅助的索引类来保存这些信息,这个索引类就是Subscriber Index。
1、Subscriber Index使用方式
首先要在 app
的build.gradle
中加入如下配置:
android {defaultConfig {javaCompileOptions {annotationProcessorOptions {// 指定辅助索引类的名称和包名,注意,这个类时自动生成的,不需要我们写arguments = [ eventBusIndex : 'com.jane.demo.MyEventBusIndex' ]}}}
}
dependencies {compile 'org.greenrobot:eventbus:3.3.1'// 引入注解处理器annotationProcessor 'org.greenrobot:eventbus-annotation-processor:3.3.1'
}
在项目的Application
中添加如下配置,以生成一个默认的 EventBus
单例:
EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
此时重新编译下项目,会生成一个MyEventBusIndex
类(不同的gradle
版本生成的位置可能不一样)。
生成的MyEventBusIndex
代码如下:
public class MyEventBusIndex implements SubscriberInfoIndex {private static final Map<Class<?>, SubscriberInfo> SUBSCRIBER_INDEX;static {SUBSCRIBER_INDEX = new HashMap<Class<?>, SubscriberInfo>();putIndex(new SimpleSubscriberInfo(EventBusService.class, true, new SubscriberMethodInfo[] {new SubscriberMethodInfo("onMsgEventReceived", String.class),new SubscriberMethodInfo("onMsgEventReceived", MsgEvent.class),new SubscriberMethodInfo("onMsgEventReceived", org.greenrobot.eventbus.NoSubscriberEvent.class),}));putIndex(new SimpleSubscriberInfo(EventBusActivity.class, true, new SubscriberMethodInfo[] {new SubscriberMethodInfo("onMsgEventReceived", Event.class),}));}private static void putIndex(SubscriberInfo info) {SUBSCRIBER_INDEX.put(info.getSubscriberClass(), info);}@Overridepublic SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);if (info != null) {return info;} else {return null;}}
}
代码中SUBSCRIBER_INDEX
是一个HashMap
,保存了当前注册类的Class
类型和其中注解方法的信息。
2、Subscriber Index源码
EventBus.builder().addIndex(new MyEventBusIndex()).installDefaultEventBus();
使用的时候,获取EventBusBuilder
对象,然后调用addIndex
方法,把生成的MyEventBusIndex
加入进去。
/** Adds an index generated by EventBus' annotation preprocessor. */public EventBusBuilder addIndex(SubscriberInfoIndex index) {if (subscriberInfoIndexes == null) {subscriberInfoIndexes = new ArrayList<>();}subscriberInfoIndexes.add(index);return this;}
上面的方法是不是比较眼熟,就是上面代码分析的时候出现的变量subscriberInfoIndexes
赋值函数。这样,运行的时候就把之前编译好的索引类的实例保存在subscriberInfoIndexes
集合中。然后调用installDefaultEventBus
方法创建EventBus
实例。
/*** Installs the default EventBus returned by {@link EventBus#getDefault()} using this builders' values. Must be* done only once before the first usage of the default EventBus.** @throws EventBusException if there's already a default EventBus instance in place*/public EventBus installDefaultEventBus() {synchronized (EventBus.class) {if (EventBus.defaultInstance != null) {throw new EventBusException("Default instance already exists." +" It may be only set once before it's used the first time to ensure consistent behavior.");}EventBus.defaultInstance = build();return EventBus.defaultInstance;}}/** Builds an EventBus based on the current configuration. */public EventBus build() {return new EventBus(this);}
上面通过build
方法,以当前EventBusBuilder
对象作为参数生成EventBus
单例对象,并赋值给defaultInstance
,这样就把subscriberInfoIndexes
信息传递给了EventBus
。以后调用EventBus
的getDefault()
返回的就是这里生成的单例对象,里面包含了subscriberInfoIndexes
信息。
而且在Application
中生成了 EventBus
的默认单例,这样就保证了在项目其它地方执行EventBus.getDefault()
就能得到就是上面包含subscriberInfoIndexes
信息的单例。
在上面分析查找注解函数的时候,分析过一个函数:
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {FindState findState = prepareFindState();findState.initForSubscriber(subscriberClass);while (findState.clazz != null) {findState.subscriberInfo = getSubscriberInfo(findState);if (findState.subscriberInfo != null) {····} else {findUsingReflectionInSingleClass(findState);}findState.moveToSuperclass();}return getMethodsAndRelease(findState);}
如果没有使用注解处理器的话getSubscriberInfo
这个方法返回值是null
,因此会走到findUsingReflectionInSingleClass
,通过反射区获取注解方法。现在使用使用注解处理器的话在重新看下这个函数。
private SubscriberInfo getSubscriberInfo(FindState findState) {if (findState.subscriberInfo != null && findState.subscriberInfo.getSuperSubscriberInfo() != null) {SubscriberInfo superclassInfo = findState.subscriberInfo.getSuperSubscriberInfo();if (findState.clazz == superclassInfo.getSubscriberClass()) {return superclassInfo;}}//不为空if (subscriberInfoIndexes != null) {for (SubscriberInfoIndex index : subscriberInfoIndexes) {SubscriberInfo info = index.getSubscriberInfo(findState.clazz);if (info != null) {return info;}}}return null;}@Overridepublic SubscriberInfo getSubscriberInfo(Class<?> subscriberClass) {SubscriberInfo info = SUBSCRIBER_INDEX.get(subscriberClass);if (info != null) {return info;} else {return null;}}
如果使用了注解处理器,因为subscriberInfoIndexes
这个集合不为空,遍历获取当前类对应的注解方法列表, index.getSubscriberInfo
这个方法实际调用的就是系统生成的MyEventBusIndex
中的getSubscriberInfo
方法,这样就获取到了之前编译生成的注解方法。
private List<SubscriberMethod> findUsingInfo(Class<?> subscriberClass) {FindState findState = prepareFindState();findState.initForSubscriber(subscriberClass);while (findState.clazz != null) {findState.subscriberInfo = getSubscriberInfo(findState);if (findState.subscriberInfo != null) {SubscriberMethod[] array = findState.subscriberInfo.getSubscriberMethods();for (SubscriberMethod subscriberMethod : array) {if (findState.checkAdd(subscriberMethod.method, subscriberMethod.eventType)) {findState.subscriberMethods.add(subscriberMethod);}}} else {findUsingReflectionInSingleClass(findState);}findState.moveToSuperclass();}return getMethodsAndRelease(findState);}
通过getSubscriberInfo
返回当前类所有注解方法后,遍历所有订阅了事件的方法然后加入到subscriberMethods
列表中,其它的和之前的注册流程一样。
参考文章:
EventBus 原理解析
相关文章:

EventBus 开源库学习(三)
源码细节阅读 上一节根据EventBus的使用流程把实现源码大体梳理了一遍,因为精力有限,所以看源码都是根据实现过程把基本流程看下,中间实现细节先忽略,否则越看越深不容易把握大体思路,这节把一些细节的部分再看看。 …...
zjzcyList.stream().map(Pb_zjzcy::getZjid).collect(Collectors.toList()); 解释一下
zjzcyList.stream().map(Pb_zjzcy::getZjid).collect(Collectors.toList()); 解释一下 这段代码是使用Java 8的流式处理(Stream)对一个存储了对象的列表(zjzcyList)进行操作,并最终返回一个包含了列表中每个对象的Zji…...

车载总线系列——J1939 二
车载总线系列——J1939 二 我是穿拖鞋的汉子,魔都中坚持长期主义的汽车电子工程师。 老规矩,分享一段喜欢的文字,避免自己成为高知识低文化的工程师: 没有人关注你。也无需有人关注你。你必须承认自己的价值,你不能站…...

【C#学习笔记】引用类型(2)
文章目录 ObjectEqualsGetTypeToStringGetHashCode string逐字文本复合格式字符串字符串内插 StringBuilderStringBuilder 的工作原理StringBuilder提供的方法访问字符迭代字符查询字符 dynamic Object 支持 .NET 类层次结构中的所有类,并为派生类提供低级别服务。…...
【Rust 基础篇】Rust类函数宏:代码生成的魔法
导言 Rust是一门现代的、安全的系统级编程语言,它提供了丰富的元编程特性,其中类函数宏(Function-Like Macros)是其中之一。类函数宏允许开发者创建类似函数调用的宏,并在编译期间对代码进行生成和转换。在本篇博客中…...

Spring-1-透彻理解Spring XML的Bean创建--IOC
学习目标 上一篇文章我们介绍了什么是Spring,以及Spring的一些核心概念,并且快速快发一个Spring项目,实现IOC和DI,今天具体来讲解IOC 能够说出IOC的基础配置和Bean作用域 了解Bean的生命周期 能够说出Bean的实例化方式 一、Bean的基础配置 …...

【JAVA】类和对象
作者主页:paper jie的博客 本文作者:大家好,我是paper jie,感谢你阅读本文,欢迎一建三连哦。 本文录入于《JAVASE语法系列》专栏,本专栏是针对于大学生,编程小白精心打造的。笔者用重金(时间和精…...

jenkins准备
回到目录 jenkins是一个开源的、提供友好操作界面的持续集成(CI)工具,主要用于持续、自动的构建/测试软件项目、监控外部任务的运行。Jenkins用Java语言编写,可在Tomcat等流行的servlet容器中运行,也可独立运行。通常与版本管理工具(SCM)、构…...

【Rust】Rust学习
文档:Rust 程序设计语言 - Rust 程序设计语言 简体中文版 (bootcss.com) 墙裂推荐这个文档 第一章入门 入门指南 - Rust 程序设计语言 简体中文版 第二章猜猜看游戏 猜猜看游戏教程 - Rust 程序设计语言 简体中文版 (bootcss.com) // 导入库 use std::io; use s…...
Linux 常用命令之配置环境变量 PATH
PATH是系统环境变量中的一种,同时将一些二进制文件的绝对路径追加进去,则在系统终端中可以发现这些路径下的文件。 一. 环境变量设置 export PATH<二进制文件的绝对路径>:$PATH 以下为结合实际例子的操作 1、临时设置 打开一个终端执行如下命令 e…...

flask-----蓝图
1.引入蓝图 flask都写在一个文件中,项目这样肯定不行,会导致循环导入的问题,分目录,分包,使用蓝图划分目录。 2.使用蓝图 步骤如下: -1 实例化得到一个蓝图对象-order_blueBlueprint(order,__name__,tem…...

学习左耳听风栏目90天——第一天 1-90(学习左耳朵耗子的工匠精神,对技术的热爱)【洞悉技术的本质,享受科技的乐趣】
洞悉技术的本质,享受科技的乐趣 第一篇,我的感受就是 耗叔是一个热爱技术,可以通过代码找到快乐的技术人。 作为it从业者,我们如何可以通过代码找到快乐呢?这是一个问题? 至少目前,我还没有这种…...
后端登录安全的一种思路
PS:作者是小白能接触到的就只会这样写。勿喷。 前提 思路: 结合io流将登录token存储到配置文件中,不将token存储到浏览器端,从而避免盗取。 下面jwt的学习可以参考下这个: JWT --- 入门学习_本郡主是喵的博客-CSDN博客 JWT工具类 Component public class JWTtU…...

【深度学习_TensorFlow】激活函数
写在前面 上篇文章我们了解到感知机使用的阶跃函数和符号函数,它们都是非连续,导数为0的函数: 建议回顾上篇文章,本篇文章将介绍神经网络中的常见激活函数,这些函数都是平滑可导的,适合于梯度下降算法。 写…...

机器学习笔记之优化算法(七)线搜索方法(步长角度;非精确搜索;Wolfe Condition)
机器学习笔记之优化算法——线搜索方法[步长角度,非精确搜索,Wolfe Condition] 引言回顾: Armijo \text{Armijo} Armijo准则及其弊端 Glodstein \text{Glodstein} Glodstein准则及其弊端 Wolfe Condition \text{Wolfe Condition} Wolfe Condi…...

十四.redis哨兵模式
redis哨兵模式 1.概述2.测试3.哨兵模式优缺点 redis哨兵模式基础是主从复制 1.概述 主从切换的技术方法:当主节点服务器宕机后,需要手动把一台从服务器切换为主服务器,这就需要人工干预,费时费力,还会造成一段时间内服…...

采用UWB技术开发的智慧工厂人员定位系统源码【UWB定位基站、卡牌】
UWB (ULTRA WIDE BAND, UWB) 技术是一种无线载波通讯技术,它不采用正弦载波,而是利用纳秒级的非正弦波窄脉冲传输数据,因此其所占的频谱范围很宽。UWB定位系统依托在移动通信,雷达,微波电路,云计算与大数据…...

当你软件测试遇上加密接口,是不是就不能测了?
相信大家在工作中做接口测试的时候,肯定会遇到一个场景,那就是你们的软件,密码是加密存储的。 那么这样的话,我们在执行接口的时候,对于密码的处理就开始头疼了。 所以,本文将使用jmeter这款java开源的接…...
Flink
Flink(Apache Flink)是一个开源的分布式流处理引擎和批处理框架。它是由 Apache 软件基金会维护的项目,旨在处理大规模数据的实时流式处理和批处理任务。Flink 提供了强大的流处理和批处理功能,具有低延迟、高吞吐量和高容错性&am…...
python入门常用操作
python常用操作 1、ndarry数组的切片2、print用法2.1格式化输出format2.2字符串格式化输出 3、均值滤波函数 1、ndarry数组的切片 例如一个5列的ndarry数组,想要获取第2列和第3列数据,可以用 #(1)用法1 data[:,1:3],…...

XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...

基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...

css实现圆环展示百分比,根据值动态展示所占比例
代码如下 <view class""><view class"circle-chart"><view v-if"!!num" class"pie-item" :style"{background: conic-gradient(var(--one-color) 0%,#E9E6F1 ${num}%),}"></view><view v-else …...

剑指offer20_链表中环的入口节点
链表中环的入口节点 给定一个链表,若其中包含环,则输出环的入口节点。 若其中不包含环,则输出null。 数据范围 节点 val 值取值范围 [ 1 , 1000 ] [1,1000] [1,1000]。 节点 val 值各不相同。 链表长度 [ 0 , 500 ] [0,500] [0,500]。 …...

基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...

c#开发AI模型对话
AI模型 前面已经介绍了一般AI模型本地部署,直接调用现成的模型数据。这里主要讲述讲接口集成到我们自己的程序中使用方式。 微软提供了ML.NET来开发和使用AI模型,但是目前国内可能使用不多,至少实践例子很少看见。开发训练模型就不介绍了&am…...

SpringTask-03.入门案例
一.入门案例 启动类: package com.sky;import lombok.extern.slf4j.Slf4j; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cache.annotation.EnableCach…...

项目部署到Linux上时遇到的错误(Redis,MySQL,无法正确连接,地址占用问题)
Redis无法正确连接 在运行jar包时出现了这样的错误 查询得知问题核心在于Redis连接失败,具体原因是客户端发送了密码认证请求,但Redis服务器未设置密码 1.为Redis设置密码(匹配客户端配置) 步骤: 1).修…...
稳定币的深度剖析与展望
一、引言 在当今数字化浪潮席卷全球的时代,加密货币作为一种新兴的金融现象,正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而,加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下,稳定…...
【生成模型】视频生成论文调研
工作清单 上游应用方向:控制、速度、时长、高动态、多主体驱动 类型工作基础模型WAN / WAN-VACE / HunyuanVideo控制条件轨迹控制ATI~镜头控制ReCamMaster~多主体驱动Phantom~音频驱动Let Them Talk: Audio-Driven Multi-Person Conversational Video Generation速…...