手写SpringBoot(一)之简易版SpringBoot
- 添加依赖
<?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.axj</groupId> <artifactId>spring-boot-base</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>my-spring-boot</artifactId> <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>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-aop</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> </project>
2.启动Spring容器
- 定义MySpringBootApplication标识注解,对应SpringBootApplication
package cn.axj.springboot.my.annnotation; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import java.lang.annotation.Documented; import java.lang.annotation.Retention; import java.lang.annotation.RetentionPolicy; @Documented @Retention(RetentionPolicy.RUNTIME) @Configuration @ComponentScan public @interface MySpringBootApplication { }
Configuration 注解表明该注解标注的类是一个配置类,在AnnotationConfigApplication 里面会解析这个类,并加载相应的容器bean
CompontScan 注解定义Configuration配置类下的扫描路径,如果不传,默认扫描标注类的包名。所以SpringBoot默认会扫描Application类下的当前包以及子包路径。
定义MySpringApplication类,对应SpringApplication
package cn.axj.springboot.my.boot; import cn.axj.springboot.my.web.container.WebContainer; import org.springframework.web.context.support.AnnotationConfigWebApplicationContext; public class MySpringApplication { public static void run(Class<?> clazz,String[] args) { //启动Spring容器 AnnotationConfigWebApplicationContext annotationConfigApplicationContext = new AnnotationConfigWebApplicationContext(); annotationConfigApplicationContext.register(clazz); annotationConfigApplicationContext.refresh(); //启动tomcat容器 WebContainer.startContainer(annotationConfigApplicationContext); } }
register(clazz)通过传入的class对象扫描步骤二中Configuration和ComponentScan定义的路径地址,扫描组件注入到容器中。
这里由于MySpringBootApplication注解是一个组合注解,包括@Configuration和@ComponentScan,Spring在获取到@MySpringBootApplication标注的类,会默认将该类作为配置类,所以这个@MySpringBootApplication不一定非得加到Application类上,可以专门定义一个Configuration类来标注,但是需要将该类Class作为参数传给MySpringApplication.run方法中。
由于@Component默认会扫描被该注解标注的类的当前包路径作为默认扫描路径。所以@MySpringBootApplication标注的类的包路径需要注意。
启动tomcat
3.启动tomcat
配置DispatcherServlet
package cn.axj.springboot.my.web.container; 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 abstract class WebContainer{ public static void startContainer(WebApplicationContext webApplicationContext){ System.out.println("启动Web容器"); Tomcat tomcat = new Tomcat(); Server server = tomcat.getServer(); Service service = server.findService("Tomcat"); Connector connector = new Connector(); connector.setPort(8080); StandardEngine 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,Springmvc专属 tomcat.addServlet(contextPath,"dispatcher",new DispatcherServlet(webApplicationContext)); context.addServletMappingDecoded("/*","dispatcher"); try { tomcat.start(); } catch (LifecycleException e) { throw new RuntimeException(e); } } }
至此,一个简易版的SpringBoot就完成了。
整个结构如下图所示
下面进行测试,新建一个user-service模块
- 引入自己写的springboot依赖
<?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.axj</groupId> <artifactId>spring-boot-base</artifactId> <version>1.0-SNAPSHOT</version> </parent> <artifactId>user-service</artifactId> <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.axj</groupId> <artifactId>my-spring-boot</artifactId> <version>1.0-SNAPSHOT</version> </dependency> </dependencies> </project>
- 在cn.axj.user下面创建UserApplication
package cn.axj.user; import cn.axj.springboot.my.annnotation.MySpringBootApplication; import cn.axj.springboot.my.boot.MySpringApplication; @MySpringBootApplication public class UserApplication { public static void main(String[] args) { MySpringApplication.run(UserApplication.class, args); } }
- 新建controller层TestController
package cn.axj.user.controller; import cn.axj.user.service.UserService; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RestController; import javax.annotation.Resource; @RestController public class TestController { @Resource private UserService userService; @GetMapping("/test") public String test(){ return userService.test(); } }
- 新建service层UserService
package cn.axj.user.service; import org.springframework.stereotype.Service; @Service public class UserService { public String test(){ return "hello"; } }
- 启动main方法
启动Web容器 三月 27, 2024 5:55:51 下午 org.apache.coyote.AbstractProtocol init 信息: Initializing ProtocolHandler ["http-nio-8080"] 三月 27, 2024 5:55:51 下午 org.apache.catalina.core.StandardService startInternal 信息: Starting service [Tomcat] 三月 27, 2024 5:55:51 下午 org.apache.catalina.core.StandardEngine startInternal 信息: Starting Servlet engine: [Apache Tomcat/9.0.75] 三月 27, 2024 5:55:52 下午 org.apache.catalina.util.SessionIdGeneratorBase createSecureRandom 警告: Creation of SecureRandom instance for session ID generation using [SHA1PRNG] took [322] milliseconds. 三月 27, 2024 5:55:52 下午 org.apache.coyote.AbstractProtocol start 信息: Starting ProtocolHandler ["http-nio-8080"] 三月 27, 2024 5:56:07 下午 org.apache.catalina.core.ApplicationContext log 信息: Initializing Spring DispatcherServlet 'dispatcher' 三月 27, 2024 5:56:07 下午 org.springframework.web.servlet.FrameworkServlet initServletBean 信息: Initializing Servlet 'dispatcher' 三月 27, 2024 5:56:08 下午 org.springframework.web.servlet.FrameworkServlet initServletBean 信息: Completed initialization in 443 ms 三月 27, 2024 5:56:08 下午 org.springframework.web.servlet.DispatcherServlet noHandlerFound 警告: No mapping for GET / 三月 27, 2024 5:56:08 下午 org.springframework.web.servlet.DispatcherServlet noHandlerFound 警告: No mapping for GET / 三月 27, 2024 5:56:08 下午 org.springframework.web.servlet.DispatcherServlet noHandlerFound 警告: No mapping for GET /favicon.ico
至此项目启动完成
- 打开浏览器,访问localhost:8080/test,看到返回test字符串,代表成功
当我们想将tomcat换成jetty或者是underTow容器怎么实现呢?