当前位置: 首页 > news >正文

UnLua源码分析(一)初始化流程

UnLua源码分析(一)初始化流程

  • 接入
  • 插件启动
  • 注册设置
  • 默认参数集
  • 注册回调
  • SetActive
  • PostLoadMapWithWorld
  • ULuaEnvLocator
    • 启动Lua虚拟机
    • 初始化UE相关的Lua Lib
    • 创建与Lua交互的数据结构
    • 注册静态导出的类,函数,枚举
    • Lua层初始化
  • UUnLuaManager
  • 总结
  • Reference

UnLua是适用于UE的一个高度优化的Lua脚本解决方案。我们今天先来分析一下它的初始化流程。本文基于UE 5.5的环境,分析的UnLua源码版本为是最新的Devlop分支。

接入

首先是去下载Develop分支的源码,这个最新分支修复了UE 5.4版本的编译问题。不过很不幸,它不能在5.5版本下编译通过,主要原因也是UE 5.5版本的某些API发生了变化。接入时可以参考GitHub上的相关issue。编译通过之后,就可以参考UnLua官方给的新手教程,进行Lua开发了。打开官方的TPS工程,在Tutorial目录下也有若干展示UnLua特性的例子。

插件启动

UnLua是以插件的形式加载到UE的,那么我们很容易找到它的启动入口,位于UnLuaModule.cpp中的FUnLuaModule::StartupModule函数。我们这里只截取当前关心的内容,其他部分先略去:

virtual void StartupModule() override
{RegisterSettings();FCoreUObjectDelegates::PostLoadMapWithWorld.AddRaw(this, &FUnLuaModule::PostLoadMapWithWorld);CreateDefaultParamCollection();#if AUTO_UNLUA_STARTUP
#if WITH_EDITORif (!IsRunningGame()){FEditorDelegates::PreBeginPIE.AddRaw(this, &FUnLuaModule::OnPreBeginPIE);FEditorDelegates::PostPIEStarted.AddRaw(this, &FUnLuaModule::OnPostPIEStarted);FEditorDelegates::EndPIE.AddRaw(this, &FUnLuaModule::OnEndPIE);FGameDelegates::Get().GetEndPlayMapDelegate().AddRaw(this, &FUnLuaModule::OnEndPlayMap);}if (IsRunningGame() || IsRunningDedicatedServer())
#endifSetActive(true);
#endif
}

可以看到,负责启动的入口函数还是比较简洁的,第一步是注册一些设置,第二步是创建默认的参数集,第三步会根据当前是否为编辑器环境,如果是则注册一些回调函数,来控制编辑器环境下UnLua的生命周期,如果是打包版则直接启动UnLua。我们先来看看第一个步骤,注册设置。

注册设置

RegisterSettings负责向UE编辑器注册UnLua的配置项,并且注册了配置修改的回调,然后便从ini文件中加载读取当前的配置。

void RegisterSettings()
{
#if WITH_EDITORISettingsModule* SettingsModule = FModuleManager::GetModulePtr<ISettingsModule>("Settings");if (!SettingsModule)return;const auto Section = SettingsModule->RegisterSettings("Project", "Plugins", "UnLua",LOCTEXT("UnLuaEditorSettingsName", "UnLua"),LOCTEXT("UnLuaEditorSettingsDescription", "UnLua Runtime Settings"),GetMutableDefault<UUnLuaSettings>());Section->OnModified().BindRaw(this, &FUnLuaModule::OnSettingsModified);
#endif#if ENGINE_MAJOR_VERSION >=5 && !WITH_EDITOR// UE5下打包后没有从{PROJECT}/Config/DefaultUnLua.ini加载,这里强制刷新一下FString UnLuaIni = TEXT("UnLua");GConfig->LoadGlobalIniFile(UnLuaIni, *UnLuaIni, nullptr, true);UUnLuaSettings::StaticClass()->GetDefaultObject()->ReloadConfig();
#endifauto& Settings = *GetDefault<UUnLuaSettings>();bPrintLuaStackOnSystemError = Settings.bPrintLuaStackOnSystemError;
}

在Project Settings/Plugins目录下,可以看到UnLua的配置项,我们暂时不去关心这些配置的具体用途。
在这里插入图片描述

默认参数集

CreateDefaultParamCollection会从一个UBT自动生成的inl文件中,读取UE中包含默认参数的函数,加入到一个名为GDefaultParamCollection的全局Map中。

