京东Java面试被问:Spring Boot嵌入式容器的启动和端口绑定原理

一、嵌入式容器架构设计

1. 整体架构概览

图表

代码

复制

下载

全屏

复制代码
graph TB
    A[SpringApplication.run] --> B[创建ApplicationContext]
    B --> C[刷新上下文]
    C --> D[触发WebServer工厂回调]
    D --> E[创建WebServer]
    E --> F{端口绑定}
    F -->|成功| G[启动容器]
    F -->|失败| H[端口递增重试/失败]
    G --> I[处理请求]
    
    subgraph "WebServer类型"
        J[Tomcat]
        K[Jetty]
        L[Undertow]
        M[Netty]
    end

2. 核心接口与抽象

java

复制

下载

复制代码
// Spring Boot嵌入式容器核心接口
public interface WebServer {
    void start() throws WebServerException;
    void stop() throws WebServerException;
    int getPort();
}

public interface WebServerFactory {
    WebServer getWebServer(ServletContextInitializer... initializers);
}

public interface ConfigurableWebServerFactory extends WebServerFactory {
    void setPort(int port);
    void setAddress(InetAddress address);
    void setSsl(Ssl ssl);
    void setCompression(Compression compression);
    // 更多配置方法...
}

// Servlet容器专用接口
public interface ServletWebServerFactory extends WebServerFactory {
    WebServer getWebServer(ServletContextInitializer... initializers);
}

二、容器启动流程详解

1. 启动入口与上下文准备

java

复制

下载

复制代码
@SpringBootApplication
public class Application {
    public static void main(String[] args) {
        // 1. 创建SpringApplication实例
        SpringApplication app = new SpringApplication(Application.class);
        
        // 2. 运行应用(核心启动流程)
        ConfigurableApplicationContext context = app.run(args);
        
        // 3. 获取WebServer
        WebServer webServer = context.getBean(WebServer.class);
        System.out.println("Server started on port: " + webServer.getPort());
    }
}

// SpringApplication.run()内部流程
public class SpringApplication {
    public ConfigurableApplicationContext run(String... args) {
        // 1. 启动计时器
        StopWatch stopWatch = new StopWatch();
        stopWatch.start();
        
        // 2. 准备环境
        ConfigurableEnvironment environment = prepareEnvironment();
        
        // 3. 打印Banner
        Banner printedBanner = printBanner(environment);
        
        // 4. 创建应用上下文(关键步骤)
        ConfigurableApplicationContext context = createApplicationContext();
        
        // 5. 准备上下文
        prepareContext(context, environment, args, printedBanner);
        
        // 6. 刷新上下文(触发容器创建)
        refreshContext(context);
        
        // 7. 启动完成回调
        afterRefresh(context, args);
        
        stopWatch.stop();
        return context;
    }
    
    // 根据classpath自动选择上下文类型
    protected ConfigurableApplicationContext createApplicationContext() {
        Class<?> contextClass = this.applicationContextClass;
        if (contextClass == null) {
            try {
                // 根据类路径判断使用哪种Web容器
                switch (this.webApplicationType) {
                    case SERVLET:
                        // 默认使用AnnotationConfigServletWebServerApplicationContext
                        contextClass = Class.forName(DEFAULT_SERVLET_WEB_CONTEXT_CLASS);
                        break;
                    case REACTIVE:
                        contextClass = Class.forName(DEFAULT_REACTIVE_WEB_CONTEXT_CLASS);
                        break;
                    default:
                        contextClass = Class.forName(DEFAULT_CONTEXT_CLASS);
                }
            } catch (ClassNotFoundException ex) {
                throw new IllegalStateException("Unable create a default ApplicationContext", ex);
            }
        }
        return BeanUtils.instantiateClass(contextClass);
    }
}

2. WebServer的创建时机

java

复制

下载

