手写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>
-
启动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定义的路径地址,扫描组件注入到容器中
-
启动tomcat
启动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 {@Resourceprivate 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换成netty或者是underTow容器怎么实现呢?