TMap<FName, FFunctionCollection> GDefaultParamCollection;void CreateDefaultParamCollection()
{static bool CollectionCreated = false;if (!CollectionCreated){CollectionCreated = true;#include "DefaultParamCollection.inl"}
}

打开DefaultParamCollection.inl文件可以看到大量的函数名称和参数名称,例如:

FC = &GDefaultParamCollection.Add(TEXT("UAvoidanceManager"));
PC = &FC->Functions.Add(TEXT("RegisterMovementComponent"));
PC->Parameters.Add(TEXT("AvoidanceWeight"), new FFloatParamValue(0.500000f));

对照引擎代码,的确可以在UAvoidanceManager中找到函数的定义:

ENGINE_API bool RegisterMovementComponent(class UMovementComponent* MovementComp, float AvoidanceWeight = 0.5f);

注册回调

编辑器环境下,会去监听当前是否处于PIE模式。可以看到UnLua的初始化逻辑分为两块,一部分在进入PIE模式之前执行,一部分则在进入PIE模式之后再执行。

void OnPreBeginPIE(bool bIsSimulating)
{SetActive(true);
}void OnPostPIEStarted(bool bIsSimulating)
{UEditorEngine* EditorEngine = Cast<UEditorEngine>(GEngine);if (EditorEngine)PostLoadMapWithWorld(EditorEngine->PlayWorld);
}

打包版同样也会先调用SetActive,然后在加载地图时调用PostLoadMapWithWorld。显然这两个函数就是UnLua初始化的核心函数了。

SetActive

SetActive接受一个bool类型的参数,说明它同时负责启动和销毁UnLua的逻辑,这里我们先只关心初始化的部分,一些细节也先略去:

virtual void SetActive(const bool bActive) override
{if (bIsActive == bActive)return;if (bActive){GUObjectArray.AddUObjectCreateListener(this);GUObjectArray.AddUObjectDeleteListener(this);const auto& Settings = *GetMutableDefault<UUnLuaSettings>();const auto EnvLocatorClass = *Settings.EnvLocatorClass == nullptr ? ULuaEnvLocator::StaticClass() : *Settings.EnvLocatorClass;EnvLocator = NewObject<ULuaEnvLocator>(GetTransientPackage(), EnvLocatorClass);EnvLocator->AddToRoot();for (const auto Class : TObjectRange<UClass>()){for (const auto& ClassPath : Settings.PreBindClasses){if (!ClassPath.IsValid())continue;const auto TargetClass = ClassPath.ResolveClass();if (!TargetClass)continue;if (Class->IsChildOf(TargetClass)){const auto Env = EnvLocator->Locate(Class);Env->TryBind(Class);break;}}}}bIsActive = bActive;
}

主要也是三件事情,首先是对UObject的创建和销毁进行了监听,这个很自然,因为UnLua需要为UObject绑定相关的Lua信息,实现Lua层与C++层之间的交互;第二是创建了一个ULuaEnvLocator类型的对象,通过类的定义可知它主要负责从上层管理Lua虚拟机环境,这个类型还支持通过配置进行修改;最后是如果配置项中存在需要预先绑定的类,则在此时尝试进行绑定。这里绑定的概念是双向的,意味着会把C++层的方法暴露给Lua层,同时也把Lua层覆盖或新增的方法设置进来,这块内容留到后面再详细展开。

UCLASS()
class UNLUA_API ULuaEnvLocator : public UObject
{GENERATED_BODY()
public:virtual UnLua::FLuaEnv* Locate(const UObject* Object);virtual void HotReload();virtual void Reset();TSharedPtr<UnLua::FLuaEnv, ESPMode::ThreadSafe> Env;
};

默认配置下UnLua有3个需要提前绑定的类:
在这里插入图片描述

PostLoadMapWithWorld

相较之下,PostLoadMapWithWorld就比较简单了,它主要就是创建出UUnLuaManager类型的对象了,这个manager负责具体的绑定工作。

void PostLoadMapWithWorld(UWorld* World) const
{if (!World || !bIsActive)return;const auto Env = EnvLocator->Locate(World);if (!Env)return;const auto Manager = Env->GetManager();if (!Manager)return;Manager->OnMapLoaded(World);
}

通过上述分析,我们进一步发现初始化的核心逻辑就在ULuaEnvLocatorUUnLuaManager中。

ULuaEnvLocator

ULuaEnvLocator提供了一个Locate函数,负责返回一个FLuaEnv类型的对象。这个对象是UnLua的核心对象,负责管理Lua虚拟机。

UnLua::FLuaEnv* ULuaEnvLocator::Locate(const UObject* Object)
{if (!Env){Env = MakeShared<UnLua::FLuaEnv, ESPMode::ThreadSafe>();Env->Start();}return Env.Get();
}

接下来对FLuaEnv的构造函数进行逐步分析。

启动Lua虚拟机

#if PLATFORM_WINDOWS// 防止类似AppleProResMedia插件忘了恢复Dll查找目录// https://github.com/Tencent/UnLua/issues/534const auto Dir = FPaths::ConvertRelativePathToFull(FPaths::ProjectDir() / TEXT("Binaries/Win64"));FPlatformProcess::PushDllDirectory(*Dir);L = lua_newstate(GetLuaAllocator(), nullptr);FPlatformProcess::PopDllDirectory(*Dir);
#elseL = lua_newstate(GetLuaAllocator(), nullptr);
#endifAllEnvs.Add(L, this);luaL_openlibs(L);AddSearcher(LoadFromCustomLoader, 2);AddSearcher(LoadFromFileSystem, 3);AddSearcher(LoadFromBuiltinLibs, 4);

此时Lua虚拟机已创建完成,并且Lua的标准库也都加载进来了。此外,UnLua还调整了Lua文件的搜索路径,使得Lua虚拟机可以读取到UE工程目录下的源文件。当然,我们也可以自定义自己的Loader。

初始化UE相关的Lua Lib

    UELib::Open(L);

Open函数会向Lua的全局环境中注册UE的表,表中包含几个基本的UE库函数,同时还设置了__index元方法,这样Lua层在访问UE.XXX时就会触发这里的逻辑。

static constexpr luaL_Reg UE_Functions[] = {{"LoadObject", UObject_Load},{"LoadClass", UClass_Load},{"NewObject", Global_NewObject},{NULL, NULL}
};int UnLua::UELib::Open(lua_State* L)
{lua_newtable(L);lua_pushstring(L, "__index");lua_pushcfunction(L, UE_Index);lua_rawset(L, -3);lua_pushvalue(L, -1);lua_setmetatable(L, -2);lua_pushvalue(L, -1);lua_pushstring(L, REGISTRY_KEY);lua_rawset(L, LUA_REGISTRYINDEX);luaL_setfuncs(L, UE_Functions, 0);lua_setglobal(L, NAMESPACE_NAME);// global access for legacy supportlua_getglobal(L, LUA_GNAME);luaL_setfuncs(L, UE_Functions, 0);lua_pop(L, 1);#if WITH_UE4_NAMESPACE == 1// 兼容UE4访问lua_getglobal(L, NAMESPACE_NAME);lua_setglobal(L, "UE4");
#elif WITH_UE4_NAMESPACE == 0// 兼容无UE4全局访问lua_getglobal(L, LUA_GNAME);lua_newtable(L);lua_pushstring(L, "__index");lua_getglobal(L, NAMESPACE_NAME);lua_rawset(L, -3);lua_setmetatable(L, -2);
#endifreturn 1;
}

创建与Lua交互的数据结构

    ObjectRegistry = new FObjectRegistry(this);ClassRegistry = new FClassRegistry(this);ClassRegistry->Initialize();FunctionRegistry = new FFunctionRegistry(this);DelegateRegistry = new FDelegateRegistry(this);ContainerRegistry = new FContainerRegistry(this);PropertyRegistry = new FPropertyRegistry(this);EnumRegistry = new FEnumRegistry(this);EnumRegistry->Initialize();lua_pushstring(L, "StructMap"); // create weak table 'StructMap'LowLevel::CreateWeakValueTable(L);lua_rawset(L, LUA_REGISTRYINDEX);lua_pushstring(L, "ArrayMap"); // create weak table 'ArrayMap'LowLevel::CreateWeakValueTable(L);lua_rawset(L, LUA_REGISTRYINDEX);

通过名字就能得知,这里创建了保存与Lua交互信息的Object、Class、Container、Struct、Array等注册表。它们的主要作用是将Lua层的对象与C++层的对象进行映射,方便调用和管理。具体细节我们等遇到了再说。

注册静态导出的类,函数,枚举

    // register statically exported classesauto ExportedNonReflectedClasses = GetExportedNonReflectedClasses();for (const auto& Pair : ExportedNonReflectedClasses)Pair.Value->Register(L);// register statically exported global functionsauto ExportedFunctions = GetExportedFunctions();for (const auto& Function : ExportedFunctions)Function->Register(L);// register statically exported enumsauto ExportedEnums = GetExportedEnums();for (const auto& Enum : ExportedEnums)Enum->Register(L);

所谓的静态导出,就是在UnLua加载时,利用静态变量初始化的方式,预先导出给Lua的类,函数和枚举。比如TArray,我们在LuaLib_Array.cpp中,可以找到它静态导出的代码:

static const luaL_Reg TArrayLib[] =
{{"Length", TArray_Length},{"Num", TArray_Length},{"Add", TArray_Add},{"AddUnique", TArray_AddUnique},{"Find", TArray_Find},{"Insert", TArray_Insert},{"Remove", TArray_Remove},{"RemoveItem", TArray_RemoveItem},{"Clear", TArray_Clear},{"Reserve", TArray_Reserve},{"Resize", TArray_Resize},{"GetData", TArray_GetData},{"Get", TArray_Get},{"GetRef", TArray_GetRef},{"Set", TArray_Set},{"Swap", TArray_Swap},{"Shuffle", TArray_Shuffle},{"LastIndex", TArray_LastIndex},{"IsValidIndex", TArray_IsValidIndex},{"Contains", TArray_Contains},{"Append", TArray_Append},{"ToTable", TArray_ToTable},{"__gc", TArray_Delete},{"__call", TArray_New},{"__pairs", TArray_Pairs},{"__index", TArray_Index},{"__newindex", TArray_NewIndex},{nullptr, nullptr}
};EXPORT_UNTYPED_CLASS(TArray, false, TArrayLib)IMPLEMENT_EXPORTED_CLASS(TArray)

EXPORT_UNTYPED_CLASS是一个宏,它定义了一个struct,和该struct类型的静态变量,以及它的构造函数,包含了静态导出的逻辑:

#define EXPORT_UNTYPED_CLASS(Name, bIsReflected, Lib) \struct FExported##Name##Helper \{ \static FExported##Name##Helper StaticInstance; \FExported##Name##Helper() \: ExportedClass(nullptr) \{ \UnLua::IExportedClass *Class = UnLua::FindExportedClass(#Name); \if (!Class) \{ \ExportedClass = new UnLua::TExportedClassBase<bIsReflected>(#Name); \UnLua::ExportClass(ExportedClass); \Class = ExportedClass; \} \Class->AddLib(Lib); \} \~FExported##Name##Helper() \{ \delete ExportedClass; \} \UnLua::IExportedClass *ExportedClass; \};

IMPLEMENT_EXPORTED_CLASS宏就是对该静态变量进行初始化,这样在UnLua启动时,就会自动调到它的构造函数,完成静态导出。

#define IMPLEMENT_EXPORTED_CLASS(Name) \FExported##Name##Helper FExported##Name##Helper::StaticInstance;

Lua层初始化

    UnLuaLib::Open(L);

在UnLua完成C++层面的构造之后,UnLua会再执行一段Lua逻辑,完成最后的初始化工作。

int Open(lua_State* L)
{lua_register(L, "print", LogInfo);luaL_requiref(L, "UnLua", LuaOpen, 1);luaL_dostring(L, R"(setmetatable(UnLua, {__index = function(t, k)local ok, result = pcall(require, "UnLua." .. tostring(k))if ok thenrawset(t, k, result)return resultelset.LogWarn(string.format("failed to load module UnLua.%s\n%s", k, result))endend}))");#if UNLUA_ENABLE_FTEXTluaL_dostring(L, "UnLua.FTextEnabled = true");
#elseluaL_dostring(L, "UnLua.FTextEnabled = false");
#endif#if UNLUA_WITH_HOT_RELOADluaL_dostring(L, R"(pcall(function() _G.require = require('UnLua.HotReload').require end))");
#endifLegacySupport(L);lua_pop(L, 1);return 1;
}

可以看到,UnLua在全局环境中定义了UnLua表,访问UnLua.XXX时,会直接去加载UnLua.XXX.lua文件,另外UnLua重写了require函数,改用HotReload模块,用于热重载的支持。

UUnLuaManager

UUnLuaManager构造函数则主要初始化UE Input相关的逻辑。

UUnLuaManager::UUnLuaManager(): InputActionFunc(nullptr), InputAxisFunc(nullptr), InputTouchFunc(nullptr), InputVectorAxisFunc(nullptr), InputGestureFunc(nullptr), AnimNotifyFunc(nullptr)
{if (HasAnyFlags(RF_ClassDefaultObject)){return;}GetDefaultInputs();             // get all Axis/Action inputsEKeys::GetAllKeys(AllKeys);     // get all key inputs// get template input UFunctions for InputAction/InputAxis/InputTouch/InputVectorAxis/InputGesture/AnimNotifyUClass *Class = GetClass();InputActionFunc = Class->FindFunctionByName(FName("InputAction"));InputAxisFunc = Class->FindFunctionByName(FName("InputAxis"));InputTouchFunc = Class->FindFunctionByName(FName("InputTouch"));InputVectorAxisFunc = Class->FindFunctionByName(FName("InputVectorAxis"));InputGestureFunc = Class->FindFunctionByName(FName("InputGesture"));AnimNotifyFunc = Class->FindFunctionByName(FName("TriggerAnimNotify"));
}

总结

自此我们梳理了UnLua的整个初始化流程,UnLua的初始化主要分为两个部分,一部分是C++层的初始化,另一部分是Lua层的初始化。C++层主要完成了Lua虚拟机的创建和UE相关的注册表的创建,Lua层则完成了最后的注册和热重载支持。UnLua的设计思路还是比较清晰的,后续我们会继续分析UnLua与UE交互的一些细节。

Reference

[1] UnLua GitHub

[2] UE4和UnLua交互核心环境分析

相关文章:

UnLua源码分析(一)初始化流程

UnLua源码分析&#xff08;一&#xff09;初始化流程 接入插件启动注册设置默认参数集注册回调SetActivePostLoadMapWithWorldULuaEnvLocator启动Lua虚拟机初始化UE相关的Lua Lib创建与Lua交互的数据结构注册静态导出的类&#xff0c;函数&#xff0c;枚举Lua层初始化 UUnLuaMa…...

非线性1 修改

第一章为读者介绍了非线性动力学与混沌理论的基本概念、发展历史以及应用领域。 1.1 动力学简史&#xff1a; 从牛顿力学开始&#xff0c;介绍动力学作为物理学分支的发展历程。重点介绍了庞加莱对混沌现象的早期探索&#xff0c;以及20世纪60年代洛伦兹方程的发现&#xff0…...

系统性能不达标,如何提升用户体验?

当系统性能不达标时&#xff0c;要想有效提升用户体验&#xff0c;必须从性能优化、前后端协同、用户感知改善、监控预警机制四个关键维度切入。其中&#xff0c;性能优化是最直接有效的策略&#xff0c;它通过代码优化、资源压缩、缓存机制、CDN加速等手段&#xff0c;显著提升…...

【Linux】进程的基本概念

目录 概念描述进程-PCB如何查看进程通过系统目录进行查看通过ps指令进行查看 通过系统调用获取进程的PID和PPID(进程标⽰符)通过系统调用创建子进程通过一段代码来介绍fork为什么要有子进程&#xff1f;fork为什么给子进程返回0&#xff0c;给父进程返回子进程的PIDfork函数到底…...

mongodb管理工具的使用

环境&#xff1a; 远程服务器的操作系统&#xff1a;centOS stream 9; mongoDB version:8.0; 本地电脑 navicat premium 17.2 ; 宝塔上安装了mongoDB 目的&#xff1a;通过本地的navicat链接mongoDB,如何打通链接&#xff0c;分2步&#xff1a; 第一步&#xff1a;宝塔-&…...

面试中的线程题

原文链接&#xff1a;线程题大全 Java 并发库同步辅助类 CountDownLatch 工作机制&#xff1a;初始化一个计数器&#xff0c;此计数器的值表示需要等待的事件数量。 提供了两个主要方法&#xff1a; await()&#xff1a;当一个线程调用此方法时&#xff0c;它将阻塞&#…...

计算机系统的层次结构

计算机系统的层次结构 一, 计算机系统的层次结构的定义 计算机的一个赢软件组成的综合体. 因为面对的应用范围越来越广, 所以必须有复杂的系统软件和硬件的支持. 软/硬件的设计者和使用者从不同的角度, 不同的语言来对待同一个计算机系统, 因此他们看到的计算机系统的属性对计…...

[特殊字符] SSL/TLS 中的密钥协商流程笔记

✅ 背景说明&#xff1a; SSL/TLS 为了确保通信保密性&#xff0c;结合使用了&#xff1a; 非对称加密&#xff08;用于密钥交换&#xff09; 对称加密&#xff08;用于数据加密&#xff09; &#x1f501; 客户端主导密钥协商的完整流程如下&#xff1a; 1️⃣ 客户端发起…...

多模态大语言模型arxiv论文略读(八十一)

What is the Visual Cognition Gap between Humans and Multimodal LLMs? ➡️ 论文标题&#xff1a;What is the Visual Cognition Gap between Humans and Multimodal LLMs? ➡️ 论文作者&#xff1a;Xu Cao, Bolin Lai, Wenqian Ye, Yunsheng Ma, Joerg Heintz, Jintai …...

SpringBoot(一)--- Maven基础

目录 前言 一、初始Maven 1.依赖管理 2.项目构建 3.统一项目结构 二、IDEA集成Maven 1.Maven安装 2.创建Maven项目 2.1全局设置 2.2 创建SpringBoot项目 2.3 常见问题 三、单元测试 1.JUnit入门 2.断言 前言 Maven 是一款用于管理和构建Java项目的工具&#xff…...

如何使用WordPress创建美食博客

不管你是否意识到&#xff0c;食物是我们生活的核心。有些人将其用作燃料&#xff0c;而另一些人则将食谱作为一种艺术形式呈现。如果您属于后者&#xff0c;并且想创建一个美食博客来分享您的热情&#xff0c;那么WordPress是一个顶级平台。 几乎每个话题都有一个博客利基&am…...

SAP集团内部公司间交易自动开票

SAP集团内部公司间交易自动开票(非STO/EDI模式) 集团内部公司间采购与销售业务&#xff0c;在确认相应单据无误后&#xff0c;为减少人工开票业务&#xff0c; 可以用系统标准功能来实现自动开票。 1.采购发票自动开票(ERS) T-CODE:BP,勾选“基于收货的发票校验”、“自动G…...

代码审计-php框架开发,实战tp项目,打击微交易,源码获取,扩大战果

实战&#xff0c;不安全写法引发的注入 这个bc靶场源码没有&#xff0c;看老师演示 打开很明显的tp框架源码 拿到tp框架之后第一步&#xff0c;搜索版本信息5.0.5 两个思路 1.代码的不安全写法 2.版本自身存在的漏洞 全局搜索where看看也没有不安全的 哎&#xff1f;&…...

Atcoder Beginner Contest 406

比赛链接&#xff1a;ABC406 A - Not Acceptable 将小时转换成分钟直接进行判断。 时间复杂度&#xff1a; O ( 1 ) O(1) O(1)。 #include <bits/stdc.h> using namespace std;int main() {ios::sync_with_stdio(false); cin.tie(nullptr); cout.tie(nullptr);int a,…...

02 K8s双主安装

00 前提准备 # 设置好主机名并添加hosts解析主机名IP地址身份master01172.2.25.50主Mastermaster02172.2.25.51备Masternode01172.2.25.52节点1node02172.2.25.53节点2node03172.2.25.54节点3 01 使用脚本安装Docker # Docker脚本文件 https://node-siyuan.oss-cn-beijing.a…...

分别用 语言模型雏形N-Gram 和 文本表示BoW词袋 来实现文本情绪分类

语言模型的雏形 N-Gram 和简单文本表示 Bag-of-Words 语言表示模型简介 (1) Bag-of-Words (BoW) 是什么&#xff1f; *定义&#xff1a;将文本表示为词频向量&#xff0c;忽略词序和语法&#xff0c;仅记录每个词的出现次数。 **示例&#xff1a; 句子1&#xff1a;I love …...

2_Spring【IOC容器中获取组件Bean】

Spring中IOC容器中获取组件Bean 实体类 //接口 public interface TestDemo {public void doSomething(); } // 实现类 public class HappyComponent implements TestDemo {public void doSomething() {System.out.println("HappyComponent is doing something...")…...

一次页面假死分析

现象&#xff1a;系统的几个功能若干次切换&#xff0c;页面假死&#xff0c;无反应 上图&#xff0c;不是每次切换都假死&#xff0c;但非常容易重现&#xff0c;每次页面假死都在getValidatorJSONInfo这个调用后 getValidatorJSONInfo调用已返回&#xff0c;但返回内容页面挂…...

WPS多级标题编号以及样式控制

WPS多级标题编号以及样式控制 上一篇&#xff1a;WPS自动生成图表目录以及样式控制 阅读提示&#xff1a;请先阅读完本篇内容再操作&#xff0c;避免出现其他错误。 初始样式&#xff1a; 1.1 步骤演示 说明&#xff1a;以3级目录为例 标题1样式&#xff1a;黑体&#xff0c;…...

方差是什么?

一、方差是什么? 方差(Variance)是概率论和统计学中衡量数据离散程度(即数据与平均值的偏离程度)的核心指标。 通俗理解:方差越大,数据越“分散”“波动大”;方差越小,数据越“集中”“稳定”。 二、方差的公式(以样本数据为例) 计算步骤: 第一步:求平均值 ( …...

Java核心API实战:从字符串到多线程全解析

Java常用API详解与代码实践 一、字符串处理类 1. String类 // 字符串基础操作 String str "Hello,Java!"; System.out.println(str.substring(7)); // 输出"Java!" System.out.println(str.indexOf("Java")); // 输出7// 正则表达式匹配 Str…...

低代码AI开发新趋势:Dify平台化开发实战

在人工智能快速发展的今天&#xff0c;AI应用的开发方式也在不断演变。从传统的手写代码到如今的低代码甚至零代码开发&#xff0c;技术的进步让更多的非专业开发者也能轻松上手。本文将带你走进Dify平台化开发的世界&#xff0c;探索如何通过这一强大的低代码AI开发平台&#…...

基于First Order Motion与TTS的AI虚拟主播系统全流程实现教程

前言&#xff1a;多模态虚拟主播的技术革命 在AI内容生成领域&#xff0c;虚拟主播技术正经历从2D到3D、从固定模板到个性化定制的跨越式发展。本文将深入解析如何通过Python技术栈构建支持形象定制与声音克隆的AI虚拟主播系统&#xff0c;涵盖从人脸建模到多模态融合的全流程…...

中科院自动化研究所通用空中任务无人机!基于大模型的通用任务执行与自主飞行

作者&#xff1a; Ji Zhao and Xiao Lin 单位&#xff1a;中科院自动化研究所 论文标题&#xff1a;General-Purpose Aerial Intelligent Agents Empowered by Large Language Models 论文链接&#xff1a;https://arxiv.org/pdf/2503.08302 主要贡献 硬件-软件协同设计框…...

DOM知识点

DOM&#xff08;Document Object Model&#xff09;即文档对象模型&#xff0c;是用于表示和操作HTML、XML文档的编程接口。以下是关于DOM的一些知识点总结&#xff1a; 基本概念 • DOM将文档解析为一个由节点&#xff08;Node&#xff09;组成的树形结构&#xff0c;每个节…...

设备预测性维护的停机时间革命:中讯烛龙如何用AI重构工业设备管理范式

在工业4.0的智能化浪潮中&#xff0c;非计划停机每年吞噬企业3%-8%的产值。中讯烛龙预测性维护系统通过多模态感知矩阵分布式智能体的创新架构&#xff0c;实现设备健康管理的范式跃迁&#xff0c;帮助制造企业将停机时间压缩70%以上。本文将深度解析技术实现路径与行业级实践方…...

JMeter 教程:使用 HTTP 请求的参数列表发送 POST 请求(form 表单格式)

目录 ✅ 教程目的 &#x1f6e0;️ 准备工作 &#x1f4c4; 操作步骤 第一步&#xff1a;新建测试计划 第二步&#xff1a;添加 HTTP 请求 第三步&#xff1a;添加参数列表&#xff08;表单参数&#xff09; 第四步&#xff1a;添加结果查看器 第五步&#xff1a;运行测…...

**HTTP/HTTPS基础** - URL结构(协议、域名、端口、路径、参数、锚点) - 请求方法(GET、POST) - 请求头/响应头 - 状态码含义

# HTTP/HTTPS基础知识点详解 ## 1. URL结构 ### 协议部分 - http:// - 超文本传输协议&#xff0c;明文传输 - https:// - 安全的超文本传输协议&#xff0c;使用SSL/TLS加密 - 爬虫注意点&#xff1a;HTTPS网站有TLS指纹(JA3指纹)&#xff0c;可被用于检测爬虫 ### 域名部分…...

2025抓包工具Reqable手机抓包HTTPS亲测简单好用-快速跑通

前言 自安卓7.0高版本系统不在信任用户证书&#xff0c;https抓包方式市面查找方法太过复杂手机要root等&#xff0c;前置条件要求太高太复杂&#xff0c;看的头痛&#xff0c;今天一台电脑按步骤操作完即可抓包https,给大家搞定抓包https问题。支持直接编辑修改请求参…...

大模型在胫骨平台骨折预测及治疗方案制定中的应用研究

目录 一、引言 1.1 研究背景与目的 1.2 国内外研究现状 1.3 研究方法和创新点 二、大模型预测胫骨平台骨折的原理和方法 2.1 相关大模型介绍 2.2 数据收集与预处理 2.3 模型训练与优化 2.4 模型评估指标 三、术前预测与手术方案制定 3.1 骨折类型和严重程度预测 3.…...

AI图像处理之ComfyUI节点(二)

一、Load Checkpoint节点 功能&#xff1a;用于加载预训练的大模型&#xff0c;如Stable Diffusion等 参数&#xff1a; chechkpoint&#xff1a;选择加载的模型文件&#xff08;.ckpt或.satetensors格式&#xff09; MODEL&#xff1a;输出用于图像扩散预测的UNet模型 CLIP&…...

异常日志规范

目录 一、错误码 二、异常处理 三、日志规约 一、错误码 强制&#xff1a; 1、错误码的制订原则&#xff1a;快速溯源、沟通标准化。 1&#xff09;错误码必须能够快速知晓错误来源&#xff0c;可快速判断是谁的问题。 2&#xff09;错误码必须能够清晰地比对&#xff08;…...

pycharm连接github(详细步骤)

【前提&#xff1a;菜鸟学习的记录过程&#xff0c;如果有不足之处&#xff0c;还请各位大佬大神们指教&#xff08;感谢&#xff09;】 1.先安装git 没有安装git的小伙伴&#xff0c;看上一篇安装git的文章。 安装git&#xff0c;2.49.0版本-CSDN博客 打开cmd&#xff08;…...

UESplineThicken节点

SplineThicken 函数用来使非常薄的多边形在渲染时显示为略厚。对于线缆、头发、草和其他此类对象&#xff0c;这种效果非常理想。 SplineThicken节点 该节点始终朝向摄像机 可以控制模型前后宽度 可以连接到wpo上制作特效效果 参考链接 https://dev.epicgames.com/document…...

【C++】map和set的使用

&#x1f4cc; 个人主页&#xff1a; 孙同学_ &#x1f527; 文章专栏&#xff1a;C &#x1f4a1; 关注我&#xff0c;分享经验&#xff0c;助你少走弯路 文章目录 1. 序列式容器和关联式容器1.1 序列式容器1.2 关联式容器 2. set系列的使用2.1 set和multiset的参考文档2.2 se…...

MCP实战:在扣子空间用扣子工作流MCP,一句话生成儿童故事rap视频

扣子最近迎来重要更新&#xff0c;支持将扣子工作流一键发布成MCP&#xff0c;在扣子空间里使用。 这个功能非常有用&#xff0c;因为我有很多业务工作流是在扣子平台上做的&#xff0c;两者打通之后&#xff0c;就可以在扣子空间里直接通过对话方式调用扣子工作流了&#xff0…...

c/c++的opencv直方图初识

C/C OpenCV中的图像直方图&#xff1a;零基础入门指南 &#x1f4ca; 大家好&#xff01;今天我们来聊聊图像处理中一个非常基础且重要的概念——直方图(Histogram)。如果你是OpenCV新手&#xff0c;或者对直方图感觉有点迷糊&#xff0c;别担心&#xff0c;这篇文章会用最简单…...

Spring Boot 与 RabbitMQ 的深度集成实践(一)

引言 ** 在当今的分布式系统架构中&#xff0c;随着业务复杂度的不断提升以及系统规模的持续扩张&#xff0c;如何实现系统组件之间高效、可靠的通信成为了关键问题。消息队列作为一种重要的中间件技术&#xff0c;应运而生并发挥着举足轻重的作用。 消息队列的核心价值在于其…...

Android动态音频柱状图可视化解析:从原理到实现

Android动态音频柱状图可视化解析:从原理到实现 一、整体架构设计二、核心组件设计三、核心代码实现四、交互设计与用户体验五、性能优化与问题解决一、整体架构设计 Android动态音频柱状图可视化解析 在移动应用开发中,音频可视化是增强用户体验的重要手段。无论是音乐播放器…...

vue3大事件项目

这周写完了vue3的大事件项目&#xff0c;从中学到了很多东西&#xff0c;并且解決了一部分bug&#xff0c;現在就和大家分享一下我遇到的问题并且是如何解決的 1. QuillEditor 的 v-model 用法错误 先讲一下quilleditor富文本的基本使用方法&#xff1a; 1.安裝quill依賴&am…...

MapReduce-WordCount实现按照value降序排序、字符小写、识别不同标点

要求&#xff1a; 输入文件的按照空格、逗号、点号、双引号等分词 输入文件的大写字母全部换成小写 文件输出要求按照value值降序排序 Hadoop给的wordcount示例代码以及代码理解 基于map reduce的word count个人理解&#xff1a;输入的文件经过map reduce框架处理后&#…...

c++线段树之单点修改区间最大子段和-----P4513 小白逛公园

题目大意 单点修改查询区间最大字段和 解题思路 如果线段树节点存储的是‘区间最大子段和’&#xff0c;如何合并&#xff1f; 简单的加法或求极值都不行&#xff0c;仔细分析可得&#xff0c;父节点最大字段和可能为&#xff1a; 左子树最大子段和右子树最大子段和左子树最…...

[Java实战]Spring Boot整合Elasticsearch(二十六)

[Java实战]Spring Boot整合Elasticsearch&#xff08;二十六&#xff09; 摘要&#xff1a;本文通过完整的实战演示&#xff0c;详细讲解如何在Spring Boot项目中整合Elasticsearch&#xff0c;实现数据的存储、检索和复杂查询功能。包含版本适配方案、Spring Data Elasticsea…...

【深度学习新浪潮】大模型在哪些垂域已经有比较好的落地?

AI大模型在多个垂直领域已实现显著落地,以下结合可验证案例与行业数据展开说明: 一、医疗健康:精准诊断与个性化治疗 呼吸系统疾病诊断 国家呼吸医学中心研发的LungDiag模型,基于公开临床数据集训练,在预印本研究中对肺炎、肺癌等10种疾病的辅助诊断准确率达92%。医联Med…...

软件测试全攻略:从概念到实践

目录 测试指南针--概念篇 1. 什么是软件测试&#xff1f; 2. 软件测试和软件开发的关系是什么&#xff1f; 3. 测试需要哪些能力&#xff1f; 4. 测试流程是什么样的&#xff1f; 5. 什么是单元测试和集成测试&#xff1f; 6. 软件的生命周期是什么样的&#xff1f; 需求…...

linux hungtask detect机制分析

1&#xff0c;机制概述‌ hungtask detect 是 Linux 内核用于检测长时间阻塞&#xff08;"hung"&#xff09;任务的机制&#xff0c;主要针对因死锁、死循环或资源竞争导致无法调度的任务 ‌触发条件‌&#xff1a;任务在 TASK_UNINTERRUPTIBLE 状态持续超过预设阈值…...

影刀处理 Excel:智能工具带来的高效变革

1. 高效的数据处理能力 1.1 快速读取与写入数据 影刀在处理 Excel 数据时展现出显著的读取与写入速度优势。传统方法处理大型 Excel 文件时&#xff0c;读取速度可能仅为每秒 100 行左右&#xff0c;而影刀通过优化底层代码和采用高效的文件解析算法&#xff0c;读取速度可达…...

2021ICPC四川省赛个人补题ABDHKLM

Dashboard - The 2021 Sichuan Provincial Collegiate Programming Contest - Codeforces 过题难度&#xff1a; A K D M H B L 铜奖 5 594 银奖 6 368 金奖 8 755 codeforces.com/gym/103117/problem/A 模拟出牌的过程&#xff0c;打表即可 // Code Start Here int t…...

HarmonyOS 影视应用APP开发--配套的后台服务go-imovie项目介绍及使用

网上有小伙伴对影视应用感兴趣&#xff0c;也想搞个自己的免费观影APP玩玩儿。前期博主开源的有uniapp版本和harmonyOS原生版本影视客户端&#xff0c;但是对博主开源的这个影视后台接口服务不太了解&#xff0c;不知道怎么用起来。这里总结介绍下该go-imove后台接口服务项目介…...

JAVA SE 多线程(上)

文章目录 &#x1f4d5;1. Thread类及常见方法✏️1.1 创建线程✏️1.2 Thread 的常见构造方法✏️1.3 Thread 的几个常见属性✏️1.4 启动一个线程---start()✏️1.5 中断一个线程---interrupt()✏️1.6 等待一个线程---join()✏️1.7 获取当前线程引用✏️1.8 休眠当前线程 &…...