复制代码
// ServletWebServerApplicationContext(关键上下文类)
public class ServletWebServerApplicationContext extends GenericWebApplicationContext
        implements ConfigurableWebServerApplicationContext {
    
    private volatile WebServer webServer;
    
    @Override
    protected void onRefresh() {
        super.onRefresh();
        try {
            // 创建WebServer的核心方法
            createWebServer();
        } catch (Throwable ex) {
            throw new ApplicationContextException("Unable to start web server", ex);
        }
    }
    
    private void createWebServer() {
        WebServer webServer = this.webServer;
        ServletContext servletContext = getServletContext();
        
        if (webServer == null && servletContext == null) {
            // 1. 获取WebServer工厂
            ServletWebServerFactory factory = getWebServerFactory();
            
            // 2. 获取ServletContextInitializer(用于配置Servlet、Filter等)
            ServletContextInitializer[] initializers = getServletContextInitializers();
            
            // 3. 创建WebServer实例
            this.webServer = factory.getWebServer(initializers);
            
        } else if (servletContext != null) {
            try {
                // 外部容器场景
                getSelfInitializer().onStartup(servletContext);
            } catch (ServletException ex) {
                throw new ApplicationContextException("Cannot initialize servlet context", ex);
            }
        }
        
        // 4. 初始化PropertySources
        initPropertySources();
    }
    
    // 获取WebServer工厂(自动检测机制)
    protected ServletWebServerFactory getWebServerFactory() {
        // 使用Spring的依赖查找
        String[] beanNames = getBeanFactory().getBeanNamesForType(ServletWebServerFactory.class);
        
        if (beanNames.length == 0) {
            throw new ApplicationContextException(
                    "Unable to start ServletWebServerApplicationContext due to missing " +
                    "ServletWebServerFactory bean.");
        }
        
        if (beanNames.length > 1) {
            throw new ApplicationContextException(
                    "Unable to start ServletWebServerApplicationContext due to multiple " +
                    "ServletWebServerFactory beans: " + StringUtils.arrayToCommaDelimitedString(beanNames));
        }
        
        return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
    }
}

篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc

需要全套面试笔记及答案
【点击此处即可/免费获取】

3. Tomcat容器的启动实现

java

复制

下载

复制代码
public class TomcatServletWebServerFactory extends AbstractServletWebServerFactory
        implements ConfigurableTomcatWebServerFactory {
    
    @Override
    public WebServer getWebServer(ServletContextInitializer... initializers) {
        // 1. 创建Tomcat实例
        Tomcat tomcat = new Tomcat();
        
        // 2. 配置基础目录
        File baseDir = (this.baseDirectory != null) ? this.baseDirectory 
                    : createTempDir("tomcat");
        tomcat.setBaseDir(baseDir.getAbsolutePath());
        
        // 3. 创建Connector(连接器)
        Connector connector = new Connector(this.protocol);
        connector.setThrowOnFailure(true);
        
        // 4. 配置端口和地址
        customizeConnector(connector);
        
        // 5. 添加连接器到Service
        tomcat.getService().addConnector(connector);
        
        // 6. 自定义Connector
        customizeConnector(connector);
        tomcat.setConnector(connector);
        
        // 7. 配置引擎和Host
        tomcat.getEngine().setBackgroundProcessorDelay(-1);
        
        for (Connector additionalConnector : this.additionalTomcatConnectors) {
            tomcat.getService().addConnector(additionalConnector);
        }
        
        // 8. 准备Tomcat嵌入上下文
        prepareContext(tomcat.getHost(), initializers);
        
        // 9. 创建并返回WebServer包装器
        return getTomcatWebServer(tomcat);
    }
    
    // TomcatWebServer的创建和启动
    protected TomcatWebServer getTomcatWebServer(Tomcat tomcat) {
        return new TomcatWebServer(tomcat, getPort() >= 0, getShutdown());
    }
}

// TomcatWebServer包装类
public class TomcatWebServer implements WebServer {
    
    private final Tomcat tomcat;
    private final boolean autoStart;
    private final GracefulShutdown gracefulShutdown;
    
    public TomcatWebServer(Tomcat tomcat, boolean autoStart, Shutdown shutdown) {
        this.tomcat = tomcat;
        this.autoStart = autoStart;
        
        // 初始化
        initialize();
    }
    
