三公机器人

牛牛机器人,三公撑船机器人,微信牛牛机器人

嵌入式Web服务器与自动装配机制


在上一篇文章中,我们实现了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的核心机制:

  1. 嵌入式Web服务器:基于Tomcat实现了嵌入式服务器,支持Servlet容器

  2. DispatcherServlet:完整的请求分发机制,支持URL映射和方法调用

  3. 条件注解系统:实现了类路径条件、Bean缺失条件等核心条件注解

  4. 自动配置机制:基于条件注解的自动配置系统,支持按需加载

  5. 完整的启动流程:从扫描、注册到启动的完整生命周期管理

通过这约300行代码,我们实现了一个功能相对完整的微型SpringBoot框架,涵盖了自动配置、嵌入式服务器、条件装配等核心特性。虽然功能相对简化,但核心原理与SpringBoot保持一致。


Powered By Z-BlogPHP 1.7.3

三公机器人,牛牛机器人,三公撑船机器人,微信牛牛机器人