手写springboot
前言
首先确定springboot在spring基础上主要做了哪些改动:
- 内嵌tomcat
- spi技术动态加载
一、基本实现
1. 建一个工程目录结构如下:
springboot: 源码实现逻辑
user : 业务系统

2.springboot工程项目构建
1. pom依赖如下
<dependencies><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>5.3.18</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-web</artifactId><version>5.3.18</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>5.3.18</version></dependency><dependency><groupId>javax.servlet</groupId><artifactId>javax.servlet-api</artifactId><version>4.0.1</version></dependency><dependency><groupId>org.apache.tomcat.embed</groupId><artifactId>tomcat-embed-core</artifactId><version>9.0.60</version></dependency></dependencies>
2. SpringBoot时,核心会用到SpringBoot一个类和注解:
- @SpringBootApplication,这个注解是加在应用启动类上的,也就是main方法所在的类
- SpringApplication,这个类中有个run()方法,用来启动SpringBoot应用的.
下面一一实现:
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Configuration@ComponentScan@Import(KcImportSelect.class)
public @interface KcSpringBootApplication {}
public class KcSpringApplication {public static AnnotationConfigWebApplicationContext run(Class cls){/*** spring启动步骤:* 1、构建上下文对象* 2、注册配置类* 3、刷新容器*/AnnotationConfigWebApplicationContext context=new AnnotationConfigWebApplicationContext();context.register(cls);context.refresh();/*** 内嵌tomcat\jetty 启动*/WebServer webServer=getWebServer(context);webServer.start();return context;}/*** 获取服务,有可能tomcat\jetty或者其他服务器* @param context* @return*/public static WebServer getWebServer(AnnotationConfigWebApplicationContext context){Map<String, WebServer> beansOfType = context.getBeansOfType(WebServer.class);if(beansOfType.isEmpty()||beansOfType.size()>1){throw new NullPointerException();}return beansOfType.values().stream().findFirst().get();}}
3.内嵌tomcat、jetty服务器实现
项目根据pom配置动态实现tomcat\jetty内嵌要求如下:
- 如果项目中有Tomcat的依赖,那就启动Tomcat
- 如果项目中有Jetty的依赖就启动Jetty
- 如果两者都没有则报错
- 如果两者都有也报错
首先定义服务接口WebServer
public interface WebServer {void start() ;
}
tomcat服务实现:
public class TomcatWebServer implements WebServer {private Tomcat tomcat;public TomcatWebServer(WebApplicationContext webApplicationContext) {tomcat = new Tomcat();Server server = tomcat.getServer();Service service = server.findService("Tomcat");Connector connector = new Connector();connector.setPort(8081);Engine engine = new StandardEngine();engine.setDefaultHost("localhost");Host host = new StandardHost();host.setName("localhost");String contextPath = "";Context context = new StandardContext();context.setPath(contextPath);context.addLifecycleListener(new Tomcat.FixContextListener());host.addChild(context);engine.addChild(host);service.setContainer(engine);service.addConnector(connector);tomcat.addServlet(contextPath, "dispatcher", new DispatcherServlet(webApplicationContext));context.addServletMappingDecoded("/*", "dispatcher");}@Overridepublic void start() {try {System.out.println("tomcat start......");tomcat.start();}catch (Exception e){e.printStackTrace();}}
}
jetty服务实现(具体实现逻辑没写,具体实现逻辑类似tomcat实现)
public class JettyWebServer implements WebServer{@Overridepublic void start() {System.out.println("jetty start......");}
}
思考:jetty\tomcat都已实现,总不能用if/else这样决定用哪个服务扩展性太差,基于此,就联想到spring的Condition条件注解和参考springboot中的OnClassCondition这个注解。
具体实现如下:
public class KcOnClassCondition implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(KcConditionalOnClass.class.getName());String value = (String) annotationAttributes.get("value");try {context.getClassLoader().loadClass(value);} catch (ClassNotFoundException e) {return false;}return true;}
}
@Configuration
public class WebServerConfiguration{@Bean@KcConditionalOnClass("org.apache.catalina.startup.Tomcat")public TomcatWebServer tomcatWebServer( WebApplicationContext webApplicationContext){return new TomcatWebServer(webApplicationContext);}@Bean@KcConditionalOnClass("org.eclipse.jetty.server.Server")public JettyWebServer jettyWebServer(){return new JettyWebServer();}
}
至此,springboot简化版已实现完成,首先启动看看

