在上一篇文章中,我们实现了SpringBoot的基本注解系统和IoC容器。本文将深入探讨嵌入式Web服务器启动机制和自动装配原理,进一步完善我们的微型SpringBoot框架。
一、嵌入式Tomcat服务器实现
1.1 服务器启动器接口设计
首先定义一个服务器启动器接口,为支持多种嵌入式服务器(Tomcat、Jetty、Undertow)做准备:
public interface MyWebServer { void start() throws Exception; void stop() throws Exception; int getPort(); }
1.2 Tomcat服务器实现
基于Servlet API实现嵌入式Tomcat服务器:
import org.apache.catalina.Context; import org.apache.catalina.LifecycleException; import org.apache.catalina.startup.Tomcat; import javax.servlet.Servlet; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.File; import java.io.IOException;
public class MyTomcatWebServer implements MyWebServer { private Tomcat tomcat; private int port;
public MyTomcatWebServer(int port) {
this.port = port;
this.tomcat = new Tomcat();
}
@Override
public void start() throws Exception {
// 设置临时目录
File baseDir = createTempDir("tomcat");
tomcat.setBaseDir(baseDir.getAbsolutePath());
// 设置端口
tomcat.setPort(port);
// 创建上下文
Context context = tomcat.addContext("", baseDir.getAbsolutePath());
// 添加DispatcherServlet
Tomcat.addServlet(context, "dispatcher", new MyDispatcherServlet());
context.addServletMappingDecoded("/", "dispatcher");
// 启动Tomcat
tomcat.start();
tomcat.getServer().await();
}
@Override
public void stop() throws Exception {
if (tomcat != null) {
tomcat.stop();
tomcat.destroy();
}
}
@Override
public int getPort() {
return port;
}
private File createTempDir(String prefix) {
try {
File tempDir = File.createTempFile(prefix + ".", "." + port);
tempDir.delete();
tempDir.mkdir();
return tempDir;
} catch (IOException e) {
throw new RuntimeException("创建临时目录失败", e);
}
}
}
二、DispatcherServlet实现
2.1 请求分发器核心逻辑
DispatcherServlet是Spring MVC的核心,负责将请求分发到对应的控制器方法:
import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.lang.reflect.Method; import java.util.HashMap; import java.util.Map;
public class MyDispatcherServlet extends HttpServlet { // URL到处理器方法的映射 private Map<String, HandlerMethod> handlerMapping = new HashMap<>();
// IoC容器引用
private Map<String, Object> iocContainer;
@Override
public void init(ServletConfig config) throws ServletException {
// 初始化处理器映射
initHandlerMapping();
}
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String requestURI = req.getRequestURI();
String contextPath = req.getContextPath();
String url = requestURI.replace(contextPath, "").replaceAll("/+", "/");
HandlerMethod handler = handlerMapping.get(url);
if (handler == null) {
resp.sendError(HttpServletResponse.SC_NOT_FOUND);
return;
}
try {
// 执行处理器方法
Object result = handler.method.invoke(handler.bean);
if (result instanceof String) {
resp.getWriter().write((String) result);
}
} catch (Exception e) {
throw new ServletException("执行处理器方法失败", e);
}
}
private void initHandlerMapping() {
// 遍历IoC容器中的所有Bean
for (Map.Entry<String, Object> entry : iocContainer.entrySet()) {
Object bean = entry.getValue();
Class<?> beanClass = bean.getClass();
// 检查类是否有RequestMapping注解
if (beanClass.isAnnotationPresent(MyRequestMapping.class)) {
MyRequestMapping classMapping = beanClass.getAnnotation(MyRequestMapping.class);
String basePath = classMapping.value();
// 遍历类中的所有方法
for (Method method : beanClass.getDeclaredMethods()) {
if (method.isAnnotationPresent(MyRequestMapping.class)) {
MyRequestMapping methodMapping = method.getAnnotation(MyRequestMapping.class);
String methodPath = methodMapping.value();
String fullPath = basePath + methodPath;
// 注册处理器方法
handlerMapping.put(fullPath, new HandlerMethod(bean, method));
}
}
}
}
}
// 处理器方法封装类
private static class HandlerMethod {
Object bean;
Method method;
HandlerMethod(Object bean, Method method) {
this.bean = bean;
this.method = method;
}
}
// 设置IoC容器
public void setIocContainer(Map<String, Object> iocContainer) {
this.iocContainer = iocContainer;
}
}
三、自动配置条件注解系统
3.1 条件注解实现
SpringBoot的自动配置依赖于条件注解系统,这里实现几个核心条件注解:
import java.lang.annotation.*;
// 类路径条件:当指定类存在时生效 @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ConditionalOnClass { String[] value() default {}; }
// Bean缺失条件:当指定Bean不存在时生效 @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ConditionalOnMissingBean { Class<?>[] value() default {}; }
// 属性条件:当配置属性满足条件时生效 @Target({ElementType.TYPE, ElementType.METHOD}) @Retention(RetentionPolicy.RUNTIME) @Documented public @interface ConditionalOnProperty { String name(); String havingValue() default ""; boolean matchIfMissing() default false; }
3.2 条件评估器
实现条件注解的评估逻辑:
import org.springframework.context.annotation.Condition; import org.springframework.context.annotation.ConditionContext; import org.springframework.core.type.AnnotatedTypeMetadata; import java.util.Map;
public class OnClassCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { Map<String, Object> attributes = metadata.getAnnotationAttributes( ConditionalOnClass.class.getName()); if (attributes == null) { return false; }
String[] classNames = (String[]) attributes.get("value");
for (String className : classNames) {
try {
Class.forName(className);
} catch (ClassNotFoundException e) {
return false;
}
}
return true;
}
}
public class OnMissingBeanCondition implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { Map<String, Object> attributes = metadata.getAnnotationAttributes( ConditionalOnMissingBean.class.getName()); if (attributes == null) { return false; }
Class<?>[] beanTypes = (Class<?>[]) attributes.get("value");
for (Class<?> beanType : beanTypes) {
String[] beanNames = context.getBeanFactory().getBeanNamesForType(beanType);
if (beanNames.length > 0) {
return false;
}
}
return true;
}
}
四、自动配置类实现
4.1 Web自动配置类
实现基于条件注解的Web自动配置:
@MyAutoConfiguration @ConditionalOnClass("javax.servlet.Servlet") @ConditionalOnProperty(name = "server.port", havingValue = "8080", matchIfMissing = true) public class WebAutoConfiguration {
@Bean
@ConditionalOnMissingBean(MyDispatcherServlet.class)
public MyDispatcherServlet dispatcherServlet() {
return new MyDispatcherServlet();
}
@Bean
@ConditionalOnMissingBean(MyWebServer.class)
public MyWebServer webServer(MyDispatcherServlet dispatcherServlet) {
// 从配置中读取端口,默认8080
String portStr = System.getProperty("server.port", "8080");
int port = Integer.parseInt(portStr);
MyTomcatWebServer server = new MyTomcatWebServer(port);
dispatcherServlet.setIocContainer(getIocContainer());
return server;
}
// 模拟获取IoC容器
private Map<String, Object> getIocContainer() {
return MyApplicationContext.getIocContainer();
}
}
4.2 数据源自动配置
实现简单的数据源自动配置:
@MyAutoConfiguration @ConditionalOnClass("javax.sql.DataSource") public class DataSourceAutoConfiguration {
@Bean
@ConditionalOnMissingBean(javax.sql.DataSource.class)
@ConditionalOnProperty(name = "spring.datasource.url")
public DataSource dataSource() {
// 从配置中读取数据源信息
String url = System.getProperty("spring.datasource.url");
String username = System.getProperty("spring.datasource.username");
String password = System.getProperty("spring.datasource.password");
// 这里简化实现,实际应该使用连接池
return new SimpleDataSource(url, username, password);
}
}
五、自动配置导入机制
5.1 自动配置导入选择器
实现自动配置类的加载机制:
import org.springframework.context.annotation.ImportSelector; import org.springframework.core.type.AnnotationMetadata; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.util.ArrayList; import java.util.List;
public class MyAutoConfigurationImportSelector implements ImportSelector {
private static final String AUTO_CONFIGURATION_FILE =
"META-INF/my-spring/autoconfigure.imports";
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
List<String> configurations = new ArrayList<>();
try (BufferedReader reader = new BufferedReader(
new InputStreamReader(
getClass().getClassLoader().getResourceAsStream(AUTO_CONFIGURATION_FILE)
)
)) {
String line;
while ((line = reader.readLine()) != null) {
line = line.trim();
if (!line.isEmpty() && !line.startsWith("#")) {
configurations.add(line);
}
}
} catch (IOException e) {
System.err.println("警告:未找到自动配置文件 " + AUTO_CONFIGURATION_FILE);
}
return configurations.toArray(new String);
}
}
5.2 自动配置文件
在resources/META-INF/my-spring/autoconfigure.imports文件中配置:
com.example.autoconfigure.WebAutoConfiguration com.example.autoconfigure.DataSourceAutoConfiguration
六、应用启动流程优化
6.1 增强版应用启动器
整合所有组件,完善启动流程:
public class MySpringApplication {
public static void run(Class<?> primarySource, String... args) {
System.out.println("MySpringBoot应用启动中...");
// 1. 创建应用上下文
MyApplicationContext context = new MyApplicationContext();
// 2. 扫描并注册Bean
context.scan(primarySource.getPackage().getName());
// 3. 执行自动配置
executeAutoConfigurations(context);
// 4. 刷新上下文
context.refresh();
// 5. 启动Web服务器
startWebServer(context);
System.out.println("MySpringBoot应用启动完成!端口:" +
System.getProperty("server.port", "8080"));
}
private static void executeAutoConfigurations(MyApplicationContext context) {
// 这里简化实现,实际应该通过ImportSelector加载
context.registerBean(WebAutoConfiguration.class);
context.registerBean(DataSourceAutoConfiguration.class);
}
private static void startWebServer(MyApplicationContext context) {
try {
MyWebServer webServer = context.getBean(MyWebServer.class);
if (webServer != null) {
webServer.start();
}
} catch (Exception e) {
throw new RuntimeException("启动Web服务器失败", e);
}
}
}
6.2 应用上下文增强
增强应用上下文,支持自动配置:
public class MyApplicationContext { private static Map<String, Object> iocContainer = new HashMap<>();
public void scan(String basePackage) {
// 扫描指定包下的所有类
List<Class<?>> classes = scanClasses(basePackage);
// 注册Bean
for (Class<?> clazz : classes) {
if (clazz.isAnnotationPresent(MyComponent.class) ||
clazz.isAnnotationPresent(MyService.class) ||
clazz.isAnnotationPresent(MyController.class)) {
registerBean(clazz);
}
}
}
public void refresh() {
// 执行依赖注入
doDependencyInjection();
// 初始化Bean
initializeBeans();
}
public <T> T getBean(Class<T> requiredType) {
for (Object bean : iocContainer.values()) {
if (requiredType.isInstance(bean)) {
return (T) bean;
}
}
return null;
}
public static Map<String, Object> getIocContainer() {
return iocContainer;
}
}
七、测试示例
7.1 启动类
@MySpringBootApplication public class MyTestApplication { public static void main(String[] args) { // 设置服务器端口 System.setProperty("server.port", "8080");
// 启动应用
MySpringApplication.run(MyTestApplication.class, args);
}
}
7.2 控制器示例
@MyController @RequestMapping("/api") public class HelloController {
@RequestMapping("/hello")
public String hello() {
return "Hello from MySpringBoot!";
}
@RequestMapping("/user/{id}")
public String getUser(@PathVariable("id") String id) {
return "User ID: " + id;
}
}
八、总结
本文实现了SpringBoot的核心机制:
嵌入式Web服务器:基于Tomcat实现了嵌入式服务器,支持Servlet容器
DispatcherServlet:完整的请求分发机制,支持URL映射和方法调用
条件注解系统:实现了类路径条件、Bean缺失条件等核心条件注解
自动配置机制:基于条件注解的自动配置系统,支持按需加载
完整的启动流程:从扫描、注册到启动的完整生命周期管理
通过这约300行代码,我们实现了一个功能相对完整的微型SpringBoot框架,涵盖了自动配置、嵌入式服务器、条件装配等核心特性。虽然功能相对简化,但核心原理与SpringBoot保持一致。