手写模拟springBoot

我们可以模拟写一个springBoot源码。

在springboot项目中有一个启动类,启动类上有一个注解@SpringBootApplication这个注解进行解析。

1.仿写一个springboot工程。

我们自定义一个Java工程来模拟springboot的工程,新建一个SpringApplication类和注解。

然后设置了一个注解叫zhouyuSpringBootApplication,并且再我们

zhouyuSpringBootApplication类设置为启动类,当在其他项目中使用到我们的zhouyu这个启动类时候,调用run方法就是调用的这个类的run方法。

2.在zhouyuSpringBootApplication的run方法中,我们内嵌一个tomcat容器,并设置一些容器的如端口等信息并启动tomcat容器,因为需要内嵌这个容器所以我们这个仿springboot工程中需要导入tomcat的依赖。

此时我们在其他项目中使用仿的springboot时候,调用run方法就能启动容器。

3.在启动tomcat后会引用仿springboot的项目无法正确的识别请求路径及其对应的方法

如这样,它是无法通过/test来执行test方法的。

所以我们还需要在第2步骤在启动tomcat之前来设置一个diespcherServlet,前端请求映射处理控制器。

那么我们的防springboot就能正常启动项目和接收请求了。

4.在步骤3的时候我们发现mvc需要配置一个spring容器,而这个spring容器我们可以在run方法开始的时候也就是tomcat启动之前进行创建,但是这个spring容器需要一个配置类作为配置信息,所以当使用我们仿springboot的注解的时候就需要它的启动类传递到我们run方法内部作为spring容器创建的配置类。

5.在第4步骤中,创建spring容器的时候说需要传递一个类作为配置类,而一般都是传递我们启动类作为配置类,且要解析这个配置类的配置信息,但我们真正的启动类上使用真正的speingbootApplication这个注解,这个启动类上是没有配置其他信息的,那么这个真正的springboot它是如何作为配置类解析它的配置信息呢?难道没有配置信息吗?打开真正的speingbootApplication注解会发现它这个注解上面还有注解,比如commontScan扫描注解。所以在我们仿写speingbootApplication时候也可以写一个commontScan注解,且这个注解默认是扫描传递给run方法的配置类所在的包路径,而不是当前启动类的所在包路径。

6.在以上我们可以完成对请求的处理和响应,但这是不够的。在我们run方法中它内嵌的是一个tomcat容器,那么如果想使用其他容器该怎么办?首先想到是用if判断,若是需要tomcat就启动tomcat,若是相同jetty容器,那就启动jetty容器。

但这种方式不够灵活,若是以后又出新的容器,那么我们仿springboot就只能修改run方法的内容进行修改,那就要使用到策略设计模式。我们定义一个webServer的接口,然后实现类分别为TomcatServer,JettyServer,分别去实现这个WebServer接口,并分别启动自己的容器。

然后再在run方法中抽出一个方法专门用于判断应该启动那个一个server类也就是启动那个容器。

那如何进行判断使用那个容器呢?那就要说到真正的springBootApplication是怎么实现切换容器的,使用真正的springboot时候想要切换容器只需要把tomcat的依赖包注释掉,将jetty容器包导入到pom文件中,保证只有一个容器包就可以实现切换。所以我们首先需要从我们创建的spring容器中拿到用户的pom文件中容器(tomcat,jetty)的对象,然后判断这个对象是否有多个?是否没有?若是对象数量大于1则代表pom文件中的容器依赖有多个,若是没有,则代表pom文件中没有容器依赖。

若是只有一个,那么就启动这个容器。

7.那么对应用户端(程序员)这边来说,它只是修改了pom文件的容器依赖,它并没有把需要容器的依赖给创建成为bean,那又是如何在spring容器中拿到这个bean的。之前说过说,我们创建srping容器时候需要传递我们的启动类作为创建容器的配置类,所以我们需要在启动类上手动加上某一个容器的bean对象。如这样:

在真正的springboot项目中启动类是不需要写这个bean的,那么我们如何解决这个问题?

那么我们在仿springboot中加入一个新的类webServerAutoconfig

在这个类中对所有需要的bean使用@Bean注解来创建到容器中,且又使用到了@ConDitional,这个注解是按需要加载,当达成括号中那个类的条件时候就将当前注解标注的对象创建到容器中。那么在这个注解条件类中它要做的内容就是判断pom文件中是否有tomcat或者jetty依赖包,然后达成条件或未达成条件。

如何判断一个项目中pom文件是否有某一个依赖呢?当pom文件映入一个依赖包后就一定有那个包所对应的类的全路类名,如tomcat全类名,如jetty全类名,再用类加载器加载类的全类名就能判断这个依赖包再pom文件中是否被引入了。