    private void initialize() throws WebServerException {
        // 1. 配置Tomcat服务器
        synchronized (this.monitor) {
            try {
                // 2. 添加引擎
                addInstanceIdToEngine();
                
                // 3. 配置上下文(将Spring的ServletContextInitializer适配到Tomcat)
                Context context = findContext();
                context.addLifecycleListener((event) -> {
                    if (context.equals(event.getSource()) 
                            && Lifecycle.START_EVENT.equals(event.getType())) {
                        // 移除Tomcat的默认JSP Servlet
                        removeServiceConnectors();
                    }
                });
                
                // 4. 启动Tomcat服务器
                this.tomcat.start();
                
                // 5. 延迟启动Connector,确保所有Servlet都初始化完成
                startDaemonAwaitThread();
                
            } catch (Exception ex) {
                stopSilently();
                throw new WebServerException("Unable to start embedded Tomcat", ex);
            }
        }
    }
    
    @Override
    public void start() throws WebServerException {
        synchronized (this.monitor) {
            if (this.started) {
                return;
            }
            try {
                // 启动Connector
                Connector connector = this.tomcat.getConnector();
                if (connector != null) {
                    // 执行Connector的start方法
                    connector.getProtocolHandler().start();
                }
                this.started = true;
                logger.info("Tomcat started on port(s): " + getPortsDescription());
                
            } catch (Exception ex) {
                throw new WebServerException("Unable to start embedded Tomcat", ex);
            }
        }
    }
}

三、端口绑定原理深度分析

1. 端口获取与配置优先级

java

复制

下载

复制代码
public class WebServerFactoryCustomizerBeanPostProcessor 
        implements BeanPostProcessor, BeanFactoryAware {
    
    // 自定义端口配置
    @Override
    public Object postProcessBeforeInitialization(Object bean, String beanName) {
        if (bean instanceof WebServerFactory) {
            postProcessBeforeInitialization((WebServerFactory) bean);
        }
        return bean;
    }
    
    private void postProcessBeforeInitialization(WebServerFactory webServerFactory) {
        // 应用所有自定义配置器
        LambdaSafe.callbacks(WebServerFactoryCustomizer.class, 
                getCustomizers(), webServerFactory)
                .withLogger(WebServerFactoryCustomizerBeanPostProcessor.class)
                .invoke((customizer) -> customizer.customize(webServerFactory));
    }
    
    // 端口配置的优先级顺序
    private void customizePort(ConfigurableWebServerFactory factory) {
        // 配置优先级:
        // 1. 编程式配置(最高优先级)
        // 2. 外部化配置(application.properties)
        // 3. 操作系统环境变量
        // 4. 默认值(8080)
        
        // 获取端口配置
        Integer port = getConfiguredPort();
        
        if (port != null) {
            factory.setPort(port);
        } else {
            // 使用随机端口
            if (isRandomPortEnabled()) {
                factory.setPort(0); // 0表示随机端口
            }
        }
        
        // 配置地址绑定
        InetAddress address = getConfiguredAddress();
        if (address != null) {
            factory.setAddress(address);
        }
    }
}

// 端口配置来源
public class ServerProperties {
    
    private final Integer port;
    private final InetAddress address;
    private final Ssl ssl;
    private final Compression compression;
    
    // 从配置文件中读取
    @ConfigurationProperties(prefix = "server", ignoreUnknownFields = true)
    public static class ServerProperties {
        
        /**
         * 端口配置,支持多种格式:
         * server.port=8080                  # 固定端口
         * server.port=0                    # 随机端口
         * server.port=${PORT:8080}         # 环境变量优先
         * server.port=${random.int(10000, 20000)} # 随机范围
         */
        private Integer port;
        
        /**
         * 绑定地址:
         * server.address=192.168.1.100     # 绑定到特定IP
         * server.address=0.0.0.0           # 绑定到所有接口(默认)
         */
        private InetAddress address;
        
