【SpringBoot】万字源码解析——启动流程
Spring Boot启动流程
Spring Boot 的入口类:
@SpringBootApplication
public class IntelGradingApplication {public static void main(String[] args) {SpringApplication.run(IntelGradingApplication.class, args);}
}
Spring Boot 的启动过程可以分为两方面,一个是 new SpringApplication(primarySources) 的初始化过程,一个是 SpringApplication.run() 的应用运行过程。
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {return run(new Class[]{primarySource}, args);
}public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {// 传入primarySources数组,构造SpringApplicationreturn (new SpringApplication(primarySources)).run(args);
}
执行构造函数
构造函数是 Spring Boot 启动过程的第一步,负责初始化各种属性、验证输入并设置必要的上下文,为后续的应用启动和上下文配置奠定基础。Spring Boot 通过一个重载构造函数来接收 null 值的资源加载器和主类数组:
public SpringApplication(Class<?>... primarySources) {this((ResourceLoader)null, primarySources);
}public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {....
}
1.初始化字段
重构的构造函数接收ResourceLoader和primarySources参数,并为各种字段进行初始化,比如ResourceLoader, bannerMode, headless等。
this.sources = new LinkedHashSet();
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.addConversionService = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = Collections.emptySet();
this.isCustomEnvironment = false;
this.lazyInitialization = false;
this.applicationContextFactory = ApplicationContextFactory.DEFAULT;
this.applicationStartup = ApplicationStartup.DEFAULT;
this.resourceLoader = resourceLoader;
2.设置主源
将primarySources转换为LinkedHashSet,以确保启动过程中按顺序处理,避免重复。
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet(Arrays.asList(primarySources));
3.推断应用类型
通过检查类路径classpath中的组件(如 "org.springframework.web.reactive.DispatcherHandler)来确定当前应用的类型:
- REACTIVE:响应式 Web 应用(基于 Reactor 和 WebFlux)。
- SERVLET:传统的 Servlet 类型 Web 应用(如使用 Spring MVC)。
- NONE:非 Web 应用,通常是命令行应用。
this.webApplicationType = WebApplicationType.deduceFromClasspath();
4.加载工厂实例
从 META-INF/spring.factories 中加载配置的初始化器 ApplicationContextInitializer 、BootstrapRegistryInitializer和监听器 ApplicationListener。
BootstrapRegistryInitializer允许在Spring Boot启动时自定义初始化Bootstrap注册表。Bootstrap注册表是一个支持应用上下文的初始化过程的机制,通常用于配置与应用启动相关的共享资源。ApplicationContextInitializer允许用户在ApplicationContext刷新前进行进一步的初始化配置操作。这包括但不限于添加属性源、修改环境变量、设置 Bean 定义等。ApplicationListener,用于监听 Spring Boot 的生命周期事件。这些监听器会在应用启动过程中响应不同的事件,如应用启动事件、环境准备事件、上下文刷新事件等。
// 加载Bootstrap注册表初始化器
this.bootstrapRegistryInitializers = new ArrayList(this.getSpringFactoriesInstances(BootstrapRegistryInitializer.class));
// 加载应用上下文初始化器
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 加载监听器
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
getSpringFactoriesInstances() 方法的核心方法:
loadFactoryNames():利用 SpringFactoriesLoader 获取指定工厂的实现类的 Set 集合createSpringFactoriesInstances:通过反射机制实例化每个工厂的实现类
// 利用Spring工厂加载器获取指定工厂的实现类的set集合
Set<String> names = new LinkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 通过反射机制实例化每个工厂的实现类
List<T> instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
5.推断主类
通过 StackTraceElement 找到 main 方法,确定应用程序的入口类。这通常是标记了 @SpringBootApplication 注解的类。
this.mainApplicationClass = this.deduceMainApplicationClass();
执行run方法
1.启动计时器并初始化
-
在执行
run方法的开头,首先启动一个计时器以记录应用启动的总时长。 -
接着,创建一个
DefaultBootstrapContext,它会遍历并执行在Bootstrap注册表中的所有初始化器,以确保启动过程中的必要资源和设置得到正确配置。 -
然后,
ConfigurableApplicationContext的引用被声明为null,并配置无头属性,以便在没有用户界面的环境中正常运行。
// 1.1.启动计时器
long startTime = System.nanoTime();
// 1.2.初始化bootstrapContext上下文,该方法会遍历并执行BootStrap注册表中的初始化器
DefaultBootstrapContext bootstrapContext = this.createBootstrapContext();
// 1.3.声明ApplicationContext为null
ConfigurableApplicationContext context = null;
// 1.4.设置无头属性
this.configureHeadlessProperty();
2.获取并启动监听器
// 2.1.获取监听器
SpringApplicationRunListeners listeners = this.getRunListeners(args);
// 2.2.通知监听器,应用程序即将启动
listeners.starting(bootstrapContext, this.mainApplicationClass);
在获取监听器的方法 getRunListeners() 中,将所有的监听器封装为一个 SpringApplicationRunListeners 对象,由于在构造函数执行阶段已经加载了监听器对象,在调用方法 getSpringFactoriesInstances 时会直接查询缓存获取对象。
// 2.1.获取监听器
private SpringApplicationRunListeners getRunListeners(String[] args) {Class<?>[] types = new Class[]{SpringApplication.class, String[].class};// 封装从getSpringFactoriesInstances()方法中获得工厂的所有实现类return new SpringApplicationRunListeners(logger, this.getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args), this.applicationStartup);
}
通知监听器应用即将启动。这一步骤确保所有监听器能够在应用启动的早期阶段参与并进行必要的初始化。
// 2.2.通知监听器
listeners.starting(bootstrapContext, this.mainApplicationClass)
3.装配环境参数
- 将环境参数与**「引导上下文」**绑定,
prepareEnvironment()方法会加载应用的外部配置。这包括application.properties或application.yml文件中的属性,环境变量,系统属性等。
// 3.1.创建命令行参数对象
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
// 3.2.加载应用的外部配置
ConfigurableEnvironment environment = this.prepareEnvironment(listeners, bootstrapContext, applicationArguments);
// 3.3.配置忽略 Bean 信息
this.configureIgnoreBeanInfo(environment);
在prepareEnvironment() 方法中,首先会进行环境配置,还会执行监听器的 environmentPrepared() 方法,表明应用程序的环境已经准备好,最后再将环境绑定到应用程序中。
4.打印横幅
Banner printedBanner = this.printBanner(environment);
5.创建应用上下文
根据构造阶段推断出的 Web 应用类型,创建Spring容器
// 5.调用createApplicationContext方法创建Spring容器
context = this.createApplicationContext();
6.应用上下文准备阶段
this.prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
进入 prepareContext()方法,具体实现如下:
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) {// 6.1.将传入的环境参数应用到上下文中,并调用后处理方法context.setEnvironment(environment);this.postProcessApplicationContext(context);// 6.2.遍历并执行所有注册的初始化器,进一步配置应用上下文this.applyInitializers(context);// 6.3.通知监听器,上下文准备完毕listeners.contextPrepared(context);// 6.4.关闭启动阶段的引导上下文,释放与启动相关的资源bootstrapContext.close(context);// 6.5.打印日志启动信息if (this.logStartupInfo) {this.logStartupInfo(context.getParent() == null);this.logStartupProfileInfo(context);}// 6.6.将命令行参数和横幅注册为Bean,存放到应用上下文中ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();beanFactory.registerSingleton("springApplicationArguments", applicationArguments);if (printedBanner != null) {beanFactory.registerSingleton("springBootBanner", printedBanner);}// 6.7.根据Bean工厂,允许配置循环引用和 bean 定义覆盖if (beanFactory instanceof AbstractAutowireCapableBeanFactory) {((AbstractAutowireCapableBeanFactory)beanFactory).setAllowCircularReferences(this.allowCircularReferences);if (beanFactory instanceof DefaultListableBeanFactory) {((DefaultListableBeanFactory)beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);}}// 6.8.设置懒初始化配置if (this.lazyInitialization) {context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());}context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));// 6.9.加载源,确保应用上下文中定义的 bean 被正确创建。Set<Object> sources = this.getAllSources();Assert.notEmpty(sources, "Sources must not be empty");this.load(context, sources.toArray(new Object[0]));// 6.10.通知监听器,所有的Bean都已经加载但未进行实例化listeners.contextLoaded(context);
}
7.应用上下文刷新阶段
this.refreshContext(context);
- 首先通过加锁确保线程安全,创建并配置
BeanFactory,这一过程包括注册 Bean 后处理器和事件监听器。 - 在
onRefresh()方法中,还会启动 Web 服务器。 - 最后,通过配置好的
BeanFactory实例化所有的Beans。在这个过程中,BeanFactory会根据定义的元数据创建和初始化Beans,并根据需求进行依赖注入,确保整个应用的组件能够顺利协作。
8.应用上下文收尾阶段
// 8.1.afterRefresh()无实际内容,后续版本被移除
this.afterRefresh(context, applicationArguments);
// 8.2.计算应用启动时间
Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
if (this.logStartupInfo) {(new StartupInfoLogger(this.mainApplicationClass)).logStarted(this.getApplicationLog(), timeTakenToStartup);
}
// 8.3.通知监听器,应用程序启动完成
listeners.started(context, timeTakenToStartup);
9.回调运行器
// 9.1.回调运行器
this.callRunners(context, applicationArguments);
// 9.2.通知监听器
Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
listeners.ready(context, timeTakenToReady);
该方法从上下文 context 中获取所有已注册的 ApplicationRunner 和 CommandLineRunner,并结合传入的应用参数 args执行这些运行器。
这些运行器允许开发者在应用程序启动后执行特定的逻辑,例如初始化数据、设置应用状态或执行启动任务,提供了灵活性和扩展性。
private void callRunners(ApplicationContext context, ApplicationArguments args) {List<Object> runners = new ArrayList();// 9.1.1.从上下文中获取运行器runners.addAll(context.getBeansOfType(ApplicationRunner.class).values());runners.addAll(context.getBeansOfType(CommandLineRunner.class).values());AnnotationAwareOrderComparator.sort(runners);Iterator var4 = (new LinkedHashSet(runners)).iterator();// 9.1.2.结合应用参数执行运行器while(var4.hasNext()) {Object runner = var4.next();if (runner instanceof ApplicationRunner) {this.callRunner((ApplicationRunner)runner, args);}if (runner instanceof CommandLineRunner) {this.callRunner((CommandLineRunner)runner, args);}}
}
10.异常处理
在整个启动过程中,如果出现任何异常,都会被捕获并通过handleRunFailure()方法进行处理,在该方法中,会通知监听器应用程序启动时出现异常。
该方法会记录错误信息,并通过监听器通知失败事件。最终,抛出IllegalStateException来中止应用启动,确保调用者能够识别到启动失败的状态。
catch (Throwable var12) {ex = var12;this.handleRunFailure(context, ex, listeners);throw new IllegalStateException(ex);
}
SpringApplicationRunListeners监听器
SpringApplicationRunListeners 是一个具体的类。它实现了 Spring Boot 中的监听器机制,用于在应用程序的不同启动阶段通知注册的监听器(SpringApplicationRunListener 接口的实现类)。通过这个类,Spring Boot 可以在应用启动过程中管理多个监听器,处理各种生命周期事件。
SpringApplicationRunListeners(Log log, Collection<? extends SpringApplicationRunListener> listeners, ApplicationStartup applicationStartup) {// 记录日志this.log = log;// 保存已注册的监听器列表,这些监听器会对应用程序生命周期事件做出响应this.listeners = new ArrayList(listeners);// 跟踪启动步骤,以便进行性能监控this.applicationStartup = applicationStartup;
}
SpringApplicationRunListener 是 Spring Boot 中的一个接口,用于在应用启动过程的不同阶段提供回调。实现这个接口允许监听并响应应用生命周期中的关键事件。该接口定义了多个方法,每个方法对应启动过程中的特定阶段,包括:
- starting(): 在运行开始时调用,此时尚未开始任何处理,可以用于初始化在启动过程中需要的资源。
- environmentPrepared(): 当
SpringApplication准备好Environment但在创建ApplicationContext之前调用,这是修改应用环境属性的好时机。 - contextPrepared(): 当
ApplicationContext准备好但在加载之前调用,可以用于对上下文进行预处理。 - contextLoaded(): 当
ApplicationContext被加载但在刷新之前调用,此时所有的 Bean 定义都已加载,但尚未实例化。 - started(): 在
ApplicationContext刷新之后、任何应用运行器和命令行运行器被调用之前调用,此时应用已经准备好接收请求。 - running(): 在运行器被调用之后、应用启动完成之前调用,这是在应用启动并准备好服务请求时执行某些动作的好时机。
- failed(): 如果启动过程中出现异常,则调用此方法。
SpringFactoriesLoader原理
SpringFactoriesLoader方法会根据传入的工厂类和类加载器,从 META-INF/spring.factories 文件中加载**「指定类型对应的工厂类名称」**。
loadFactoryNames()
loadFactoryNames 方法是一个高级 API,它通过获取入参中的全限定类名 factoryTypeName,在内部调用 loadSpringFactories() 方法获取返回的 Map 集合,并根据 factoryTypeName 获取了 Map 中的实现类的 List 集合。
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {ClassLoader classLoaderToUse = classLoader;if (classLoaderToUse == null) {classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();}// 获取工厂类的全限定名String factoryTypeName = factoryType.getName();// 从 loadSpringFactories 返回的 Map 中获取指定类型工厂的实现类return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());
}
loadSpringFactories()
loadSpringFactories 方法是更加底层的方法,通过缓存机制和类加载器获取 spring.factories 文件中所有配置的工厂及其实现类,将这些信息封装为 Map 集合后返回给上游的 API。
缓存机制
方法会检查是否已经通过当前类加载器加载过 spring.factories 文件。如果缓存 (cache) 中已经存在相应的工厂信息,直接返回缓存的 Map<String, List<String>>,避免重复加载。
Map<String, List<String>> result = (Map)cache.get(classLoader);
if (result != null) {return result;
}
加载 META-INF/spring.factories
方法会通过类加载器查找所有路径下名为 META-INF/spring.factories 的文件。由于每个 JAR 包都可能包含一个 META-INF/spring.factories 文件,方法会返回一个 Enumeration<URL> 对象,表示找到的所有相关资源文件。
Enumeration<URL> urls = classLoader.getResources("META-INF/spring.factories");
解析spring.factories文件
通过迭代逐个读取每个找到的 spring.factories 文件。对于每个文件,使用 PropertiesLoaderUtils.loadProperties() 将文件内容解析为 Properties 对象。
每个 Properties 对象对应一个 spring.factories 文件的内容,其中 key 是工厂类型(例如 org.springframework.context.ApplicationContextInitializer),value 是逗号分隔的工厂实现类列表。
while(urls.hasMoreElements()) {URL url = (URL)urls.nextElement();UrlResource resource = new UrlResource(url);Properties properties = PropertiesLoaderUtils.loadProperties(resource);
将工厂类型和实现类存入Map
遍历 Properties 的 entrySet()。对于每个 entry,key 是工厂类型的全限定类名,value 是对应的工厂实现类名(逗号分隔)。
工厂类型名称通过 entry.getKey() 获取,并使用 String.trim() 去除可能的空白字符。工厂实现类则将逗号分隔的字符串转换为实现类的数组。
while(var6.hasNext()) {Map.Entry<?, ?> entry = (Map.Entry)var6.next();String factoryTypeName = ((String)entry.getKey()).trim();String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String)entry.getValue());String[] var10 = factoryImplementationNames;int var11 = factoryImplementationNames.length;for(int var12 = 0; var12 < var11; ++var12) {String factoryImplementationName = var10[var12];((List)result.computeIfAbsent(factoryTypeName, (key) -> {return new ArrayList();})).add(factoryImplementationName.trim());}
}
相关文章:
【SpringBoot】万字源码解析——启动流程
Spring Boot启动流程 Spring Boot 的入口类: SpringBootApplication public class IntelGradingApplication {public static void main(String[] args) {SpringApplication.run(IntelGradingApplication.class, args);} }Spring Boot 的启动过程可以分为两方面&am…...
Nginx 配置初步 下
Nginx 配置初步(下) 一行代表一个指令; 每个指令有其上下文环境,比如 listen 指令只能在 http 指令块中出现,不能单独出现。1. Http 服务配置初步 1.1 常用指令 Nginx 的所有模块,打开模块我们就能看到模块中支持的指令。最常用…...
可视化ETL平台-Kettle的安装及简单使用
本章知识简介 主线A: 自连接查询; 主线B: 安装JDK与Kettle; 主线C: 使用Kettle工具. 本章目标: 1: 知道使用一张表可以实现自连接查询; [了解]注意: 左表、右表都是同一张表 2: 了解Kettle环境的安装流程; [了解]a.安装JDKb.安装Kettle 3: 熟悉使用kettle将txt数…...
java8 动态加载jar包至系统的classpath
1. io.test包 创建MyMain.java类,创建addJarToClasspath方法将jar包动态加载进系统的classpath中 package io.test;import java.io.File; import java.net.URL; import java.net.URLClassLoader; import java.lang.reflect.Method;public class MyMain {public st…...
C++二级题 计算好数:1数大于0数(二进制的位运算)
1、题目 若将一个正整数化为二进制数,在此二进制数中,我们将数字1的个数多于数字0的个数的这类二进制数称为好数。 例如: (13)10 (1101)2,其中1的个数为3,0的个数为1,则此数是好数; (10)10 (1…...
数字孪生城市:智慧城市的未来蓝图
在当今数字化时代,智能技术的广泛应用正在改变人们的生活和工作方式。数字孪生城市作为未来新型智慧城市演进的重要方向,数字孪生城市是一种将城市物理世界的各个方面转化为数字形式的技术,通过网络空间与物理世界之间的实时数据交换和仿真分…...
Java篇图书管理系统
目录 前言 一. 图书管理系统的核心 二. 图书管理系统基本框架 2.1 book包 2.1.1 Book(书籍类) 2.1.2 Booklist (书架类) 2.2 user包 2.2.1 User类 2.2.2 Administrator(管理员类) 2.2.3 Visitor(用户类) 2.…...
BUUCTF之web篇
第一题 [极客大挑战 2019]EasySQL 打开靶机后可以看到这是一个登陆的页面 我们可以尝试两种方式登录 弱口令爆破(burpsuite) 通过SQL注入里的万能密码来跳过账户和密码验证的过程 这里就需要万能密码aor true # 在这里单引号的作用是结束用户名或者密码…...
010——二叉树(2)线索化
引入: 问题1: n个节点的二叉树,用二叉链表存储,问在这个二叉链表中一共有 __个指针域? 其中,有 __个指针域不为NULL,__个指针域为NULL? 答:2n n-1 n1 在二叉链表中…...
鸿蒙拍照小助手02
项目文件目录 为了确保项目文件目录清晰,以下是完整的项目文件目录结构: code 拍照小助手/ │ ├── entry/ │ ├── src/ │ │ ├── main/ │ │ │ ├── js/ │ │ │ │ └── 默认/ │ │ │ │ ├── 页面/ │ │ │ │ │ ├── 主页/ │ │ │ │ │ │ ├…...
lua while循环
软考鸭微信小程序 过软考,来软考鸭! 提供软考免费软考讲解视频、题库、软考试题、软考模考、软考查分、软考咨询等服务 Lua作为一种小巧精致的语言,特别适用于嵌入其他程序提供脚本支持。在编程中,循环结构是不可或缺的一部分,而while循环则是…...
JAVA篇之类和对象
目录 一. 面向对象 1.1 面向对象和面向过程 二. 类的定义和使用 2.1 什么是类 2.2 类的定义格式 三. 类的实例化 四. this引用 4.1 this引用的作用 五. 构造方法 5.1 构造方法重载 5.2 通过this调用其他构造方法 5.3 默认初始化 结语 一. 面向对象 Java 是一门面向对…...
IO流详解_CoderLix
主要内容 File类IO流字节流字符流异常处理Properties缓冲流转换流序列化流打印流 File类 1.1 概述 java.io.File 类是文件和目录路径名的抽象表示,主要用于文件和目录的创建、查找和删除等操作。 1.2 构造方法 public File(String pathname) :通过…...
241023-RHEL非管理员安装Docker并开放指定宿主机端口部署Gitlab
A. RHEL非管理员安装Docker 要在没有管理员权限的情况下离线安装 Docker 和 Docker Compose,虽然受到一定限制,仍有一些可行的步骤可以帮助你在有限权限下完成这项任务。需要注意的是,这种方式适用于本地用户环境下的 Docker 安装࿰…...
python ubuntu安装加速
ubuntu升级python到python3.11(可能是全网最靠谱的方法,亲测有效)_ubuntu python3.11-CSDN博客 python-release安装包下载_开源镜像站-阿里云...
100种算法【Python版】第12篇——快速幂算法
本文目录 1 基本原理2 基本步骤3 数学示例4 python代码1 基本原理 快速幂算法(Fast Exponentiation)是一种高效计算整数幂的方法,尤其适用于计算大数的幂。其主要思想是利用分治法和二进制表示来减少乘法运算的次数,从而加快计算速度。 计算 x n x^n x...
Java多线程详解②(全程干货!!!)Thread Runnable
这里是Themberfue 上节主要讲完了多线程的一些基础知识,这节通过代码进一步理解多线程🫡 多线程 Java标准库中提供了Thread类,以程序员们编写多线程代码,我们可以查看官方文档进一步了解Thread的特性以及提供的接口。 类似于Sy…...
机器学习——图神经网络
图神经网络(GNN):理解复杂网络数据的有效工具 图神经网络(Graph Neural Network, GNN)是近年来机器学习领域的热门话题。GNN 以图结构数据为核心,能够高效地捕捉节点和边的复杂关系,广泛应用于社交网络、推荐系统、生…...
一、在cubemx下RTC配置调试实例测试
一、rtc的时钟有lse提供。 二、选择rtc唤醒与闹钟功能 内部参数介绍 闹钟配置 在配置时间时,注意将时间信息存储起来,防止复位后时间重新配置。 if(HAL_RTCEx_BKUPRead(&hrtc, RTC_BKP_DR0)! 0x55AA)//判断标志位是否配置过,没有则进…...
【Nas】X-DOC:Mac mini Docker部署中国特供版Jellyfin
【Nas】X-DOC:Mac mini Docker部署中国特供版Jellyfin 1、拉取镜像:2、启动镜像3、访问服务4、参考文档 Mac mini Docker部署中国特供版Jellyfin 1、拉取镜像: docker pull nyanmisaka/jellyfin:230901-amd64jellyfin 10.8.10版本ÿ…...
Mybatis逆向工程,动态创建实体类、条件扩展类、Mapper接口、Mapper.xml映射文件
今天呢,博主的学习进度也是步入了Java Mybatis 框架,目前正在逐步杨帆旗航。 那么接下来就给大家出一期有关 Mybatis 逆向工程的教学,希望能对大家有所帮助,也特别欢迎大家指点不足之处,小生很乐意接受正确的建议&…...
线程与协程
1. 线程与协程 1.1. “函数调用级别”的切换、上下文切换 1. 函数调用级别的切换 “函数调用级别的切换”是指:像函数调用/返回一样轻量地完成任务切换。 举例说明: 当你在程序中写一个函数调用: funcA() 然后 funcA 执行完后返回&…...
基于Docker Compose部署Java微服务项目
一. 创建根项目 根项目(父项目)主要用于依赖管理 一些需要注意的点: 打包方式需要为 pom<modules>里需要注册子模块不要引入maven的打包插件,否则打包时会出问题 <?xml version"1.0" encoding"UTF-8…...
Java多线程实现之Thread类深度解析
Java多线程实现之Thread类深度解析 一、多线程基础概念1.1 什么是线程1.2 多线程的优势1.3 Java多线程模型 二、Thread类的基本结构与构造函数2.1 Thread类的继承关系2.2 构造函数 三、创建和启动线程3.1 继承Thread类创建线程3.2 实现Runnable接口创建线程 四、Thread类的核心…...
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...
Reasoning over Uncertain Text by Generative Large Language Models
https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829https://ojs.aaai.org/index.php/AAAI/article/view/34674/36829 1. 概述 文本中的不确定性在许多语境中传达,从日常对话到特定领域的文档(例如医学文档)(Heritage 2013;Landmark、Gulbrandsen 和 Svenevei…...
保姆级教程:在无网络无显卡的Windows电脑的vscode本地部署deepseek
文章目录 1 前言2 部署流程2.1 准备工作2.2 Ollama2.2.1 使用有网络的电脑下载Ollama2.2.2 安装Ollama(有网络的电脑)2.2.3 安装Ollama(无网络的电脑)2.2.4 安装验证2.2.5 修改大模型安装位置2.2.6 下载Deepseek模型 2.3 将deepse…...
基于鸿蒙(HarmonyOS5)的打车小程序
1. 开发环境准备 安装DevEco Studio (鸿蒙官方IDE)配置HarmonyOS SDK申请开发者账号和必要的API密钥 2. 项目结构设计 ├── entry │ ├── src │ │ ├── main │ │ │ ├── ets │ │ │ │ ├── pages │ │ │ │ │ ├── H…...
Python爬虫实战:研究Restkit库相关技术
1. 引言 1.1 研究背景与意义 在当今信息爆炸的时代,互联网上存在着海量的有价值数据。如何高效地采集这些数据并将其应用于实际业务中,成为了许多企业和开发者关注的焦点。网络爬虫技术作为一种自动化的数据采集工具,可以帮助我们从网页中提取所需的信息。而 RESTful API …...
MeshGPT 笔记
[2311.15475] MeshGPT: Generating Triangle Meshes with Decoder-Only Transformers https://library.scholarcy.com/try 真正意义上的AI生成三维模型MESHGPT来袭!_哔哩哔哩_bilibili GitHub - lucidrains/meshgpt-pytorch: Implementation of MeshGPT, SOTA Me…...
