手写springboot启动器, 学习SpringBoot的最佳实践

自己手写的SpringBoot启动器, 是一个学习了解SpringBoot启动逻辑和了解springboot原理的不错的实践Demo. 废话不多说,直接上代码:

项目结构

maven多项目结构, 

myspringboot  自己手写的SpringBoot启动器

service-demo 用来测试SpringBoot启动器的示例项目

项目pom依赖

1. 主项目Maven Pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>cn.tekin</groupId><artifactId>myspringboot-app</artifactId><version>1.0-SNAPSHOT</version></parent><artifactId>myspringboot</artifactId><packaging>jar</packaging><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><spring-version>5.3.29</spring-version><tomcat-version>9.0.80</tomcat-version></properties><dependencies><!-- https://mavenlibs.com/maven/dependency/org.springframework/spring-web --><dependency><groupId>org.springframework</groupId><artifactId>spring-web</artifactId><version>${spring-version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-webmvc</artifactId><version>${spring-version}</version></dependency><dependency><groupId>org.springframework</groupId><artifactId>spring-context</artifactId><version>${spring-version}</version></dependency><!-- https://mavenlibs.com/maven/dependency/org.apache.tomcat.embed/tomcat-embed-core --><dependency><groupId>org.apache.tomcat.embed</groupId><artifactId>tomcat-embed-core</artifactId><version>${tomcat-version}</version></dependency></dependencies><build><plugins><!--  这个插件用于生成可执行jar文件或者war文档 --><plugin><groupId>org.springframework.boot</groupId><artifactId>spring-boot-maven-plugin</artifactId></plugin></plugins></build>
</project>

2. 测试项目Maven Pom

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><parent><groupId>cn.tekin</groupId><artifactId>myspringboot-app</artifactId><version>1.0-SNAPSHOT</version></parent><artifactId>service-demo</artifactId><packaging>jar</packaging><properties><maven.compiler.source>8</maven.compiler.source><maven.compiler.target>8</maven.compiler.target><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding></properties><dependencies><dependency><groupId>cn.tekin</groupId><artifactId>myspringboot</artifactId><version>1.0-SNAPSHOT</version></dependency><!-- https://mavenlibs.com/maven/dependency/org.redisson/redisson --><dependency><groupId>org.redisson</groupId><artifactId>redisson</artifactId><version>3.23.3</version></dependency><dependency><groupId>de.ruedigermoeller</groupId><artifactId>fst</artifactId><version>2.57</version></dependency></dependencies>
</project>

 

手写SpringBoot启动器项目核心代码

自定义的SpringBootApplication启动类