        /**
         * SSL配置
         */
        private Ssl ssl;
        
        // 应用配置到WebServerFactory
        public void customize(ConfigurableWebServerFactory factory) {
            PropertyMapper map = PropertyMapper.get();
            map.from(this::getPort).whenNonNull().to(factory::setPort);
            map.from(this::getAddress).whenNonNull().to(factory::setAddress);
            map.from(this::getSsl).whenNonNull().to(factory::setSsl);
            map.from(this::getCompression).whenNonNull().to(factory::setCompression);
        }
    }
}

2. 随机端口实现机制

java

复制

下载

复制代码
public class AbstractServletWebServerFactory 
        extends AbstractConfigurableWebServerFactory {
    
    @Override
    public WebServer getWebServer(ServletContextInitializer... initializers) {
        // 如果端口为0,使用随机端口
        if (getPort() == null || getPort() == 0) {
            // 使用Socket找到可用端口
            int port = SocketUtils.findAvailableTcpPort();
            setPort(port);
        }
        
        // 创建WebServer...
        return createWebServer(initializers);
    }
}

// 端口查找工具类
public abstract class SocketUtils {
    
    /**
     * 查找可用的TCP端口
     * @param minPort 最小端口(包含)
     * @param maxPort 最大端口(包含)
     * @return 可用的端口号
     */
    public static int findAvailableTcpPort(int minPort, int maxPort) {
        return findAvailablePort(minPort, maxPort, Type.TCP);
    }
    
    /**
     * 查找可用端口的实现
     */
    private static int findAvailablePort(int minPort, int maxPort, Type type) {
        for (int port = minPort; port <= maxPort; port++) {
            try {
                if (isPortAvailable(port, type)) {
                    return port;
                }
            } catch (Exception ex) {
                // 端口检查失败,继续尝试下一个
            }
        }
        
        throw new IllegalStateException(
                String.format("Could not find an available %s port in the range [%d, %d]", 
                type, minPort, maxPort));
    }
    
    /**
     * 检查端口是否可用
     */
    private static boolean isPortAvailable(int port, Type type) {
        try (ServerSocket serverSocket = (type == Type.TCP) 
                ? new ServerSocket(port) 
                : new DatagramSocket(port)) {
            
            // 设置SO_REUSEADDR以快速重用端口
            serverSocket.setReuseAddress(true);
            
            // 绑定到本地所有地址
            if (type == Type.TCP) {
                ((ServerSocket) serverSocket).bind(
                    new InetSocketAddress(InetAddress.getByName("0.0.0.0"), port));
            }
            
            return true;
        } catch (IOException ex) {
            return false;
        }
    }
}

3. 端口绑定与监听实现

java

复制

下载

复制代码
// Tomcat Connector端口绑定实现
public class TomcatWebServer extends WebServer {
    
    private void startConnector(Connector connector) {
        try {
            // 获取协议处理器
            ProtocolHandler handler = connector.getProtocolHandler();
            
            // 配置端点
            AbstractEndpoint<?, ?> endpoint = (AbstractEndpoint<?, ?>) 
                    handler.getEndpoint();
            
            // 设置端口和地址
            endpoint.setPort(connector.getPort());
            if (connector.getProperty("address") != null) {
                endpoint.setAddress(
                    InetAddress.getByName(connector.getProperty("address")));
            }
            
            // 启动端点,开始监听端口
            endpoint.start();
            
            // 端口绑定成功后的回调
            connector.setState(LifecycleState.STARTED);
            
        } catch (Exception ex) {
            // 端口绑定失败处理
            if (connector.getThrowOnFailure()) {
                stopSilently();
                throw new WebServerException("Unable to start embedded Tomcat", ex);
            }
        }
    }
}

// NIO端口的详细绑定过程
public class NioEndpoint extends AbstractEndpoint<NioChannel, SocketChannel> {
    
