如何手写一个SpringBoot框架
你好,我是柳岸花开。
在这篇文章中,我们将手写模拟SpringBoot的核心流程,让大家能够以一种简单的方式了解SpringBoot的大概工作原理。
项目结构
我们创建一个工程,包含两个模块:
-
springboot模块,表示SpringBoot框架的源码实现。 -
user包,表示用户业务系统,用来写业务代码来测试我们所模拟出来的SpringBoot。
首先,在springboot模块中添加以下依赖:
<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>
在user模块下,我们进行正常的开发,比如先添加SpringBoot依赖,然后定义相关的Controller和Service:
<dependencies>
<dependency>
<groupId>org.example</groupId>
<artifactId>springboot</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
</dependencies>
定义UserController和UserService
在user包下定义UserController和UserService,希望能通过运行MyApplication中的main方法,直接启动项目,并能在浏览器中正常访问到UserController中的某个方法。
@RestController
public class UserController {
@Autowired
private UserService userService;
@GetMapping("test")
public String test(){
return userService.test();
}
}
核心注解和核心类:
-
@SpringBootApplication:这个注解加在应用启动类上,即main方法所在的类。 -
SpringApplication:这个类中有个run()方法,用来启动SpringBoot应用。
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Configuration
@ComponentScan
public @interface BobSpringBootApplication {
}
public class BobSpringApplication {
public static void run(Class clazz){
AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext();
applicationContext.register(clazz);
applicationContext.refresh();
startTomcat(applicationContext);
}
private static void startTomcat(WebApplicationContext applicationContext) {
Tomcat 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(context, "dispatcher", new DispatcherServlet(applicationContext));
context.addServletMappingDecoded("/", "dispatcher");
try {
tomcat.start();
} catch (LifecycleException e) {
e.printStackTrace();
}
tomcat.getServer().await();
}
}
@BobSpringBootApplication
public class MyApplication {
public static void main(String[] args) {
BobSpringApplication.run(MyApplication.class);
}
}
实现Tomcat和Jetty的切换
如果项目中有Tomcat的依赖,那就启动Tomcat;如果有Jetty的依赖就启动Jetty;如果两者都没有则报错;如果两者都有也报错。
public interface WebServer {
void start();
}
public class TomcatWebServer implements WebServer {
@Override
public void start() {
System.out.println("启动Tomcat");
}
}
public class JettyWebServer implements WebServer {
@Override
public void start() {
System.out.println("启动Jetty");
}
}
模拟实现条件注解:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Conditional(BobOnClassCondition.class)
public @interface BobConditionalOnClass {
String value() default "";
}
public class BobOnClassCondition implements Condition {
@Override
public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(BobConditionalOnClass.class.getName());
String className = (String) annotationAttributes.get("value");
try {
context.getClassLoader().loadClass(className);
return true;
} catch (ClassNotFoundException e) {
return false;
}
}
}
模拟实现自动配置类:
@Configuration
public class WebServiceAutoConfiguration {
@Bean
@BobConditionalOnClass("org.apache.catalina.startup.Tomcat")
public TomcatWebServer tomcatWebServer() {
return new TomcatWebServer();
}
@Bean
@BobConditionalOnClass("org.eclipse.jetty.server.Server")
public JettyWebServer jettyWebServer() {
return new JettyWebServer();
}
}
在BobSpringApplication中添加获取WebServer的方法:
public static WebServer getWebServer(ApplicationContext applicationContext) {
Map<String, WebServer> webServers = applicationContext.getBeansOfType(WebServer.class);
if (webServers.isEmpty()) {
throw new NullPointerException();
}
if (webServers.size() > 1) {
throw new IllegalStateException();
}
return webServers.values().stream().findFirst().get();
}
通过以上步骤,我们实现了一个简单的SpringBoot,并且可以根据依赖切换Tomcat和Jetty。
👇关注我,下期了解👇 SpringBoot自动配置
回复 222,获取Java面试题合集 关于我 一枚爱折腾的Java程序猿,专注Spring干货。把路上的问题记录下来,帮助那些和我一样的人。 好奇心强,喜欢并深入研究古天文。 崇尚 个人系统创建,做一些时间越长越有价值的事情。思考 把时间留下来 又 每刻都是新的。
本文由 mdnice 多平台发布
相关文章:
如何手写一个SpringBoot框架
你好,我是柳岸花开。 在这篇文章中,我们将手写模拟SpringBoot的核心流程,让大家能够以一种简单的方式了解SpringBoot的大概工作原理。 项目结构 我们创建一个工程,包含两个模块: springboot模块,表示Spring…...
vite解决前端跨域步骤
Vite 解决跨域问题的原理主要是通过其内置的开发服务器功能实现的,具体来说,是通过 HTTP 代理(HTTP Proxy)机制。在开发环境中,Vite 服务器可以配置为一个代理服务器,将前端应用发出的请求转发到实际的后端…...
同步交互与异步交互:深入解析与选择
同步交互与异步交互:深入解析与选择 1、同步交互2、异步交互3、选择策略 💖The Begin💖点点关注,收藏不迷路💖 在软件开发的世界里,交互方式主要分为两大类:同步与异步。下面是对这两种方式的解…...
Day1
首先,大概学习了一下用anaconda去创建一个环境(因为Django是有python版本的要求),然后学着去切换环境。 创建环境:conda create -n django_study python3.10 激活环境:conda activate django_study 删除环…...
Introduction to Data Analysis with PySpark
1.DataFrame and RDDs 2.Spark Architecture 3. Data Formats and Data Sources 倘若您觉得我写的好,那么请您动动你的小手粉一下我,你的小小鼓励会带来更大的动力。Thanks....
基于双PI控制器结构的六步逆变器供电无刷直流电机调速simulink仿真
目录 1.课题概述 2.系统仿真结果 3.核心程序与模型 4.系统原理简介 4.1 无刷直流电机(BLDCM)原理 4.2 六步换相逆变器 4.3 双PI控制器设计 5.完整工程文件 1.课题概述 基于双PI控制器结构的六步逆变器供电无刷直流电机调速simulink仿真。双PI控制…...
双向链表的基本操作
#include<iostream> #include<cmath> #include<cstring> using namespace std; typedef long long ll; typedef struct line {int data;struct line *pre;//前指针struct line *next;//后指针 }line,*a; line* init_line(line*head) {cout<<"请输…...
modbus tcp和modbusRTU的区别是什么?
Modbus是一种应用广泛的通信协议,主要用于工业自动化和过程控制系统。Modbus有多种变体,其中Modbus TCP和Modbus RTU是最常见的两种。以下是它们之间的主要区别: 1. 基本定义 Modbus RTU (Remote Terminal Unit): 是基于串行通信的协议&am…...
web小游戏开发:拼图(四)对调和移动拼图玩法的实现
web小游戏开发:拼图(四)对调和移动拼图玩法的实现 对调方式对调模式实现移动方式移动的实现小结对调方式 在完成了原始拼图玩法后,剩下两个玩法其实相对就变得简单的多了。 对调模式,简单来说,就是选中两个图块,然后位置对调一下。 那么,我们来整理一下,看看需要哪…...
前端:Vue学习 - 智慧商城项目
前端:Vue学习 - 智慧商城项目 1. vue组件库 > vant-ui2. postcss插件 > vw 适配3. 路由配置4. 登录页面静态布局4.1 封装axios实例访问验证码接口4.2 vant 组件 > 轻提示4.3 短信验证倒计时4.4 登录功能4.5 响应拦截器 > 统一处理错误4.6 登录权证信息存…...
KVM调整虚拟机与CPU铆钉(绑定)关系
虚拟机铆钉CPU 把虚拟机的vCPU绑定在物理CPU上,即VCPU只在绑定的物理CPU上调度,在特定场景下达到提升虚拟机性能的目的。比如在NUMAQ系统中,把vCPU绑定在同一个NUMA节点上,可以避免CPU跨节点访问内存,避免影响虚拟机运…...
华火电焰灶:烹饪新宠,温暖与美味的完美融合
在众多厨房电器中,华火电焰灶以其独特的魅力和卓越的性能脱颖而出,成为了众多家庭的烹饪新宠。今天,就让我们一同走进华火电焰灶的精彩世界,探索它的非凡之处。 华火电焰灶,首先吸引人的便是其创新的等离子电生明火技术…...
理想发周榜,不是新能源市场的原罪
余华在他的小说《在细雨中呼喊》曾写过这么一段话: “仓廪实而知礼节,衣食足而知荣辱”,在物质需求得到满足以前,精神文明的发展难免会有所滞后。所以,贫穷,不是原罪。 同样的,在如今的新能源…...
AHK是让任何软件都支持 Shift + 鼠标滚轮 实现界面水平滚动
目录 基本介绍 详细特点 图解安装 下载失败?缓慢? 创建并运行脚本代码😃 新建空 xxx.ahk文件 vscode/记事本等编辑工具打开 复制并粘贴简易脚本 运行 其他问题 问题一:弹出无法执行此脚本 关闭脚本 基本介绍 AutoHot…...
如何在C语言中实现求解超级丑数
超级丑数是一个正整数,并且它的质因数只包含在给定的质数列表中。超级丑数的定义类似于丑数,但可以根据特定需求改变质因数的范围。下面是如何在C语言中实现求解超级丑数的代码。 我们使用最小堆(优先队列)来高效地生成超级丑数序…...
secExample靶场之java反序列化漏洞复现
我是使用kali虚拟机搭建的靶场,具体搭建步骤可以看我之前的博客内容。 访问靶机地址,打开java反序列化的 点进去后是如图的页面,随便输入一信息。 可以看到敏感信息直接显示在了页面上。 访问192.168.189.141:8080/cors1,可以看到…...
解决升级Linux内核后,open files设置无效的问题。
问题过程 操作系统是OpenEuler 20.03,内核由4.19.90-2112.8.0.0131.oe1.aarch64升级到kernel-4.19.90-2401.1.0.0233.oe1.aarch64后,重启系统后,重新开起来运行OceanBase就运行不起来了,提示open files must not be less than 20…...
关于防范勒索病毒Play新变种的风险提示
近日,工业信息化部网络安全威胁和漏洞信息共享平台监测发现针对 Linux的勒索病毒Play新变种,攻击对象主要为VMware ESXi 虚拟化环境,攻击目标包括制造、建筑业、IT、金融和房地产等行业。 Play勒索病毒又名 Balloonfly和PlayCrypt࿰…...
一款.NET开源、跨平台的DASH/HLS/MSS下载工具
前言 今天大姚给大家分享一款.NET开源(MIT License)、免费、跨平台的DASH/HLS/MSS下载工具,并且支持点播和直播(DASH/HLS)的内容下载:N_m3u8DL-RE。 网络流媒体传输协议介绍 DASH DASH是一种基于HTTP的…...
MATLAB学习日志DAY21
结构体(2) 如果将生成逗号分隔列表的表达式括在方括号中,MATLAB 会将该列表中的每一项都存储在数组中。示例中,MATLAB 创建一个数值行向量,该向量包含结构体数组 S 的每个元素的 score 字段: scores [S.…...
GitHub 74.2k Star的Redis,开发者必备的内存数据库
文章目录GitHub 74.2k Star的Redis,开发者必备的内存数据库核心能力覆盖多数开发场景实际使用建议GitHub 74.2k Star的Redis,开发者必备的内存数据库 Redis是GitHub上的热门开源项目,Star数达到74223,是很多开发者日常工作中常用…...
STM32L4低功耗实战:用RTC内部唤醒定时1秒,让设备续航翻倍(附CubeIDE配置)
STM32L4低功耗实战:RTC唤醒中断与CubeIDE配置全解析 在电池供电的物联网终端设计中,每微安电流都关乎产品寿命。曾有个智能农业项目,原本预计6个月的传感器续航,因未优化低功耗模式,实际仅维持了3周。这促使我们深入研…...
阵列天线方向图综合算法与应用【附代码】
✨ 长期致力于方向图综合算法、交替投影迭代、交替方向乘子法、子阵方向图综合、相控阵系统、软件设计研究工作,擅长数据搜集与处理、建模仿真、程序编写、仿真设计。 ✅ 专业定制毕设、代码 ✅ 如需沟通交流,点击《获取方式》 (1)…...
边缘AI与TinyML在医疗影像筛查中的实战:从模型轻量化到临床部署
1. 项目概述:当AI成为医生的“仿生眼”在医疗诊断领域,尤其是癌症早期筛查中,人类医生的经验与肉眼观察长期是金标准。然而,这个标准背后隐藏着巨大的不确定性:研究显示,即便是标准的放射影像学检查&#x…...
Claude AI代码扩展工具:在IDE中无缝集成智能编程助手
1. 项目概述:一个为Claude AI设计的代码扩展工具最近在折腾AI编程助手的时候,发现了一个挺有意思的项目——dliedke/ClaudeCodeExtension。这玩意儿说白了,就是一个专门为Claude(就是Anthropic家那个AI)设计的代码扩展…...
基于OpenClaw的MacOS自动化AI助手:架构、配置与实战
1. 项目概述:一个为MacOS设计的自动化AI助手 最近在折腾桌面自动化,特别是想把一些高频、重复的跨应用操作给整合起来。比如,我经常需要在Telegram或WhatsApp上接收消息,然后根据内容去浏览器查资料、整理到笔记软件,或…...
别再只会addItem了!QT QComboBox的5个高级用法与实战场景(含完整代码)
别再只会addItem了!QT QComboBox的5个高级用法与实战场景(含完整代码) 在QT开发中,QComboBox可能是最容易被低估的控件之一。很多开发者仅仅把它当作一个简单的下拉选择框,用addItem()填充几个静态选项就草草了事。但实…...
终极指南:如何使用Azure Quickstart Templates实现成本管理与预算警报
终极指南:如何使用Azure Quickstart Templates实现成本管理与预算警报 【免费下载链接】azure-quickstart-templates Azure Quickstart Templates 项目地址: https://gitcode.com/gh_mirrors/az/azure-quickstart-templates Azure Quickstart Templates是微软…...
C++智能指针详解:原理、使用及避坑指南
文章目录 前言 一、智能指针核心原理:RAII机制 二、C常用智能指针详解(重点掌握后两种) 三、智能指针高频坑点(重中之重) 四、三大智能指针对比(选择指南) 五、实战案例:智能指…...
从SolidWorks到Simulink:手把手教你用Simscape Multibody Link搭建你的第一个虚拟样机
从SolidWorks到Simulink:手把手教你用Simscape Multibody Link搭建你的第一个虚拟样机 虚拟样机技术正在彻底改变传统机电系统的开发流程。想象一下,你刚刚在SolidWorks中完成了一个精巧的自动门闭锁装置的设计,现在不需要花费数周时间加工金…...