8.在完成7的按需要注入对应的bean后,若是导入一个依赖,然后运行我们仿springboot依然存在问题,会发现我们的仿springboot在启动容器时候会在容器中找不到任何一个容器。这是因为我们写的自动装配类(按需加载bean类)webServerAutoconfig是没有被spring容器给扫描到的。这是因为之前说过spring配置类来创建spring容器而这个配置类所在的包路径就是扫描的路径,又因为我们说过一般配置类是由用户端(程序员)的启动类作为配置类的,但webServerAutoconfig类写在我们的仿springboot工程中,这个webServerAutoconfig的包路径和启动类的包路径是不同的,所以comontScan注解无法扫描到webServerAutoconfig,这就导致了容器无法被加载到spring的bean。

问题找到了,如何使得webServerAutoconfig被扫描到,那么我们只需要在启动类上加上import注解将webServerAutoconfig导入到启动类中,之前说过spring容器会解析这个类上的bean,所以这个webServerAutoconfig就会被解析并放入spring容器中,那么webServerAutoconfig中按需加载的tomcat,jetty容器就会被正常加载了。

这样就能正常按需加载自己对应的容器bean,且也能根据修改pom文件来达到修改容器。

但这样用户端(程序员)就需要自己手动写一行注解@Import注解,我们说过我们仿SpiringBootApplication注解里面的注解是相当于在当前类中的,所以我们只需要修改仿SpiringBootApplication注解就可以,将这一行@Import(WebServcerConfigration.class)定义在仿SpiringBootApplication注解上即可。这样我们用户端(程序员)就可以值使用仿SpiringBootApplication注解即可。

以上就是srpingboot的自动装配基本原理。

9.在完成步骤8后,需要的类就可以直接写一个自动装配类然后再仿SpiringBootApplication注解上写上Impot注解就可以了。但是对于自动装配tomcat和jetty容器来说依然存在问题。我们用户端(程序员)引入自己的pom文件中引入了一个tomcat,但是没有引入jetty依赖然后引入了我们的的仿springboot工程的依赖,理论上说是可以完成按需加载的加载tomcat容器,但是运行会发现仿SpiringBootApplication的run方法中判断是否未多个容器时候判断结果为多个容器,但我们自己只引入了tomcat依赖,这什么原因?原因在于我们要使用仿的springboot所以我们引入了仿springboot依赖,在仿springboot工程中有一个自动装配tomcat,jetty等容器的这个类,想要装配这些bean到spring容器中,我们的仿springboot工程就也需要引入对于tomcat,jetty依赖包。根据依赖传递性来说,我用户端(程序员)自己的项目中虽然只引入了tomcat依赖,但还引入了仿springboot依赖,而仿springboot依赖又引入了tomcat,jetty等其他容器依赖,所以run方法中判断容器数量的时候显示为多个容器。

如何解决这个问题,解决问题的思路是,只需要在我们自己项目中无法访问到仿springboot的容器依赖就行,所以我们在仿springboot项目中的pom文件中所有不需要被客户端(程序员)访问的依赖加上optional,那么其他人引用当前项目就无法引用到这个依赖。

或者在客户端(程序员)项目中引入仿springboot时候,加入排除依赖

10.自动装配详解:之前已经讲解了springboot的自动装配,但是这是仅仅是需要一个配置bean信息的一个类配置类,WebServcerConfigration类,然后使用import注解来导入这个配置类到我们的仿springBootApplication注解上,但是实际项目中肯定有很多个依赖包,没一个依赖包自己的bean都需要创建到spring容器中,所以他们自己的依赖包中都自己写了一个bean的配置类,我们的仿springboot中springBootApplication就要将这些依赖包给都使用@Import注解来解析配置类将bean加入到spring容器中。

那么我们的仿springBootapplcation注解就会变成这样:

当然这里webserverAutoconfigration是指不同依赖提供的不同配置类。

那么这样的情况就会出现问题,我们仿springBootApplcation会非常臃肿,且每次出现一个新的依赖包它的配置类我就需要修改我们仿springBootApplcation注解的源码,这显然是不可取的,所以我们需要一个自动配置的功能。

11.在所有jar中都提供了自己的Autoconfig配置文件,这个配置文件用于spring容器的创建。我们想要把每一个jar包中的配置文件都进行配置,那么有个思路是将所有jar包的配置文件都找到进行配置,但这显然不可能,jar太多,类太多效率非常低。所以每一个jar包都创建了一个自己的文件spring.factories文件,这个文件描述了自己jar包的全部配置类信息,包括它的名称,它全类名。

在我们的SpringFactoryClassLoader类中它加载了全部jar包中的所有spring.factories文件的信息,将所有的配置类的全类名给加载并进行处理:

对于真正的springBoot它的自动装配的原理是一个极其复杂的过程,这是无法模拟出它的真正的原理和思想的,springBoot的自动装配原理有兴趣可自行翻阅源码阅读,感谢观看。

项目地址:luoSpringBoot: 对springBoot框架的模拟

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值