    @Override
    public void bind() throws Exception {
        // 1. 初始化ServerSocketChannel
        serverSock = ServerSocketChannel.open();
        socketProperties.setProperties(serverSock.socket());
        
        // 2. 获取InetSocketAddress
        InetSocketAddress addr = new InetSocketAddress(
            getAddress(), getPort());
        
        // 3. 绑定到端口
        serverSock.socket().bind(addr, getAcceptCount());
        
        // 4. 配置为非阻塞模式
        serverSock.configureBlocking(true);
        
        // 5. 设置SO_REUSEADDR
        serverSock.socket().setReuseAddress(getReuseAddress());
        
        // 6. 初始化Acceptor线程
        initialiseSsl();
        
        // 7. 开始接受连接
        startAcceptorThreads();
    }
    
    @Override
    public void startInternal() throws Exception {
        if (!running) {
            running = true;
            paused = false;
            
            // 创建工作线程池
            if (getExecutor() == null) {
                createExecutor();
            }
            
            // 初始化连接限流器
            initializeConnectionLatch();
            
            // 启动轮询器线程
            startPollers();
            
            // 绑定端口
            bind();
            
            // 设置状态为运行中
            setState(LifecycleState.STARTING);
        }
    }
}

四、端口冲突处理与优雅启动

1. 端口冲突检测与重试

java

复制

下载

复制代码
public class PortInUseException extends WebServerException {
    
    private final int port;
    
    public PortInUseException(int port, Throwable cause) {
        super("Port " + port + " is already in use", cause);
        this.port = port;
    }
    
    public int getPort() {
        return this.port;
    }
}

// 端口冲突处理器
public class WebServerStartStopLifecycle implements SmartLifecycle {
    
    private final WebServer webServer;
    private volatile boolean running;
    
    @Override
    public void start() {
        this.webServer.start();
        this.running = true;
    }
    
    @Override
    public void stop() {
        this.running = false;
        try {
            this.webServer.stop();
        } catch (Exception ex) {
            throw new IllegalStateException(ex);
        }
    }
}

// 自动端口递增策略
public class IncrementalPortStrategy implements PortStrategy {
    
    private final int basePort;
    private final int maxAttempts;
    private final int portRange;
    
    public IncrementalPortStrategy(int basePort, int maxAttempts) {
        this.basePort = basePort;
        this.maxAttempts = maxAttempts;
        this.portRange = 100; // 默认尝试100个端口
    }
    
    @Override
    public int getNextPort() {
        for (int i = 0; i < maxAttempts; i++) {
            int port = basePort + (i % portRange);
            if (isPortAvailable(port)) {
                return port;
            }
        }
        throw new IllegalStateException(
            String.format("No available port found in range [%d, %d]", 
                         basePort, basePort + portRange - 1));
    }
    
    private boolean isPortAvailable(int port) {
        try (ServerSocket serverSocket = new ServerSocket()) {
            serverSocket.setReuseAddress(true);
            serverSocket.bind(new InetSocketAddress(port));
            return true;
        } catch (IOException e) {
            return false;
        }
    }
}

2. 优雅启动与健康检查

java

复制

下载

复制代码
// 优雅启动实现
public class GracefulStartup implements ApplicationListener<ApplicationReadyEvent> {
    
    private final HealthIndicator healthIndicator;
    private final WebServer webServer;
    private boolean ready = false;
    
    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        if (!ready) {
            // 等待依赖服务就绪
            waitForDependencies();
            
            // 执行健康检查
            if (performHealthCheck()) {
                ready = true;
                // 标记服务为可用状态
                markAsReady();
            }
        }
    }
    
    private void waitForDependencies() {
        // 等待数据库连接就绪
        waitForDatabase();
        
        // 等待消息队列连接就绪
        waitForMessageQueue();
        
        // 等待缓存服务就绪
        waitForCache();
    }
    
    private boolean performHealthCheck() {
        Health health = healthIndicator.health();
        return health.getStatus().equals(Status.UP);
    }
}

五、多端口与协议支持

1. HTTP/HTTPS双端口支持

java

复制

下载

复制代码
@Configuration
public class MultiPortConfiguration {
    