package cn.tekin;import cn.tekin.webserver.MyWebServer;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.context.support.AnnotationConfigWebApplicationContext;import java.util.Map;/*** 自定义的SpringBootApplication启动类*/
public class MySpringbootApplication {public static void run(Class clazz, String[] args) {// 创建一个Spring容器AnnotationConfigWebApplicationContext applicationContext= new AnnotationConfigWebApplicationContext();// 注册启动类applicationContext.register(clazz);// 刷新容器applicationContext.refresh();// 从spring容器中获取WebServer bean 这样就可以解耦 避免if else判断要使用那个WebServer了MyWebServer webserver = getWebServer(applicationContext);webserver.start(applicationContext);}/*** 这里通过使用上下文中的 getBeansOfType 方法,通过将接口类来获取容器中的所有实现类,从而达到解耦的目的.* @param applicationContext* @return*/private static MyWebServer getWebServer(WebApplicationContext applicationContext) {// 从spring容器中获取WebServer Bean对象Map<String, MyWebServer> webservers = applicationContext.getBeansOfType(MyWebServer.class);if (webservers.isEmpty()) {throw new NullPointerException();}// 返回map中的第一个对象; 如果有多个Bean, 这里只返回第一个return webservers.values().stream().findFirst().get();}}

自定义的SpringBoot注解 @MySpringbootApp

package cn.tekin.anno;import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;/*** 自定义的Spingboot注解** 注意:这里@ComponentScan的没有指定扫描的基础包, spring默认会扫描run参数中传递的类所在的包下面的所有类*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Configuration
@ComponentScan(basePackages = {"cn.tekin.config","ws.yunnan.demo"})
public @interface MySpringbootApp {}

自定义的webserver服务自动配置类 MyWebServerAutoConfiguration

package cn.tekin.config;import cn.tekin.condition.MyJettyCondition;
import cn.tekin.webserver.MyJettyWebServer;
import cn.tekin.condition.MyTomcatCondition;
import cn.tekin.webserver.MyTomcatWebServer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Conditional;
import org.springframework.context.annotation.Configuration;@Configuration
public class MyWebServerAutoConfiguration implements MyAutoConfiguration {@Bean@Conditional(MyTomcatCondition.class)public MyTomcatWebServer tomcatWebServer() {return new MyTomcatWebServer();}@Bean@Conditional(MyJettyCondition.class)public MyJettyWebServer jettyWebServer() {return new MyJettyWebServer();}}

自定义的Tomcat服务启动类 MyTomcatWebServer

package cn.tekin.webserver;import org.apache.catalina.*;
import org.apache.catalina.connector.Connector;
import org.apache.catalina.core.StandardContext;
import org.apache.catalina.core.StandardEngine;
import org.apache.catalina.core.StandardHost;
import org.apache.catalina.startup.Tomcat;
import org.springframework.web.context.WebApplicationContext;
import org.springframework.web.servlet.DispatcherServlet;public class MyTomcatWebServer implements MyWebServer {@Overridepublic void start(WebApplicationContext applicationContext) {Tomcat tomcat = new Tomcat();Server server = tomcat.getServer();Service service = server.findService("Tomcat");Connector connector = new Connector();connector.setPort(8089);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(applicationContext));context.addServletMappingDecoded("/*", "dispatcher");try {tomcat.start();} catch (LifecycleException e) {e.printStackTrace();}System.out.println("Tomcat Started");}
}

@Conditional条件注解 条件判断类 MyTomcatCondition

package cn.tekin.condition;import org.springframework.context.annotation.Condition;
import org.springframework.context.annotation.ConditionContext;
import org.springframework.core.type.AnnotatedTypeMetadata;public class MyTomcatCondition implements Condition {@Overridepublic boolean matches(ConditionContext conditionContext, AnnotatedTypeMetadata annotatedTypeMetadata) {try {// 通过从上下文中 加载Tomcat的核心类来判断pom中是否添加了Tomcat依赖conditionContext.getClassLoader().loadClass("org.apache.catalina.startup.Tomcat");return true;} catch (ClassNotFoundException e) {return false;}}
}

service-demo MySpringBootApp服务测试代码

package ws.yunnan.demo;import cn.tekin.MySpringbootApplication;
import cn.tekin.anno.MySpringbootApp;@MySpringbootApp
public class MyApplication {public static void main(String[] args) {// 这里的MyApplication.class 就是传入Spring容器的配置类, 一般是使用的当前的类, 也可以是其他的类MySpringbootApplication.run(MyApplication.class, args);}}

完整项目代码见

Gitee码云
https://gitee.com/tekintian/myspringboot-app

Github
https://github.com/tekintian/myspringboot-app

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/769957.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python、execl数据分析(数据描述)

一 python 1.各函数 1.1python库的安装与导入 #pip install os#pip install matplotlib#pip install seaborn#pip install scikit-learn#pip install scipy#修 改 工 作 目 录import osos.getcwd () # 查看当前工作环境os.chdir( F :\my course\database ) # 修改工作环境o…

fifo ip核 ————读写时钟同步

1.原理 timescale 1ns/1ns module tb_fifo();reg sys_clk ; reg sys_rst_n ; reg [7:0] pi_data ; reg rd_req ; reg wr_req ; reg [2:0] cnt;wire empty ; wire full ; wire [7:0] po_data ; wire [7:0] usedw ;initial begins…

ThreadPoolExecutor @since 1.5 @author Doug Lea

ThreadPoolExecutor 类的设计思路和目的主要是为了提供一种高效、 灵活且可控的方式来管理和复用线程资源&#xff0c; 以便更好地处理并发任务。以下是源码中的注释说明&#xff1a; An ExecutorService that executes each submitted task using one of possibly several poo…

力扣HOT100 - 283. 移动零

解题思路&#xff1a; 双指针 指针 i 用于寻找不为零的位置 指针 j 用于寻找为零的位置 不为零时&#xff0c;自己与自己交换&#xff0c;i 和 j 同时向下一个位置移动 为零时&#xff0c;nums[ i ]与nums[ j ]交换&#xff0c;使零向后移动 class Solution {public void…

总结虚函数表机制——c++多态底层原理

前言&#xff1a; 前几天学了多态。 然后过去几天一直在测试多态的底层与机制。今天将多态的机制以及它的本质分享给受多态性质困扰的友友们。 本节内容只涉及多态的原理&#xff0c; 也就是那张虚表的规则&#xff0c;有点偏向底层。 本节不谈语法&#xff01;不谈语法&#x…

Qt——智能指针实战

目录 前言正文一、理论介绍1、QPointer2、QScopedPoint3、QSharedPoint4、QWeakPoint 二、实战演练1、QPoint2、QScopedPoint3、QSharedPointa、示例一b、示例二 4、QWeakPoint END、总结的知识与问题 参考 前言 智能指针的使用&#xff0c;对很多程序员来说&#xff0c;都算是…

发布文章积分自动增加

controller ApiOperation(value "添加文章")PostMapping("/addwengzhang")public String addwengzhang(RequestBody WengDto wengDto) {if (wengDto.getContent() null || wengDto.getTitle() null) {return "参数不可为空";}User user user…

【MySQL】InnoDB引擎

逻辑结构 InnoDB存储引擎逻辑结构如图所示&#xff1a; Tablespace&#xff1a;表空间&#xff0c;一个数据库可以对应多个表空间。数据库中的每张表都有一个表空间&#xff0c;用来存放表记录、索引等数据。 Segment&#xff1a;段&#xff0c;表空间中有多个段&#xff0c…

第九届蓝桥杯大赛个人赛省赛(软件类)真题C 语言 A 组-乘积尾零

solution 找末尾0的个数&#xff0c;即找有多少对2和5 >问题等价于寻找所给数据中&#xff0c;有多少个2和5的因子&#xff0c;较少出现的因子次数即为0的个数 #include <iostream> using namespace std; int main() {// 请在此输入您的代码printf("31");…

Java代码基础算法练习-搬砖问题-2024.03.25

任务描述&#xff1a; m块砖&#xff0c;n人搬&#xff0c;男搬4&#xff0c;女搬3&#xff0c;两个小孩抬一砖&#xff0c;要求一次全搬完&#xff0c;问男、 女、小孩各若干&#xff1f; 任务要求&#xff1a; 代码示例&#xff1a; package M0317_0331;import java.util.S…

3.C++:类与对象(下)

一、再谈构造函数 1.1构造函数体赋值 在创建对象时&#xff0c;编译器通过调用构造函数&#xff0c;给对象中各个成员变量一个合适的初始值。 class Date { public:Date(int year, int month, int day){_year year;_month month;_day day;}private:int _year;int _month;i…

== 和 equals 的区别是什么?

和 equals() 在 Java 中都是用于比较两个对象&#xff0c;但它们之间存在显著的差异&#xff1a; 比较的内容&#xff1a; &#xff1a;这是 Java 中的基本比较运算符&#xff0c;对于基本数据类型&#xff08;如 int, char, double 等&#xff09;&#xff0c;它比较的是值&a…

二手车交易网站|基于JSP技术+ Mysql+Java+ B/S结构的二手车交易网站设计与实现(可运行源码+数据库+设计文档)

推荐阅读100套最新项目 最新ssmjava项目文档视频演示可运行源码分享 最新jspjava项目文档视频演示可运行源码分享 最新Spring Boot项目文档视频演示可运行源码分享 2024年56套包含java&#xff0c;ssm&#xff0c;springboot的平台设计与实现项目系统开发资源&#xff08;可…

将若依项目部署上线

1. 购买轻量服务器&#xff0c;新人优惠一年61元&#xff08;有点赚&#xff09;&#xff1b; 2. 在轻量服务器重置密码&#xff0c;再远程连接。 3. 登录宝塔面板&#xff1b; 4. 下载mysql5.7&#xff0c;redis7.2&#xff0c;nginx 5. 在宝塔页面设置数据库密码&#xf…

快速熟悉ElasticSearch的基本概念

1.全文检索 全文检索是通过文本内容进行全面搜索的技术。通过全文检索可以快速地在大量文本数据中查找包含特定关键词或者短语的文档&#xff0c;并且返回相关的搜索结果。 检索和查询的区别 检索没有搜索条件边界&#xff0c;检索的结果取决于相关性&#xff0c;相关性计算…

VUE:内置组件<Teleport>妙用

一、<Teleport>简介 <Teleport>能将其插槽内容渲染到 DOM 中的另一个位置。也就是移动这个dom。 我们可以这么使用它: 将class为boxB的盒子移动到class为boxA的容器中。 <Teleport to".boxA"><div class"boxB"></div> &…

OC高级编程 第3章:Grand Central Dispatch

3.1 Grand Central Dispatch (GCD)概要 3.1.1什么是GCD Grand Central Dispatch&#xff08;GCD&#xff09;是异步执行任务的技术之一。一般将应用中记述线程管理用的代码在系统级中实现。开发者只要定义想执行的任务并追加到Dispatch Queue中&#xff0c;GCD就能生成必要的…

如何使用Docker安装Paperless-ngx系统并实现远程在线搜索查阅文档

文章目录 1. 部署Paperless-ngx2. 本地访问Paperless-ngx3. Linux安装Cpolar4. 配置公网地址5. 远程访问6. 固定Cpolar公网地址7. 固定地址访问 Paperless-ngx是一个开源的文档管理系统&#xff0c;可以将物理文档转换成可搜索的在线档案&#xff0c;从而减少纸张的使用。它内置…

登录注册界面

T1、编程设计理工超市功能菜单并完成注册和登录功能的实现。 显示完菜单后&#xff0c;提示用户输入菜单项序号。当用户输入<注册>和<登录>菜单序号时模拟完成注册和登录功能&#xff0c;最后提示注册/登录成功并显示注册信息/欢迎XXX登录。当用户输入其他菜…

Docker操作基础命令

注意&#xff1a;以下命令在特权模式下进行会更有效&#xff01; 进入特权模式 sudo -ssudo su拉取镜像 sudo docker pull [镜像名] # sudo docker pull baiduxlab/sgx-rust:2004-1.1.3进入容器 端口开启服务&#xff1a; sudo docker start 3df9bf5dbd0c进入容器&#xf…