【SpringBoot】手写模拟SpringBoot核心流程
- 互联网
- 2025-08-13 07:48:02

依赖包
新建一个工程,包含两个 module:
springboot 模块,表示 springboot 源码实现;user 模块,表示业务系统,使用 springboot 模块;
依赖包:Spring、SpringMVC、Tomcat 等,引入依赖如下:
<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 模块下引入依赖:
<dependencies> <dependency> <groupId>org.example</groupId> <artifactId>springboot</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies>定义对应的 controller 和 service:
@RestController public class UserController { @Autowired private UserService userService; @GetMapping("test") public String test(){ return userService.test(); } }最终希望通过启动 MyApplication 的 main 方法,启动项目,能访问到 UserController。
核心注解和核心类SpringBoot 的核心类和注解:
@SpringBootApplication,这个注解是加在应用启动类上的,也就是 main 方法所在的类;SpringApplication,这个类中有个 run() 方法,用来启动 SpringBoot 应用的;
所以,自定义类和注解以实现上面的功能。@FireSpringBootApplication 注解:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Configuration @ComponentScan public @interface FireSpringBootApplication { }FireSpringApplication 启动类:
public class FireSpringApplication { public static void run(Class clazz){ } }在 MyApplication 中使用:
@FireSpringBootApplication public class MyApplication { public static void main(String[] args) { FireSpringApplication.run(MyApplication.class); } } run 方法需要在 run 方法中启动 tomcat,通过 tomcat 接收请求;DispatchServlet 绑定 spring 容器,DispatchServlet 接收到请求后需要在 spring 容器中找到一个 controller 中对应的方法;
run 方法中需要实现的逻辑:
创建一个 Spring 容器创建 Tomcat 对象生成 DispatcherServlet 对象,并且和前面创建出来的 Spring 容器进行绑定将 DispatcherServlet 添加到 Tomcat 中启动 Tomcat 创建 Spring 容器 public class FireSpringApplication { public static void run(Class clazz){ AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext(); applicationContext.register(clazz); applicationContext.refresh(); } }run 方法中传入的即使 MyApplication 类,被解析为 Spring 容器的配置类;默认会将 MyApplication 所在的包作为扫描路径,从而扫描到 UserController 和 UserService,所以在 spring 容器启动后就会存在两个 bean 了;
启动 Tomcat使用内嵌的 Tomact,即 Embed-Tomcat,启动代码如下:
public 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); // 添加DispatcherServlet,并且绑定一个Spring容器 tomcat.addServlet(contextPath, "dispatcher", new DispatcherServlet(applicationContext)); // 设置Mapping关系 context.addServletMappingDecoded("/*", "dispatcher"); try { tomcat.start(); } catch (LifecycleException e) { e.printStackTrace(); } }在 run 方法中调用 startTomcat 方法启动 tomcat:
public static void run(Class clazz){ AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext(); applicationContext.register(clazz); applicationContext.refresh(); // 启动tomcat startTomcat(applicationContext); }到此,一个简单的 SpringBoot 就写出来了,运行 MyApplication 正常启动项目,通过浏览器就可以访问 UserController 了。
实现 Tomcat 和 Jetty 的切换前面代码中默认启动的是 Tomcat,现在想改成这样子:
如果项目中有 Tomcat 的依赖,那就启动 Tomcat如果项目中有 Jetty的依赖就启动 Jetty如果两者都没有则报错如果两者都有也报错这个逻辑希望 SpringBoot 自动实现,对于程序员用户而言,只要在 Pom 文件中添加相关依赖就可以了,想用 Tomcat 就加 Tomcat 依赖,想用 Jetty 就加 Jetty 依赖。Tomcat 和 Jetty 都是应用服务器,或者是 Servlet 容器,可以定义接口来表示它们,这个接口交 WebServer(SpringBoot 源码中也叫这个)。定义接口如下:
public interface WebServer { public void start(); }Tomcat 实现类:
public class TomcatWebServer implements WebServer{ @Override public void start() { System.out.println("启动Tomcat"); } }Jetty 实现类:
public class JettyWebServer implements WebServer{ @Override public void start() { System.out.println("启动Jetty"); } }在 FireSpringApplication 中的 run 方法中,去获取对应的 WebServer,然后启动对应的 webServer。代码如下:
public static void run(Class clazz){ AnnotationConfigWebApplicationContext applicationContext = new AnnotationConfigWebApplicationContext(); applicationContext.register(clazz); applicationContext.refresh(); // 自动获取配置的Tomcat或者Jetty容器 WebServer webServer = getWebServer(applicationContext); webServer.start(); } public static WebServer getWebServer(ApplicationContext applicationContext){ return null; } 模拟实现条件注解首先实现一个条件注解@FireConditionalOnClass,对应代码如下:
@Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Conditional(FireOnClassCondition.class) public @interface FireConditionalOnClass { String value() default ""; }注意核心为@Conditional(FireOnClassCondition.class)中的 FireOnClassCondition,因为它才是真正得条件逻辑:
public class FireOnClassCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { Map<String, Object> annotationAttributes = metadata.getAnnotationAttributes(FireConditionalOnClass.class.getName()); String className = (String) annotationAttributes.get("value"); try { context.getClassLoader().loadClass(className); return true; } catch (ClassNotFoundException e) { return false; } } }具体逻辑为,拿到@FireConditionalOnClass中的 value 属性,然后用类加载器进行加载,如果加载到了所指定的这个类,那就表示符合条件,如果加载不到,则表示不符合条件。
模拟实现自动配置类配置类代码如下:
@Configuration public class WebServiceAutoConfiguration { @Bean @FireConditionalOnClass("org.apache.catalina.startup.Tomcat") public TomcatWebServer tomcatWebServer(){ return new TomcatWebServer(); } @Bean @FireConditionalOnClass("org.eclipse.jetty.server.Server") public JettyWebServer jettyWebServer(){ return new JettyWebServer(); } }表示org.apache.catalina.startup.Tomcat存在,则有 tomcatWebServer 这个bean;表示org.eclipse.jetty.server.Server存在,则有 jettyWebServer 这个bean;
FireSpringApplication#getWebServer()方法实现:
public static WebServer getWebServer(ApplicationContext applicationContext){ // key为beanName, value为Bean对象 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 启动逻辑就是这样的:
创建一个 AnnotationConfigWebApplicationContext 容器解析 MyApplication 类,然后进行扫描通过 getWebServer 方法从 Spring 容器中获取 WebServer 类型的 Bean调用 WebServer 对象的 start 方法 发现自动配置类WebServiceAutoConfiguration 需要被 SpringBoot 发现,可以通过 SPI 机制实现,比较 JDK 自带的 SPI 来实现。
在 springboot 项目中的 resources 目录下添加目录META-INF/services和文件 org.example.springboot.AutoConfiguration,文件内容为org.example.springboot.WebServiceAutoConfiguration。
接口:
public interface AutoConfiguration { }WebServiceAutoConfiguration 实现该接口:
@Configuration public class WebServiceAutoConfiguration implements AutoConfiguration { @Bean @FireConditionalOnClass("org.apache.catalina.startup.Tomcat") public TomcatWebServer tomcatWebServer(){ return new TomcatWebServer(); } @Bean @FireConditionalOnClass("org.eclipse.jetty.server.Server") public JettyWebServer jettyWebServer(){ return new JettyWebServer(); } }再利用 spring 中的@Import技术来导入这些配置类,我们在@FireSpringBootApplication的定义上增加如下代码:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Configuration @ComponentScan @Import(FireImportSelect.class) public @interface FireSpringBootApplication { }FireImportSelect:
public class FireImportSelect implements DeferredImportSelector { @Override public String[] selectImports(AnnotationMetadata importingClassMetadata) { ServiceLoader<AutoConfiguration> serviceLoader = ServiceLoader.load(AutoConfiguration.class); List<String> list = new ArrayList<>(); for (AutoConfiguration autoConfiguration : serviceLoader) { list.add(autoConfiguration.getClass().getName()); } return list.toArray(new String[0]); } }如此,Spring 容器可以装载 WebServiceAutoConfiguration 配置类了,对于 user 模块而言,不需要修改代码就可以自动识别 Tomcat 和 Jetty 了。
总结到此,实现了一个简单版本的 SpringBoot,因为 SpringBoot 首先是基于 Spring 的,而且提供的功能也更加强大,后面会对这些功能进行更深入的剖析。
【SpringBoot】手写模拟SpringBoot核心流程由讯客互联互联网栏目发布,感谢您对讯客互联的认可,以及对我们原创作品以及文章的青睐,非常欢迎各位朋友分享到个人网站或者朋友圈,但转载请说明文章出处“【SpringBoot】手写模拟SpringBoot核心流程”
上一篇
k8s存储