    @Bean
    public ServletWebServerFactory servletContainer() {
        TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
        
        // 添加HTTPS连接器
        factory.addAdditionalTomcatConnectors(createSslConnector());
        
        // 添加HTTP/2支持
        factory.addConnectorCustomizers(connector -> {
            connector.addUpgradeProtocol(new Http2Protocol());
        });
        
        return factory;
    }
    
    private Connector createSslConnector() {
        Connector connector = new Connector("org.apache.coyote.http11.Http11NioProtocol");
        
        // 配置HTTPS
        Http11NioProtocol protocol = (Http11NioProtocol) connector.getProtocolHandler();
        
        try {
            // 设置SSL
            SSLHostConfig sslHostConfig = new SSLHostConfig();
            sslHostConfig.setCertificateKeystoreFile("classpath:keystore.p12");
            sslHostConfig.setCertificateKeystorePassword("changeit");
            sslHostConfig.setCertificateKeystoreType("PKCS12");
            
            protocol.addSslHostConfig(sslHostConfig);
            connector.setScheme("https");
            connector.setSecure(true);
            connector.setPort(8443); // HTTPS端口
            
            // 配置重定向:HTTP自动跳转到HTTPS
            connector.setRedirectPort(8443);
            
        } catch (Exception ex) {
            throw new IllegalStateException("Could not configure SSL", ex);
        }
        
        return connector;
    }
}

2. 管理端口与健康端点分离

yaml

复制

下载

复制代码
# application.yml配置
server:
  port: 8080  # 应用主端口
  
management:
  server:
    port: 8081  # 管理端口
    address: 127.0.0.1  # 只绑定到本地地址
    
  endpoints:
    web:
      exposure:
        include: health,info,metrics
      base-path: /actuator
      
  endpoint:
    health:
      show-details: always
      probes:
        enabled: true

java

复制

下载

复制代码
// 管理端口配置类
@Configuration
@ConditionalOnWebApplication(type = Type.SERVLET)
@ConditionalOnManagementPort(ManagementPortType.DIFFERENT)
public class ManagementPortConfiguration {
    
    @Bean
    public WebServerFactoryCustomizer<ConfigurableServletWebServerFactory> 
            managementPortCustomizer(ManagementServerProperties management) {
        
        return (factory) -> {
            if (management.getPort() != null) {
                factory.setPort(management.getPort());
            }
            if (management.getAddress() != null) {
                factory.setAddress(management.getAddress());
            }
            
            // 只暴露管理端点
            factory.setContextPath(management.getBasePath());
            factory.setRegisterDefaultServlet(false);
        };
    }
    
    @Bean
    public DispatcherServletPathProvider managementDispatcherServletPathProvider() {
        return () -> "/";
    }
}

六、性能优化与生产实践

1. 连接器优化配置

java

复制

下载

复制代码
@Configuration
public class TomcatOptimizationConfig {
    
    @Bean
    public WebServerFactoryCustomizer<TomcatServletWebServerFactory> tomcatCustomizer() {
        return (factory) -> {
            factory.addConnectorCustomizers((connector) -> {
                // 获取协议处理器
                Http11NioProtocol protocol = 
                    (Http11NioProtocol) connector.getProtocolHandler();
                
                // 连接数配置
                protocol.setMaxConnections(10000);     // 最大连接数
                protocol.setMaxThreads(200);           // 最大工作线程
                protocol.setMinSpareThreads(20);       // 最小空闲线程
                protocol.setConnectionTimeout(30000);  // 连接超时30秒
                
                // 连接器优化
                protocol.setAcceptCount(100);          // 等待队列长度
                protocol.setMaxKeepAliveRequests(100); // 最大长连接请求数
                protocol.setKeepAliveTimeout(30000);   // 长连接超时
                
                // NIO缓冲配置
                protocol.setSocketBuffer(8192);        // Socket缓冲区
                protocol.setProcessorCache(200);       // 处理器缓存
                
                // 启用SSL会话缓存
                protocol.setSSLCacheSize(1024);
                protocol.setSSLProtocol("TLSv1.2");
                
                // 启用GZIP压缩
                protocol.setCompression("on");
                protocol.setCompressionMinSize(2048);
                protocol.setCompressableMimeType(
                    "text/html,text/xml,text/plain,text/css,text/javascript," +
                    "application/javascript,application/json");
            });
            
            // 配置静态资源缓存
            factory.addContextCustomizers((context) -> {
                context.addWelcomeFile("index.html");
                context.setResources(new File("src/main/resources/static"));
                context.setCacheMaxSize(1024 * 1024 * 10); // 10MB缓存
            });
        };
    }
}

