系列文章目录
手写SpringBoot(一)之简易版SpringBoot
手写SpringBoot(二)之动态切换Servlet容器
手写SpringBoot(三)之自动配置
手写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定义的路径地址,扫描组件注入到容器中。
这里由于MySpringBootApplication注解是一个组合注解,包括@Configuration和@ComponentScan,Spring在获取到@MySpringBootApplication标注的类,会默认将该类作为配置类,所以这个@MySpringBootApplication不一定非得加到Application类上,可以专门定义一个Configuration类来标注,但是需要将该类Class作为参数传给MySpringApplication.run方法中。
由于@Component默认会扫描被该注解标注的类的当前包路径作为默认扫描路径。所以@MySpringBootApplication标注的类的包路径需要注意。
-
启动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 {
@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容器怎么实现呢?