4.基于JDK的SPI实现扫描AutoConfiguration接口
- AutoConfiguration接口
public interface AutoConfiguration {
}
- 实现DeferredImportSelector接口实现类(具体为什么实现Import这个接口,请看以前的文章,主要这个接口具有延迟功能)
public class KcImportSelect implements DeferredImportSelector {@Overridepublic String[] selectImports(AnnotationMetadata importingClassMetadata) {ServiceLoader<AutoConfiguration> load = ServiceLoader.load(AutoConfiguration.class);List<String> list = new ArrayList<>();for (AutoConfiguration autoConfiguration : load) {list.add(autoConfiguration.getClass().getName());}return list.toArray(new String[0]);}
}
- 即KcSpringBootApplication注解导入该配置类
@Retention(RetentionPolicy.RUNTIME)@Target(ElementType.TYPE)@Configuration@ComponentScan@Import(KcImportSelect.class)
public @interface KcSpringBootApplication {}
- WebServerConfiguration实现AutoConfiguration接口
@Configuration
public class WebServerConfiguration implements AutoConfiguration{@Bean@KcConditionalOnClass("org.apache.catalina.startup.Tomcat")public TomcatWebServer tomcatWebServer( WebApplicationContext webApplicationContext){return new TomcatWebServer(webApplicationContext);}@Bean@KcConditionalOnClass("org.eclipse.jetty.server.Server")public JettyWebServer jettyWebServer(){return new JettyWebServer();}
}
- 在springboot项目下创建META-INFO/service 接口全路径命名的文件,文件内容接口实现类全路径

至此,springboot简易版已全部实现。
二、应用业务系统引入自己构建的springboot
1、user项目的pom依赖(引入自己构建的springboot项目)
<dependencies><dependency><groupId>com.kc</groupId><artifactId>springboot</artifactId><version>1.0-SNAPSHOT</version></dependency></dependencies>
2、启动类
@ComponentScan(basePackages = {"kc.*"})@KcSpringBootApplication
public class UserApplication {public static void main(String[] args) {AnnotationConfigWebApplicationContext run = KcSpringApplication.run(UserApplication.class);System.out.println(run.getBeanFactory());}
}
3、实现具体业务类
@RestController
public class UserController {@Autowiredprivate UserService userService;@GetMapping("test")public String test() {return userService.test();}
}
@Service
public class UserService {public String test() {return "hello springboot";}
}
4、启动测试访问