2. 启动性能监控

java

复制

下载

复制代码
@Component
public class StartupMetrics implements ApplicationRunner {
    
    private final MeterRegistry meterRegistry;
    private final long startupTime;
    
    public StartupMetrics(MeterRegistry meterRegistry) {
        this.meterRegistry = meterRegistry;
        this.startupTime = System.currentTimeMillis();
    }
    
    @Override
    public void run(ApplicationArguments args) {
        // 记录启动时间
        long duration = System.currentTimeMillis() - startupTime;
        meterRegistry.gauge("application.startup.time", duration);
        
        // 记录内存使用
        Runtime runtime = Runtime.getRuntime();
        meterRegistry.gauge("jvm.memory.used", 
            runtime.totalMemory() - runtime.freeMemory());
        meterRegistry.gauge("jvm.memory.max", runtime.maxMemory());
        
        // 记录端口信息
        WebServer webServer = ApplicationContextHolder.getBean(WebServer.class);
        meterRegistry.gauge("server.port", webServer.getPort());
        
        // 记录活跃连接数
        Timer timer = Timer.builder("server.connections")
            .description("Active connections")
            .register(meterRegistry);
        
        // 定期收集连接数
        ScheduledExecutorService scheduler = Executors.newSingleThreadScheduledExecutor();
        scheduler.scheduleAtFixedRate(() -> {
            int activeConnections = getActiveConnections();
            meterRegistry.gauge("server.connections.active", activeConnections);
        }, 0, 10, TimeUnit.SECONDS);
    }
    
    private int getActiveConnections() {
        // 通过JMX获取Tomcat活跃连接数
        try {
            MBeanServer mBeanServer = ManagementFactory.getPlatformMBeanServer();
            ObjectName objectName = new ObjectName(
                "Tomcat:type=ThreadPool,name=\"http-nio-*\"");
            
            Integer connectionCount = (Integer) mBeanServer.getAttribute(
                objectName, "connectionCount");
            return connectionCount != null ? connectionCount : 0;
            
        } catch (Exception e) {
            return 0;
        }
    }
}

篇幅限制下面就只能给大家展示小册部分内容了。整理了一份核心面试笔记包括了:Java面试、Spring、JVM、MyBatis、Redis、MySQL、并发编程、微服务、Linux、Springboot、SpringCloud、MQ、Kafc

需要全套面试笔记及答案
【点击此处即可/免费获取】

七、常见问题与解决方案

1. 端口绑定失败处理

bash

复制

下载

复制代码
# 常见错误及解决方案

# 错误1: Port 8080 already in use
# 解决方案:
# 1. 杀死占用进程
lsof -i :8080
kill -9 <PID>

# 2. 使用随机端口
server.port=0

# 3. 指定其他端口
server.port=8081

# 错误2: Cannot assign requested address
# 解决方案:
# 1. 检查IP地址配置
server.address=0.0.0.0  # 绑定到所有接口

# 2. 检查网络接口状态
ifconfig
ip addr show

# 错误3: Permission denied (bind)
# 解决方案:
# 1. 使用1024以上端口(非特权端口)
server.port=1025

# 2. 以root权限运行(不推荐)
sudo java -jar app.jar

2. 启动超时配置

yaml

复制

下载

复制代码
# application.yml
spring:
  lifecycle:
    timeout-per-shutdown-phase: 30s  # 优雅关闭超时时间
  
  main:
    banner-mode: off  # 关闭Banner加快启动
    
  application:
    name: myapp
    
