手写springboot

news2024/10/6 20:27:24

前言

 首先确定springboot在spring基础上主要做了哪些改动:
  1. 内嵌tomcat
  2. 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一个类和注解:

  1. @SpringBootApplication,这个注解是加在应用启动类上的,也就是main方法所在的类
  2. 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内嵌要求如下:

  1. 如果项目中有Tomcat的依赖,那就启动Tomcat
  2. 如果项目中有Jetty的依赖就启动Jetty
  3. 如果两者都没有则报错
  4. 如果两者都有也报错

首先定义服务接口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");

    }

    @Override
    public void start() {
        try {
            System.out.println("tomcat start......");
            tomcat.start();
        }catch (Exception e){
            e.printStackTrace();
        }

    }
}

jetty服务实现(具体实现逻辑没写,具体实现逻辑类似tomcat实现)

public class JettyWebServer implements WebServer{
    @Override
    public void start() {
        System.out.println("jetty  start......");
    }
}

思考:jetty\tomcat都已实现,总不能用if/else这样决定用哪个服务扩展性太差,基于此,就联想到spring的Condition条件注解和参考springboot中的OnClassCondition这个注解。

具体实现如下:

public class KcOnClassCondition implements Condition {
    @Override
    public 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 {
    @Override
    public 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 {

    @Autowired
    private UserService userService;

    @GetMapping("test")
    public String test() {
        return userService.test();
    }
}
@Service
public class UserService {

    public String test() {
        return "hello  springboot";
    }
}

4、启动测试访问

在这里插入图片描述

三、项目地址

git地址

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

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

相关文章

vue基于java的高校就业管理系统的设计和实现f0c2k

相比于传统的高校就业管理方式&#xff0c;智能化的管理方式可以大幅提高学生的就业率&#xff0c;实现了高校就业管理的标准化、制度化、程序化的管理&#xff0c;有效地防止了高校就业的随意管理&#xff0c;提高了信息的处理速度和精确度&#xff0c;能够及时、准确地查询和…

生命科学组织使用 OpenText ETX 随时随地为医疗保健和生命科学人员提供相关信息

生命科学组织使用 OpenText ETX 随时随地为医疗保健和生命科学人员提供相关信息 引领生命科学远程工作的IT新高度 生命科学组织一直承受着改进工作流程、降低成本和比以往更快地交付产品的压力。 使用远程访问和集中式 IT 基础架构&#xff0c;企业可以在加快上市时间方面取得…

opencv基础45-图像金字塔01-高斯金字塔cv2.pyrDown()

什么是图像金字塔&#xff1f; 图像金字塔&#xff08;Image> Pyramid&#xff09;是一种用于多尺度图像处理和分析的技术&#xff0c;它通过构建一系列不同分辨率的图像&#xff0c;从而使得图像可以在不同尺度下进行处理和分析。图像金字塔在计算机视觉、图像处理和计算机…

就地程控站控制柜与斗轮机之间无线通讯

一、应用背景 马钢的前身是成立于1953年的马鞍山铁厂&#xff0c;2019年马钢集团正式成为中国宝武控股子公司。马钢产品以建筑用型线材为主&#xff0c;满足重型工业厂房、轻钢结构、高层建筑、桥梁结构、工业管道等构件的加工需要。目前马钢在岗员工4.8万人&#xff0c;具备了…

三菱PLC与变频器通讯-ModbusRTU协议

Modbus是Modicon公司为其PLC与主机之间的通讯而发明的串行通讯协议。其物理层采用RS232、485等异步串行标准。由于其开放性而被大量的PLC及RTU厂家采用。Modbus通讯方式采用主从方式的查询&#xff0d;相应机制&#xff0c;只有主站发出查询时&#xff0c;从站才能给出响应&…

【快应用】list组件属性的运用指导

【关键词】 list、瀑布流、刷新、页面布局 【问题背景】 1、 页面部分内容需要瀑布流格式展示&#xff0c;在使用lsit列表组件设置columns进行多列渲染时&#xff0c;此时在里面加入刷新动画时&#xff0c;动画只占了list组件的一列&#xff0c;并没有完全占据一行宽度&…

Node.js新手在哪儿找小项目练手?

前言 可以参考一下下面的nodejs相关的项目&#xff0c;希望对你的学习有所帮助&#xff0c;废话少说&#xff0c;让我们直接进入正题>> 1、 NodeBB Star: 13.3k 一个基于Node.js的现代化社区论坛软件&#xff0c;具有快速、可扩展、易于使用和灵活的特点。它支持多种数…

数字孪生技术:建筑环境的智能化革命

在数字化时代&#xff0c;科技的飞速发展正在深刻影响着建筑行业。数字孪生技术作为数字化转型的重要组成部分&#xff0c;正在为建筑环境带来革命性的变革。数字孪生技术通过将现实世界与虚拟模型实时同步&#xff0c;为建筑规划、施工、运营和维护等各个环节提供了更智能、高…

准备三个月,终拿快手offer!薪资28k*16

昨天有VIP小伙伴给小孟说&#xff1a;拿到了快手的offer。 聊了半个小时&#xff0c;待遇还不错。准备去了&#xff01;28k&#xff0c;16薪。 快手的k3c职级可对标阿里的P7。 前面我说过&#xff1a;能去大厂就去大厂&#xff0c;有机会就去争取&#xff0c;年纪轻轻的&a…

电脑怎么备份文件?学会这4招!

“由于我是个比较粗心的人&#xff0c;平常很容易把一些文件弄不见。因此&#xff0c;我想把电脑中一些比较重要的文件进行备份&#xff0c;有没有朋友知道电脑怎么备份文件呢&#xff1f;求解答&#xff01;” 在使用电脑时&#xff0c;可能经常会由于各种突然原因导致我们的文…

RDMA服务类型(二)

参考IB协议版本V1.4&#xff1a;https://download.csdn.net/download/zz2633105/88148107 参考知乎文章《RDMA基本服务类型》&#xff1a;https://zhuanlan.zhihu.com/p/144099636 可靠服务 何为可靠服务呢&#xff0c;引用IB协议中的原话&#xff08;IB V1.4版本9.7章节&am…

专业服务管理软件推荐:提升效率的强大工具

借助Zoho Projects的多功能专业服务管理平台&#xff0c;将服务交付提升到一个新的水平。 一、提高利润并满足每一位客户 Zoho Projects的多功能专业服务管理系统使您和您的团队能够更好地管理您的所有工作&#xff0c;以提高客户满意度。优化沟通、与同事协作并监控项目的每个…

Kendo UI for jQuery,一个现代的jQuery UI组件!

Kendo UI for jQuery是什么&#xff1f; Kendo UI for jQuery是完整的jQuery UI组件库&#xff0c;可快速构建出色的高性能响应式Web应用程序。Kendo UI for jQuery提供在短时间内构建现代Web应用程序所需要的工具&#xff0c;从多个UI组件中选择&#xff0c;并轻松地将它们组…

亿发江西中小型制造企业信息化建设解决方案,2023数字化转型升级

实体经济在经济中的重要性愈发凸显&#xff0c;江西省作为制造业强省&#xff0c;要实现制造业经济高质量发展&#xff0c;信息技术与制造业的深度汇合是不可或缺的关键路径。在这个制造业转型升级的浪潮中&#xff0c;中小企业成为了江西省制造业转型的焦点。让我们深入探讨一…

C++学习笔记总结练习:正则表达式

正则表达式 目录 regex正则表达式的定义regex_match/search/replace正则表达式的三个操作smatch、sregex_iterator正则表达式的结果对象以及与其配套的迭代器。 参考文献 正则表达式总结 1 正则表达式基础 不考虑子表达式的内容 头文件 #include<regex>库组件 库组件作…

Kubernetes kubectl管理命令使用方法

陈述式资源管理方法&#xff08;通过命令行&#xff09; 1.kubernetes 集群管理集群资源的唯一入口是通过相应的方法调用 apiserver 的接口 2.kubectl 是官方的CLI命令行工具&#xff0c;用于与 apiserver 进行通信&#xff0c;将用户在命令行输入的命令&#xff0c;组织并转化…

开发者如何预防及解决应用内的无效流量问题?

对开发者而言&#xff0c;当APP应用内被发现存在大量的无效流量时&#xff0c;存在被广告平台限流甚至封号的风险。 广告平台的“无效流量”政策是为了保护广告主、媒体、用户免受欺诈和不良广告体验的侵害&#xff0c;开发者如何预防和解决无效流量的问题呢&#xff1f; 一、…

动力节点|Docker教程,入门到高阶即学即用

Docker是当今云计算领域最热门的技术之一&#xff0c;也是IT从业人员的必备技能之一 学习Docker可以让你更好地了解和应用容器化技术&#xff0c;实现软件运环境的快速部署和管理。 如何系统全面掌握Docker&#xff1f; 推荐一套天花板级Docker教程&#xff0c;动力节点的动力哥…

C语言笔试训练【第五天】

文章目录 1、如下程序的功能是&#xff08; &#xff09; 2、对于代码段&#xff0c;下面描述正确的是&#xff08; &#xff09; 3、以下程序运行时&#xff0c;若输入 1abcedf2df<回车> 输出结果是&#xff08; &#xff09; 4、下列条件语句中&#xff0c;功能与其…

成功解决Android设备adb连接后显示device unauthorized

一、提出问题 在电脑通过USB连接新的Android设备&#xff0c;想要通过adb来进行一些操作时&#xff0c;却发现命令提示符上在输入下面命令后显示设备未授权的信息也就是"unauthorized" adb devices二、不可行的解决方案 有人提出的解决方案是打开Android设备的开发…