三、项目地址
git地址
相关文章:
手写springboot
前言 首先确定springboot在spring基础上主要做了哪些改动:内嵌tomcatspi技术动态加载 一、基本实现 1. 建一个工程目录结构如下: springboot: 源码实现逻辑 user : 业务系统2.springboot工程项目构建 1. pom依赖如下 <dependencies>…...
报错Uncaught (in promise) Error: Manifest request to...
在使用nuxt框架时,出现如下报错: 解决方案: 不要打开两个以上的开发者工具更换nuxt的端口号 参考资料:https://github.com/nuxt/nuxt.js/issues/6202...
微信私域更好玩了
之前分享过,“小绿书”“公众号文章转音频”等内测中或悄悄已升级的功能。 其实,微信还在内测很多新功能,只是没公开 今天,小编又发现新升级 就是『附近』功能 增加了一个本地生活的入口,这里面是短视频和图文 展示…...
基于ant-design的a-modal自定义vue拖拽指令
写一个dragDialog.js 在main.js中引入 import ‘./core/directives/dragDialog.js’ // 让 dialog 支持鼠标拖动 import Vue from vueVue.directive(DragDialog, {update: function (el, binding, vnode) {if (!binding.value || !binding.value.reset) returnconst dialog e…...
【ES】笔记-模板字符串(template string)是增强版的字符串`${expresions}`
模板字符串 传统的 JavaScript 语言,输出模板通常是这样写的(下面使用了 jQuery 的方法)。 $(#result).append(There are <b> basket.count </b> items in your basket, <em> basket.onSale </em> are on sal…...
利用 OLE 对象漏洞的 HWP 恶意文件浮出水面
ASEC 分析人员发现了一个利用 OLE 对象的恶意 HWP 文件,尽管其使用了 2020 年就被识别的恶意 URL,但仍然使用了 Flash 漏洞(CVE-2018-15982),需要用户谨慎对待。 打开 HWP 文件时会在 %TEMP%文件夹中生成如下文件。攻…...
【CSS】倾斜按钮
效果 index.html <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"/><meta http-equiv"X-UA-Compatible" content"IEedge"/><meta name"viewport" content"widthdevice-…...
js 正则表达式
js 正则表达式 http://tool.oschina.net/regex https://developer.mozilla.org/zh-CN/docs/Web/JavaScript/Guide/Regular_Expressions 11 22...
心理咨询预约管理系统javaweb医院挂号jsp源代码mysql
本项目为前几天收费帮学妹做的一个项目,Java EE JSP项目,在工作环境中基本使用不到,但是很多学校把这个当作编程入门的项目来做,故分享出本项目供初学者参考。 一、项目描述 心理咨询预约管理系统javaweb MVC模式,普…...
Linux中安装Node
安装 先从 官方网站 下载安装包,有时 node 版本太新会导致失败,详见下方的常见问题第2点 cd /home // 创建目录,将下载好的 node 安装包上传到此目录 mkdir Download mkdir /usr/local/lib/node解压 // 解压,前面是文件当前路径…...
爬虫011_元组高级操作_以及字符串的切片操作---python工作笔记030
获取元组的下标对应的值 注意元组是不可以修改值的,只能获取不能修改 但是列表是可以修改值的对吧...
JVM虚拟机篇
JVM组成 面试题1:什么是程序计数器? 面试题2:你能给我详细的介绍Java堆吗? 面试题3:什么是虚拟机栈? 面试题4:垃圾回收是否涉及栈内存? 垃圾回收主要指就是堆内存,当栈帧弹栈以后…...
Flutter 让软键盘不再自动弹起
1、问题说明: 在开发中,经常遇到这种事,一个页面有输入框,点击输入框后,会弹起软键盘,同时输入框会聚焦,手动收起软键盘后,点击另一个按钮前往下一个页面或者显示一个弹窗࿰…...
k8s 自身原理 1
咱们从 pod 一直分享到最近的 Statefulset 资源,到现在好像我们只是知道如何使用 k8s,如何按照 k8s 设计好的规则去应用,去玩 k8s 仔细想想,对于 k8s 自身的内在原理,我们好像还不是很清楚,对于每一个资源…...
在CPU上安装部署chatglm-6b实用经验分享
chatglm-6b很强,很多同学都想自己试一试,但不是每个人都有GPU、高端显卡的环境,大多数同学都是一台普通的笔记本。 笔者这里分享一下在自己的8G内存,intel i3笔记本上安装部署chatglm-6b的实际经验。有很多网站都分享了一些经验&…...
Mermaid系列之FlowChart流程图
一.欢迎来到我的酒馆 介绍mermaid下,Flowchat流程图语法。 目录 一.欢迎来到我的酒馆二.什么是mermiad工具三.在vs code中使用mermaid四.基本语法 二.什么是mermiad工具 2.1 mermaid可以让你使用代码来创建图表和可视化效果。mermaid是一款基于javascript语言的图表…...
分享Java技术下AutojsPro7云控代码
引言 有图有真相,那短视频就更是真相了。下面是三大语言的短视频。 Java源码版云控示例: Java源码版云控示例在线视频 核心技术:各个编程语言的WebSocket技术。 Java:Nettey、Net:Fleck、Python:Tornad…...
黑马机器学习day2
1.1sklearn转换器和估计器 转换器和预估器(estimator) 1.1.1转换器 实例化一个转换器类 Transformer调用fit_transform() 转换器调用有以下几种形式: fit_transformfittransform 1.1.2估计器 在sklearn中,估计器是一…...
rosdep init || rosdep update || 出错?链接失败?换源!
问题简述 本文主要解决rosdep init失败,rosdep update失败的问题。 rosdep init失败和rosdep update失败,最常见的问题就是网络链接失败。有的朋友会说“诶我使用了tz啊”,但是这里的链接失败对time out的要求不低,虽然你使用了…...
流量、日志分析分析
这周主要以做题为主 先找找理论看然后在buuctrf以及nssctf找了题做 了解wireshark Wireshark是一款开源的网络协议分析软件,具有录制和检查网络数据包的功能,可以深入了解网络通信中的传输协议、数据格式以及通信行为。Wireshark可以捕获发送和接收的数…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...
多模态2025:技术路线“神仙打架”,视频生成冲上云霄
文|魏琳华 编|王一粟 一场大会,聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中,汇集了学界、创业公司和大厂等三方的热门选手,关于多模态的集中讨论达到了前所未有的热度。其中,…...
LLM基础1_语言模型如何处理文本
基于GitHub项目:https://github.com/datawhalechina/llms-from-scratch-cn 工具介绍 tiktoken:OpenAI开发的专业"分词器" torch:Facebook开发的强力计算引擎,相当于超级计算器 理解词嵌入:给词语画"…...
pikachu靶场通关笔记22-1 SQL注入05-1-insert注入(报错法)
目录 一、SQL注入 二、insert注入 三、报错型注入 四、updatexml函数 五、源码审计 六、insert渗透实战 1、渗透准备 2、获取数据库名database 3、获取表名table 4、获取列名column 5、获取字段 本系列为通过《pikachu靶场通关笔记》的SQL注入关卡(共10关࿰…...
使用 SymPy 进行向量和矩阵的高级操作
在科学计算和工程领域,向量和矩阵操作是解决问题的核心技能之一。Python 的 SymPy 库提供了强大的符号计算功能,能够高效地处理向量和矩阵的各种操作。本文将深入探讨如何使用 SymPy 进行向量和矩阵的创建、合并以及维度拓展等操作,并通过具体…...
Mobile ALOHA全身模仿学习
一、题目 Mobile ALOHA:通过低成本全身远程操作学习双手移动操作 传统模仿学习(Imitation Learning)缺点:聚焦与桌面操作,缺乏通用任务所需的移动性和灵活性 本论文优点:(1)在ALOHA…...
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据
微软PowerBI考试 PL300-在 Power BI 中清理、转换和加载数据 Power Query 具有大量专门帮助您清理和准备数据以供分析的功能。 您将了解如何简化复杂模型、更改数据类型、重命名对象和透视数据。 您还将了解如何分析列,以便知晓哪些列包含有价值的数据,…...
Go 语言并发编程基础:无缓冲与有缓冲通道
在上一章节中,我们了解了 Channel 的基本用法。本章将重点分析 Go 中通道的两种类型 —— 无缓冲通道与有缓冲通道,它们在并发编程中各具特点和应用场景。 一、通道的基本分类 类型定义形式特点无缓冲通道make(chan T)发送和接收都必须准备好࿰…...
逻辑回归暴力训练预测金融欺诈
简述 「使用逻辑回归暴力预测金融欺诈,并不断增加特征维度持续测试」的做法,体现了一种逐步建模与迭代验证的实验思路,在金融欺诈检测中非常有价值,本文作为一篇回顾性记录了早年间公司给某行做反欺诈预测用到的技术和思路。百度…...
【 java 虚拟机知识 第一篇 】
目录 1.内存模型 1.1.JVM内存模型的介绍 1.2.堆和栈的区别 1.3.栈的存储细节 1.4.堆的部分 1.5.程序计数器的作用 1.6.方法区的内容 1.7.字符串池 1.8.引用类型 1.9.内存泄漏与内存溢出 1.10.会出现内存溢出的结构 1.内存模型 1.1.JVM内存模型的介绍 内存模型主要分…...