server:
  tomcat:
    connection-timeout: 20000
    keep-alive-timeout: 30000
    max-connections: 10000
    threads:
      max: 200
      min-spare: 10
      
# 启用快速失败
  jetty:
    max-wait: 10000
    min-threads: 8
    max-threads: 200

3. 容器选择与性能对比

java

复制

下载

复制代码
// 容器性能对比配置
@Configuration
public class WebServerComparison {
    
    // Tomcat (默认,平衡性好)
    @Bean
    @ConditionalOnClass(name = "org.apache.catalina.startup.Tomcat")
    public ServletWebServerFactory tomcatFactory() {
        TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
        // Tomcat适合传统Web应用
        return factory;
    }
    
    // Jetty (轻量级,异步支持好)
    @Bean
    @ConditionalOnClass(name = "org.eclipse.jetty.server.Server")
    public ServletWebServerFactory jettyFactory() {
        JettyServletWebServerFactory factory = new JettyServletWebServerFactory();
        // Jetty适合高并发长连接
        return factory;
    }
    
    // Undertow (性能优秀,内存占用低)
    @Bean
    @ConditionalOnClass(name = "io.undertow.Undertow")
    public ServletWebServerFactory undertowFactory() {
        UndertowServletWebServerFactory factory = new UndertowServletWebServerFactory();
        // Undertow适合微服务和高性能场景
        return factory;
    }
    
    // Netty (响应式编程,非阻塞IO)
    @Bean
    @ConditionalOnClass(name = "reactor.netty.http.server.HttpServer")
    public ReactiveWebServerFactory nettyFactory() {
        NettyReactiveWebServerFactory factory = new NettyReactiveWebServerFactory();
        // Netty适合响应式应用
        return factory;
    }
}

八、总结

核心要点总结:

  1. 启动流程

    • SpringApplication.run() 触发上下文刷新

    • ServletWebServerApplicationContext 创建WebServer

    • 通过SPI自动检测并创建合适的容器(Tomcat/Jetty/Undertow)

  2. 端口绑定机制

    • 端口配置优先级:编程配置 > 外部配置 > 默认值

    • 端口0表示随机端口,由SocketUtils查找可用端口

    • 实际绑定由容器(如Tomcat的Connector)执行

  3. 关键设计模式

    • 工厂模式:WebServerFactory创建不同类型的WebServer

    • 模板方法:AbstractServletWebServerFactory定义创建流程

    • 观察者模式:通过事件监听器处理启动各个阶段

  4. 生产建议

    • 使用随机端口+服务注册实现动态端口分配

    • 配置管理端口分离,增强安全性

    • 监控启动时间和资源使用

    • 根据应用场景选择合适的容器

Spring Boot的嵌入式容器设计充分体现了"约定优于配置"的原则,通过自动配置和合理的默认值,让开发者能够快速启动和运行Web应用,同时提供了丰富的扩展点供高级用户定制。

相关推荐
shangjian0072 小时前
AI大模型-机器学习-算法-线性回归-优化方法
人工智能·算法·机器学习
嗯mua.2 小时前
【人工智能】机器学习基础概念
人工智能·机器学习
Yuer20252 小时前
状态不是变量:Rust 量化算子中的 State 工程语义
开发语言·后端·深度学习·机器学习·rust
光羽隹衡2 小时前
机器学习——词向量转化和评论判断项目分析
人工智能·学习·机器学习
前端切图仔0012 小时前
Chrome 扩展程序上架指南
android·java·javascript·google
有味道的男人2 小时前
接入京东关键词API的核心利弊分析
大数据·人工智能·信息可视化
啊巴矲2 小时前
小白从零开始勇闯人工智能:机器学习初级篇(词向量转换)
人工智能·机器学习
shangjian0072 小时前
AI大模型-机器学习-算法-逻辑回归
人工智能·算法·机器学习
彩妙不是菜喵2 小时前
c++:初阶/初始模版
开发语言·c++