SpringBoot run()方法全链路解析(33)

SpringBoot run()方法全链路解析

一、SpringBoot启动流程概述

1.1 SpringBoot应用的入口点

SpringBoot应用的启动通常从main方法开始,其中最核心的是调用SpringApplication.run()方法:

@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

这行简单的代码背后隐藏着复杂的启动流程,SpringApplication.run()方法是整个SpringBoot应用启动的核心入口,它负责创建并配置Spring应用上下文、启动嵌入式服务器等一系列关键操作。

1.2 启动流程的主要阶段

SpringBoot应用的启动流程可以大致分为以下几个主要阶段:

  1. 初始化阶段:创建SpringApplication实例,准备应用上下文环境
  2. 配置加载阶段:加载并解析应用配置,包括application.properties/yml文件、命令行参数等
  3. 自动配置阶段:基于类路径和配置信息,应用自动配置机制
  4. 上下文创建阶段:创建并刷新Spring应用上下文
  5. 嵌入式服务器启动阶段:启动嵌入式Web服务器,处理HTTP请求
  6. 应用就绪阶段:发布应用就绪事件,标志应用已成功启动

1.3 核心组件概述

在启动过程中,涉及到多个核心组件:

  • SpringApplication:启动SpringBoot应用的核心类,负责协调整个启动过程
  • ApplicationContext:Spring应用上下文,管理应用中的所有Bean
  • Environment:应用环境抽象,负责加载和管理配置属性
  • ApplicationEventPublisher:事件发布器,负责发布应用生命周期事件
  • AutoConfiguration:自动配置机制,基于条件注解自动配置应用

二、SpringApplication实例的创建

2.1 run()方法的重载形式

SpringApplication.run()方法有多种重载形式,最常用的是:

public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) {
    return run(new Class<?>[] { primarySource }, args);
}

public static ConfigurableApplicationContext run(Class<?>[] primarySources, String... args) {
    return new SpringApplication(primarySources).run(args);
}

可以看到,最终会创建一个SpringApplication实例并调用其run()方法。

2.2 SpringApplication构造函数

SpringApplication的构造函数会执行一系列初始化操作:

public SpringApplication(Class<?>... primarySources) {
    this(null, primarySources);
}

@SuppressWarnings({ "unchecked", "rawtypes" })
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
    // 设置资源加载器
    this.resourceLoader = resourceLoader;
    Assert.notNull(primarySources, "PrimarySources must not be null");
    this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
    
    // 判断应用类型(REACTIVE, SERVLET, NONE)
    this.webApplicationType = WebApplicationType.deduceFromClasspath();
    
    // 设置初始化器
    setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
    
    // 设置监听器
    setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
    
    // 推断主应用类
    this.mainApplicationClass = deduceMainApplicationClass();
}

2.3 应用类型的推断

在构造函数中,会通过WebApplicationType.deduceFromClasspath()方法推断应用类型:

static WebApplicationType deduceFromClasspath() {
    // 判断是否存在REACTIVE相关类
    if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null) && !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
            && !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
        return WebApplicationType.REACTIVE;
    }
    
    // 判断是否存在SERVLET相关类
    for (String className : SERVLET_INDICATOR_CLASSES) {
        if (!ClassUtils.isPresent(className, null)) {
            return WebApplicationType.NONE;
        }
    }
    
    return WebApplicationType.SERVLET;
}

这里通过检查类路径中是否存在特定的类来判断应用类型:

  • REACTIVE:存在org.springframework.web.reactive.DispatcherHandler且不存在org.springframework.web.servlet.DispatcherServlet
  • SERVLET:存在javax.servlet.Servlet和org.springframework.web.context.ConfigurableWebApplicationContext
  • NONE:既不是REACTIVE也不是SERVLET

2.4 初始化器和监听器的加载

SpringApplication会从META-INF/spring.factories文件中加载初始化器和监听器:

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) {
    return getSpringFactoriesInstances(type, new Class<?>[] {});
}

private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) {
    ClassLoader classLoader = getClassLoader();
    
    // 从META-INF/spring.factories加载指定类型的类
    Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader));
    
    // 实例化这些类
    List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
    
    // 排序
    AnnotationAwareOrderComparator.sort(instances);
    return instances;
}

SpringFactoriesLoader.loadFactoryNames()方法会从所有META-INF/spring.factories文件中查找指定类型的实现类:

public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
    String factoryTypeName = factoryType.getName();
    return loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}

private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
    MultiValueMap<String, String> result = cache.get(classLoader);
    if (result != null) {
        return result;
    }
    
    try {
        // 查找所有META-INF/spring.factories资源
        Enumeration<URL> urls = (classLoader != null ?
                classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
                ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
        result = new LinkedMultiValueMap<>();
        
        // 解析每个spring.factories文件
        while (urls.hasMoreElements()) {
            URL url = urls.nextElement();
            UrlResource resource = new UrlResource(url);
            Properties properties = PropertiesLoaderUtils.loadProperties(resource);
            
            for (Map.Entry<?, ?> entry : properties.entrySet()) {
                String factoryTypeName = ((String) entry.getKey()).trim();
                String[] factoryImplementationNames =
                        StringUtils.commaDelimitedListToStringArray((String) entry.getValue());
                
                for (String factoryImplementationName : factoryImplementationNames) {
                    result.add(factoryTypeName, factoryImplementationName.trim());
                }
            }
        }
        
        cache.put(classLoader, result);
        return result;
    }
    catch (IOException ex) {
        throw new IllegalArgumentException("Unable to load factories from location [" +
                FACTORIES_RESOURCE_LOCATION + "]", ex);
    }
}

2.5 主应用类的推断

SpringApplication会尝试推断主应用类:

private Class<?> deduceMainApplicationClass() {
    try {
        // 获取当前线程的堆栈轨迹
        StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
        for (StackTraceElement stackTraceElement : stackTrace) {
            if ("main".equals(stackTraceElement.getMethodName())) {
                // 返回包含main方法的类
                return Class.forName(stackTraceElement.getClassName());
            }
        }
    }
    catch (ClassNotFoundException ex) {
        // 忽略异常
    }
    return null;
}

三、run()方法的核心流程

3.1 核心run()方法实现

SpringApplication实例创建完成后,会调用其run()方法:

public ConfigurableApplicationContext run(String... args) {
    // 记录启动开始时间
    long startTime = System.nanoTime();
    
    // 创建默认的应用上下文和异常报告器
    DefaultBootstrapContext bootstrapContext = createBootstrapContext();
    ConfigurableApplicationContext context = null;
    
    // 配置无头模式
    configureHeadlessProperty();
    
    // 获取并启动应用监听器
    SpringApplicationRunListeners listeners = getRunListeners(args);
    listeners.starting(bootstrapContext, this.mainApplicationClass);
    
    try {
        // 创建应用参数容器
        ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
        
        // 准备环境
        ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
        
        // 配置忽略的Bean信息
        configureIgnoreBeanInfo(environment);
        
        // 打印Banner
        Banner printedBanner = printBanner(environment);
        
        // 创建应用上下文
        context = createApplicationContext();
        
        // 设置引导上下文
        context.setBootstrapContext(bootstrapContext);
        
        // 准备上下文
        prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
        
        // 刷新上下文
        refreshContext(context);
        
        // 刷新后的处理
        afterRefresh(context, applicationArguments);
        
        // 计算并记录启动时间
        Duration timeTakenToStartup = Duration.ofNanos(System.nanoTime() - startTime);
        
        // 记录启动完成日志
        if (this.logStartupInfo) {
            new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), timeTakenToStartup);
        }
        
        // 发布应用就绪事件
        listeners.started(context, timeTakenToStartup);
        
        // 调用所有Runner
        callRunners(context, applicationArguments);
    }
    catch (Throwable ex) {
        // 处理启动异常
        handleRunFailure(context, ex, listeners);
        throw new IllegalStateException(ex);
    }
    
    try {
        // 发布应用运行中事件
        Duration timeTakenToReady = Duration.ofNanos(System.nanoTime() - startTime);
        listeners.ready(context, timeTakenToReady);
    }
    catch (Throwable ex) {
        // 处理就绪异常
        if (context != null) {
            context.close();
            throw new IllegalStateException("Application run failed", ex);
        }
        throw new IllegalStateException("Application run failed", ex);
    }
    
    // 返回应用上下文
    return context;
}

3.2 启动监听器的初始化与启动

在run()方法开始时,会初始化并启动应用监听器:

private SpringApplicationRunListeners getRunListeners(String... args) {
    Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
    return new SpringApplicationRunListeners(logger,
            getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args),
            this.applicationStartup);
}

// SpringApplicationRunListeners类
public void starting(DefaultBootstrapContext bootstrapContext, @Nullable Class<?> mainApplicationClass) {
    doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),
            (step) -> {
                if (mainApplicationClass != null) {
                    step.tag("mainApplicationClass", mainApplicationClass.getName());
                }
                return step;
            });
}

SpringApplicationRunListener是SpringBoot应用启动过程中的核心监听器接口,它定义了应用启动各个阶段的回调方法。

3.3 应用参数与环境的准备

run()方法会创建并准备应用参数和环境:

private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners,
        DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) {
    // 创建并配置环境
    ConfigurableEnvironment environment = getOrCreateEnvironment();
    configureEnvironment(environment, applicationArguments.getSourceArgs());
    
    // 应用属性转换服务
    ConfigurationPropertySources.attach(environment);
    
    // 发布环境准备就绪事件
    listeners.environmentPrepared(bootstrapContext, environment);
    
    // 绑定应用参数
    bindToSpringApplication(environment);
    
    // 确保是标准环境
    if (!this.isCustomEnvironment) {
        environment = new EnvironmentConverter(getClassLoader()).convertEnvironmentIfNecessary(environment,
                deduceEnvironmentClass());
    }
    
    // 配置属性源
    ConfigurationPropertySources.attach(environment);
    return environment;
}

3.4 Banner的打印

SpringBoot应用启动时会打印Banner:

private Banner printBanner(ConfigurableEnvironment environment) {
    if (this.bannerMode == Banner.Mode.OFF) {
        return null;
    }
    
    // 获取资源加载器
    ResourceLoader resourceLoader = (this.resourceLoader != null) ? this.resourceLoader
            : new DefaultResourceLoader(getClassLoader());
    
    // 创建Banner打印器
    SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(resourceLoader, this.banner);
    
    // 打印控制台Banner
    if (this.bannerMode == Banner.Mode.CONSOLE) {
        return bannerPrinter.print(environment, this.mainApplicationClass, System.out);
    }
    
    // 打印日志Banner
    return bannerPrinter.print(environment, this.mainApplicationClass, logger);
}

Banner打印器会尝试从classpath下加载banner.txt或banner.gif等文件作为Banner:

public Banner print(Environment environment, Class<?> sourceClass, PrintStream out) {
    Banner banner = getBanner(environment);
    banner.printBanner(environment, sourceClass, out);
    return banner;
}

private Banner getBanner(Environment environment) {
    Banners banners = new Banners();
    
    // 添加文本Banner
    banners.addIfNotNull(getTextBanner(environment));
    
    // 添加图像Banner
    banners.addIfNotNull(getImageBanner(environment));
    
    // 如果没有找到任何Banner,使用默认Banner
    if (banners.hasAtLeastOneBanner()) {
        return banners;
    }
    
    // 返回默认Banner
    return this.fallbackBanner != null ? this.fallbackBanner : DEFAULT_BANNER;
}

3.5 应用上下文的创建与准备

run()方法会创建并准备应用上下文:

protected ConfigurableApplicationContext createApplicationContext() {
    // 根据应用类型创建不同的上下文
    Class<?> contextClass = this.applicationContextClass;
    if (contextClass == null) {
        try {
            switch (this.webApplicationType) {
                case SERVLET:
                    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 to create a default ApplicationContext, please specify an ApplicationContextClass",
                    ex);
        }
    }
    
    // 实例化上下文
    return (ConfigurableApplicationContext) BeanUtils.instantiateClass(contextClass);
}

private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
        ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
        ApplicationArguments applicationArguments, Banner printedBanner) {
    // 设置环境
    context.setEnvironment(environment);
    
    // 应用上下文后置处理器
    postProcessApplicationContext(context);
    
    // 应用初始化器
    applyInitializers(context);
    
    // 发布上下文准备就绪事件
    listeners.contextPrepared(context);
    
    // 加载资源
    if (this.logStartupInfo) {
        logStartupInfo(context.getParent() == null);
        logStartupProfileInfo(context);
    }
    
    // 添加单例Bean
    ConfigurableListableBeanFactory beanFactory = context.getBeanFactory();
    beanFactory.registerSingleton("springApplicationArguments", applicationArguments);
    
    if (printedBanner != null) {
        beanFactory.registerSingleton("springBootBanner", printedBanner);
    }
    
    // 设置懒加载
    if (beanFactory instanceof DefaultListableBeanFactory) {
        ((DefaultListableBeanFactory) beanFactory).setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding);
    }
    
    // 加载主资源
    if (this.lazyInitialization) {
        context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor());
    }
    
    // 加载所有资源
    Set<Object> sources = getAllSources();
    Assert.notEmpty(sources, "Sources must not be empty");
    
    // 加载主配置类
    load(context, sources.toArray(new Object[0]));
    
    // 发布上下文加载完成事件
    listeners.contextLoaded(context);
}

四、应用上下文的刷新

4.1 刷新上下文的核心方法

run()方法中调用的refreshContext(context)会触发应用上下文的刷新:

private void refreshContext(ConfigurableApplicationContext context) {
    // 如果需要刷新,则刷新上下文
    if (this.registerShutdownHook) {
        try {
            // 注册关闭钩子,确保应用优雅关闭
            context.registerShutdownHook();
        }
        catch (AccessControlException ex) {
            // 忽略安全管理器异常
        }
    }
    
    // 刷新上下文
    refresh(context);
}

protected void refresh(ConfigurableApplicationContext context) {
    // 调用标准的上下文刷新方法
    context.refresh();
}

4.2 AbstractApplicationContext的refresh()方法

refresh()方法是Spring应用上下文的核心方法,定义在AbstractApplicationContext中:

@Override
public void refresh() throws BeansException, IllegalStateException {
    synchronized (this.startupShutdownMonitor) {
        // 准备刷新上下文
        prepareRefresh();
        
        // 获取并刷新Bean工厂
        ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
        
        // 配置Bean工厂
        prepareBeanFactory(beanFactory);
        
        try {
            // 允许Bean工厂后置处理器对Bean定义进行后置处理
            postProcessBeanFactory(beanFactory);
            
            // 调用所有注册的Bean工厂后置处理器
            invokeBeanFactoryPostProcessors(beanFactory);
            
            // 注册所有Bean后置处理器
            registerBeanPostProcessors(beanFactory);
            
            // 初始化消息源
            initMessageSource();
            
            // 初始化应用事件多播器
            initApplicationEventMulticaster();
            
            // 初始化其他特殊Bean
            onRefresh();
            
            // 检查并注册监听器
            registerListeners();
            
            // 实例化所有剩余的(非懒加载)单例
            finishBeanFactoryInitialization(beanFactory);
            
            // 完成刷新,发布相应事件
            finishRefresh();
        }
        catch (BeansException ex) {
            if (logger.isWarnEnabled()) {
                logger.warn("Exception encountered during context initialization - " +
                        "cancelling refresh attempt: " + ex);
            }
            
            // 销毁已创建的单例以避免资源悬空
            destroyBeans();
            
            // 重置"active"标志
            cancelRefresh(ex);
            
            // 传播异常到调用者
            throw ex;
        }
        finally {
            // 重置公共缓存
            resetCommonCaches();
        }
    }
}

4.3 Bean工厂的准备与后置处理

在refresh()方法中,会准备并配置Bean工厂:

protected void prepareBeanFactory(ConfigurableListableBeanFactory beanFactory) {
    // 设置Bean工厂的类加载器
    beanFactory.setBeanClassLoader(getClassLoader());
    
    // 设置Bean表达式解析器
    beanFactory.setBeanExpressionResolver(new StandardBeanExpressionResolver(beanFactory.getBeanClassLoader()));
    
    // 添加属性编辑器注册器
    beanFactory.addPropertyEditorRegistrar(new ResourceEditorRegistrar(this, getEnvironment()));
    
    // 添加Bean后置处理器
    beanFactory.addBeanPostProcessor(new ApplicationContextAwareProcessor(this));
    
    // 设置忽略的自动装配接口
    beanFactory.ignoreDependencyInterface(EnvironmentAware.class);
    beanFactory.ignoreDependencyInterface(EmbeddedValueResolverAware.class);
    beanFactory.ignoreDependencyInterface(ResourceLoaderAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationEventPublisherAware.class);
    beanFactory.ignoreDependencyInterface(MessageSourceAware.class);
    beanFactory.ignoreDependencyInterface(ApplicationContextAware.class);
    
    // 注册可解析的依赖
    beanFactory.registerResolvableDependency(BeanFactory.class, beanFactory);
    beanFactory.registerResolvableDependency(ResourceLoader.class, this);
    beanFactory.registerResolvableDependency(ApplicationEventPublisher.class, this);
    beanFactory.registerResolvableDependency(ApplicationContext.class, this);
    
    // 添加ApplicationListener探测器
    beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(this));
    
    // 添加LoadTimeWeaver探测器
    if (beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
        beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
        // 设置临时类加载器进行类型匹配
        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }
    
    // 注册默认环境Bean
    if (!beanFactory.containsLocalBean(ENVIRONMENT_BEAN_NAME)) {
        beanFactory.registerSingleton(ENVIRONMENT_BEAN_NAME, getEnvironment());
    }
    
    // 注册系统属性和环境变量
    if (!beanFactory.containsLocalBean(SYSTEM_PROPERTIES_BEAN_NAME)) {
        beanFactory.registerSingleton(SYSTEM_PROPERTIES_BEAN_NAME, getEnvironment().getSystemProperties());
    }
    
    // 注册系统环境
    if (!beanFactory.containsLocalBean(SYSTEM_ENVIRONMENT_BEAN_NAME)) {
        beanFactory.registerSingleton(SYSTEM_ENVIRONMENT_BEAN_NAME, getEnvironment().getSystemEnvironment());
    }
}

4.4 Bean工厂后置处理器的调用

refresh()方法会调用所有注册的Bean工厂后置处理器:

protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    // 调用BeanDefinitionRegistryPostProcessor类型的处理器
    PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());
    
    // 检测LoadTimeWeaver并准备编织器
    if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {
        beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }
}

PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()方法会处理所有BeanDefinitionRegistryPostProcessor和BeanFactoryPostProcessor:

public static void invokeBeanFactoryPostProcessors(
        ConfigurableListableBeanFactory beanFactory, List<BeanFactoryPostProcessor> beanFactoryPostProcessors) {
    
    // 保存已经处理的处理器名称,避免重复处理
    Set<String> processedBeans = new HashSet<>();
    
    // 处理BeanDefinitionRegistry类型的工厂
    if (beanFactory instanceof BeanDefinitionRegistry) {
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) beanFactory;
        
        // 区分普通的BeanFactoryPostProcessor和BeanDefinitionRegistryPostProcessor
        List<BeanFactoryPostProcessor> regularPostProcessors = new ArrayList<>();
        List<BeanDefinitionRegistryPostProcessor> registryProcessors = new ArrayList<>();
        
        // 首先处理传入的BeanFactoryPostProcessor
        for (BeanFactoryPostProcessor postProcessor : beanFactoryPostProcessors) {
            if (postProcessor instanceof BeanDefinitionRegistryPostProcessor) {
                BeanDefinitionRegistryPostProcessor registryProcessor =
                        (BeanDefinitionRegistryPostProcessor) postProcessor;
                registryProcessor.postProcessBeanDefinitionRegistry(registry);
                registryProcessors.add(registryProcessor);
            }
            else {
                regularPostProcessors.add(postProcessor);
            }
        }
        
        // 现在,处理在上下文中定义的BeanDefinitionRegistryPostProcessor
        List<BeanDefinitionRegistryPostProcessor> currentRegistryProcessors = new ArrayList<>();
        
        // 首先,调用实现PriorityOrdered接口的BeanDefinitionRegistryPostProcessor
        String[] postProcessorNames =
                beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
        
        for (String ppName : postProcessorNames) {
            if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
                currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                processedBeans.add(ppName);
            }
        }
        
        // 排序并调用
        sortPostProcessors(currentRegistryProcessors, beanFactory);
        registryProcessors.addAll(currentRegistryProcessors);
        invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
        currentRegistryProcessors.clear();
        
        // 接下来,调用实现Ordered接口的BeanDefinitionRegistryPostProcessor
        postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
        
        for (String ppName : postProcessorNames) {
            if (!processedBeans.contains(ppName) && beanFactory.isTypeMatch(ppName, Ordered.class)) {
                currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                processedBeans.add(ppName);
            }
        }
        
        // 排序并调用
        sortPostProcessors(currentRegistryProcessors, beanFactory);
        registryProcessors.addAll(currentRegistryProcessors);
        invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
        currentRegistryProcessors.clear();
        
        // 最后,调用所有其他的BeanDefinitionRegistryPostProcessor,直到不再有新的处理器出现
        boolean reiterate = true;
        while (reiterate) {
            reiterate = false;
            postProcessorNames = beanFactory.getBeanNamesForType(BeanDefinitionRegistryPostProcessor.class, true, false);
            
            for (String ppName : postProcessorNames) {
                if (!processedBeans.contains(ppName)) {
                    currentRegistryProcessors.add(beanFactory.getBean(ppName, BeanDefinitionRegistryPostProcessor.class));
                    processedBeans.add(ppName);
                    reiterate = true;
                }
            }
            
            // 排序并调用
            sortPostProcessors(currentRegistryProcessors, beanFactory);
            registryProcessors.addAll(currentRegistryProcessors);
            invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry);
            currentRegistryProcessors.clear();
        }
        
        // 调用所有BeanDefinitionRegistryPostProcessor的postProcessBeanFactory方法
        invokeBeanFactoryPostProcessors(registryProcessors, beanFactory);
        
        // 调用普通的BeanFactoryPostProcessor
        invokeBeanFactoryPostProcessors(regularPostProcessors, beanFactory);
    }
    else {
        // 如果不是BeanDefinitionRegistry类型的工厂,直接调用所有BeanFactoryPostProcessor
        invokeBeanFactoryPostProcessors(beanFactoryPostProcessors, beanFactory);
    }
    
    // 现在,调用在上下文中定义的所有BeanFactoryPostProcessor
    String[] postProcessorNames =
            beanFactory.getBeanNamesForType(BeanFactoryPostProcessor.class, true, false);
    
    // 分离实现PriorityOrdered、Ordered和其他的BeanFactoryPostProcessor
    List<BeanFactoryPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
    List<String> orderedPostProcessorNames = new ArrayList<>();
    List<String> nonOrderedPostProcessorNames = new ArrayList<>();
    
    for (String ppName : postProcessorNames) {
        if (processedBeans.contains(ppName)) {
            // 跳过已处理的处理器
        }
        else if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
            priorityOrderedPostProcessors.add(beanFactory.getBean(ppName, BeanFactoryPostProcessor.class));
        }
        else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
            orderedPostProcessorNames.add(ppName);
        }
        else {
            nonOrderedPostProcessorNames.add(ppName);
        }
    }
    
    // 首先,调用实现PriorityOrdered接口的BeanFactoryPostProcessor
    sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
    invokeBeanFactoryPostProcessors(priorityOrderedPostProcessors, beanFactory);
    
    // 接下来,调用实现Ordered接口的BeanFactoryPostProcessor
    List<BeanFactoryPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
    for (String postProcessorName : orderedPostProcessorNames) {
        orderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
    }
    
    sortPostProcessors(orderedPostProcessors, beanFactory);
    invokeBeanFactoryPostProcessors(orderedPostProcessors, beanFactory);
    
    // 最后,调用所有其他的BeanFactoryPostProcessor
    List<BeanFactoryPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
    for (String postProcessorName : nonOrderedPostProcessorNames) {
        nonOrderedPostProcessors.add(beanFactory.getBean(postProcessorName, BeanFactoryPostProcessor.class));
    }
    
    invokeBeanFactoryPostProcessors(nonOrderedPostProcessors, beanFactory);
    
    // 清除缓存的合并Bean定义,因为后处理器可能修改了原始元数据
    beanFactory.clearMetadataCache();
}

4.5 Bean后置处理器的注册

refresh()方法会注册所有Bean后置处理器:

protected void registerBeanPostProcessors(ConfigurableListableBeanFactory beanFactory) {
    PostProcessorRegistrationDelegate.registerBeanPostProcessors(beanFactory, this);
}

PostProcessorRegistrationDelegate.registerBeanPostProcessors()方法会注册并排序所有Bean后置处理器:

public static void registerBeanPostProcessors(
        ConfigurableListableBeanFactory beanFactory, AbstractApplicationContext applicationContext) {
    
    // 获取所有Bean后置处理器的名称
    String[] postProcessorNames = beanFactory.getBeanNamesForType(BeanPostProcessor.class, true, false);
    
    // 注册BeanPostProcessorChecker,用于记录信息
    int beanProcessorTargetCount = beanFactory.getBeanPostProcessorCount() + 1 + postProcessorNames.length;
    beanFactory.addBeanPostProcessor(new BeanPostProcessorChecker(beanFactory, beanProcessorTargetCount));
    
    // 分离实现PriorityOrdered、Ordered和其他的BeanPostProcessor
    List<BeanPostProcessor> priorityOrderedPostProcessors = new ArrayList<>();
    List<BeanPostProcessor> internalPostProcessors = new ArrayList<>();
    List<String> orderedPostProcessorNames = new ArrayList<>();
    List<String> nonOrderedPostProcessorNames = new ArrayList<>();
    
    for (String ppName : postProcessorNames) {
        if (beanFactory.isTypeMatch(ppName, PriorityOrdered.class)) {
            BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
            priorityOrderedPostProcessors.add(pp);
            
            if (pp instanceof MergedBeanDefinitionPostProcessor) {
                internalPostProcessors.add(pp);
            }
        }
        else if (beanFactory.isTypeMatch(ppName, Ordered.class)) {
            orderedPostProcessorNames.add(ppName);
        }
        else {
            nonOrderedPostProcessorNames.add(ppName);
        }
    }
    
    // 首先,注册实现PriorityOrdered接口的BeanPostProcessor
    sortPostProcessors(priorityOrderedPostProcessors, beanFactory);
    registerBeanPostProcessors(beanFactory, priorityOrderedPostProcessors);
    
    // 接下来,注册实现Ordered接口的BeanPostProcessor
    List<BeanPostProcessor> orderedPostProcessors = new ArrayList<>(orderedPostProcessorNames.size());
    for (String ppName : orderedPostProcessorNames) {
        BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
        orderedPostProcessors.add(pp);
        
        if (pp instanceof MergedBeanDefinitionPostProcessor) {
            internalPostProcessors.add(pp);
        }
    }
    
    sortPostProcessors(orderedPostProcessors, beanFactory);
    registerBeanPostProcessors(beanFactory, orderedPostProcessors);
    
    // 然后,注册所有其他的BeanPostProcessor
    List<BeanPostProcessor> nonOrderedPostProcessors = new ArrayList<>(nonOrderedPostProcessorNames.size());
    for (String ppName : nonOrderedPostProcessorNames) {
        BeanPostProcessor pp = beanFactory.getBean(ppName, BeanPostProcessor.class);
        nonOrderedPostProcessors.add(pp);
        
        if (pp instanceof MergedBeanDefinitionPostProcessor) {
            internalPostProcessors.add(pp);
        }
    }
    
    registerBeanPostProcessors(beanFactory, nonOrderedPostProcessors);
    
    // 最后,注册所有内部BeanPostProcessor
    sortPostProcessors(internalPostProcessors, beanFactory);
    registerBeanPostProcessors(beanFactory, internalPostProcessors);
    
    // 添加ApplicationListenerDetector以检测ApplicationListener Bean
    beanFactory.addBeanPostProcessor(new ApplicationListenerDetector(applicationContext));
}

4.6 消息源与事件多播器的初始化

refresh()方法会初始化消息源和事件多播器:

protected void initMessageSource() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    
    // 检查是否存在用户定义的messageSource Bean
    if (beanFactory.containsLocalBean(MESSAGE_SOURCE_BEAN_NAME)) {
        this.messageSource = beanFactory.getBean(MESSAGE_SOURCE_BEAN_NAME, MessageSource.class);
        
        // 如果messageSource是HierarchicalMessageSource类型,设置父消息源
        if (this.parent != null && this.messageSource instanceof HierarchicalMessageSource) {
            HierarchicalMessageSource hms = (HierarchicalMessageSource) this.messageSource;
            if (hms.getParentMessageSource() == null) {
                // 只有在没有父消息源的情况下设置,避免覆盖用户设置
                hms.setParentMessageSource(getInternalParentMessageSource());
            }
        }
        
        if (logger.isTraceEnabled()) {
            logger.trace("Using MessageSource [" + this.messageSource + "]");
        }
    }
    else {
        // 创建并注册默认的DelegatingMessageSource
        DelegatingMessageSource dms = new DelegatingMessageSource();
        dms.setParentMessageSource(getInternalParentMessageSource());
        this.messageSource = dms;
        beanFactory.registerSingleton(MESSAGE_SOURCE_BEAN_NAME, this.messageSource);
        
        if (logger.isTraceEnabled()) {
            logger.trace("No '" + MESSAGE_SOURCE_BEAN_NAME + "' bean, using [" + this.messageSource + "]");
        }
    }
}

protected void initApplicationEventMulticaster() {
    ConfigurableListableBeanFactory beanFactory = getBeanFactory();
    
    // 检查是否存在用户定义的applicationEventMulticaster Bean
    if (beanFactory.containsLocalBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME)) {
        this.applicationEventMulticaster =
                beanFactory.getBean(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, ApplicationEventMulticaster.class);
        
        if (logger.isTraceEnabled()) {
            logger.trace("Using ApplicationEventMulticaster [" + this.applicationEventMulticaster + "]");
        }
    }
    else {
        // 创建并注册默认的SimpleApplicationEventMulticaster
        this.applicationEventMulticaster = new SimpleApplicationEventMulticaster(beanFactory);
        beanFactory.registerSingleton(APPLICATION_EVENT_MULTICASTER_BEAN_NAME, this.applicationEventMulticaster);
        
        if (logger.isTraceEnabled()) {
            logger.trace("No '" + APPLICATION_EVENT_MULTICASTER_BEAN_NAME + "' bean, using " +
                    "[" + this.applicationEventMulticaster.getClass().getSimpleName() + "]");
        }
    }
}

4.7 特殊Bean的初始化与刷新完成

refresh()方法会调用onRefresh()方法初始化特殊Bean:

protected void onRefresh() throws BeansException {
    // 对于Web应用上下文,这里会初始化Servlet上下文
    // 子类可以重写此方法以初始化其他特殊Bean
}

然后注册事件监听器并完成刷新:

protected void registerListeners() {
    // 注册静态指定的监听器
    for (ApplicationListener<?> listener : getApplicationListeners()) {
        getApplicationEventMulticaster().addApplicationListener(listener);
    }
    
    // 从Bean工厂中获取所有ApplicationListener Bean并注册
    String[] listenerBeanNames = getBeanNamesForType(ApplicationListener.class, true, false);
    for (String listenerBeanName : listenerBeanNames) {
        getApplicationEventMulticaster().addApplicationListenerBean(listenerBeanName);
    }
    
    // 发布早期应用事件
    Set<ApplicationEvent> earlyEventsToProcess = this.earlyApplicationEvents;
    this.earlyApplicationEvents = null;
    
    if (earlyEventsToProcess != null) {
        for (ApplicationEvent earlyEvent : earlyEventsToProcess) {
            getApplicationEventMulticaster().multicastEvent(earlyEvent);
        }
    }
}

protected void finishBeanFactoryInitialization(ConfigurableListableBeanFactory beanFactory) {
    // 初始化类型转换器
    if (beanFactory.containsBean(TYPE_CONVERTER_BEAN_NAME)) {
        beanFactory.addPropertyEditorRegistrar(
                new BeanFactoryTypeConverter(beanFactory.getBean(TYPE_CONVERTER_BEAN_NAME, TypeConverter.class)));
    }
    
    // 初始化LoadTimeWeaverAware Bean
    if (!beanFactory.getTempClassLoader().getClass().getName().contains("ContextTypeMatchClassLoader")) {
        beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));
        beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));
    }
    
    // 冻结所有Bean定义,表明注册的Bean定义不会被修改或进一步后处理
    beanFactory.freezeConfiguration();
    
    // 实例化所有剩余的(非懒加载)单例
    beanFactory.preInstantiateSingletons();
}

protected void finishRefresh() {
    // 清除资源缓存
    clearResourceCaches();
    
    // 初始化生命周期处理器
    initLifecycleProcessor();
    
    // 注册生命周期处理器的回调
    getLifecycleProcessor().onRefresh();
    
    // 发布ContextRefreshedEvent事件
    publishEvent(new ContextRefreshedEvent(this));
    
    // Participate in LiveBeansView MBean, if active.
    LiveBeansView.registerApplicationContext(this);
}

五、嵌入式服务器的启动

5.1 嵌入式服务器的创建与启动

在应用上下文刷新完成后,SpringBoot会启动嵌入式服务器:

// 在ServletWebServerApplicationContext类中
@Override
protected void onRefresh() {
    super.onRefresh();
    try {
        // 创建并启动Web服务器
        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) {
        // 获取ServletWebServerFactory
        ServletWebServerFactory factory = getWebServerFactory();
        
        // 创建Web服务器
        this.webServer = factory.getWebServer(getSelfInitializer());
    }
    else if (servletContext != null) {
        try {
            // 如果已经有Servlet上下文,初始化它
            getSelfInitializer().onStartup(servletContext);
        }
        catch (ServletException ex) {
            throw new ApplicationContextException("Cannot initialize servlet context", ex);
        }
    }
    
    // 初始化属性源
    initPropertySources();
}

protected ServletWebServerFactory getWebServerFactory() {
    // 获取ServletWebServerFactory Bean名称
    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));
    }
    
    // 获取并返回ServletWebServerFactory Bean
    return getBeanFactory().getBean(beanNames[0], ServletWebServerFactory.class);
}

5.2 ServletWebServerFactory的实现

SpringBoot提供了多种嵌入式服务器的实现,如Tomcat、Jetty和Undertow:

// TomcatServletWebServerFactory类
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
    // 创建Tomcat实例
    Tomcat tomcat = new Tomcat();
    
    // 配置基本目录
    File baseDir = (this.baseDirectory != null) ? this.baseDirectory : createTempDir("tomcat");
    tomcat.setBaseDir(baseDir.getAbsolutePath());
    
    // 配置连接器
    Connector connector = new Connector(this.protocol);
    tomcat.getService().addConnector(connector);
    customizeConnector(connector);
    tomcat.setConnector(connector);
    tomcat.getHost().setAutoDeploy(false);
    
    // 配置Engine和Host
    configureEngine(tomcat.getEngine());
    for (Connector additionalConnector : this.additionalTomcatConnectors) {
        tomcat.getService().addConnector(additionalConnector);
    }
    
    // 准备上下文
    prepareContext(tomcat.getHost(), initializers);
    
    // 返回Web服务器包装器
    return getTomcatWebServer(tomcat);
}

// JettyServletWebServerFactory类
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
    // 创建Server实例
    Server server = new Server();
    
    // 配置连接器
    ServerConnector connector = new ServerConnector(server);
    if (this.port != null) {
        connector.setPort(this.port);
    }
    server.addConnector(connector);
    
    // 配置处理程序
    configureHandler(server);
    
    // 配置Session
    if (this.session != null && this.session.getTimeout() != null) {
        Duration timeout = this.session.getTimeout();
        long seconds = timeout.getSeconds();
        if (seconds >= 0) {
            this.sessionTimeout = (int) seconds;
        }
    }
    
    // 准备上下文
    ServletContextHandler contextHandler = prepareContext(server, initializers);
    
    // 返回Web服务器包装器
    return new JettyWebServer(server, this.autoStart, contextHandler);
}

// UndertowServletWebServerFactory类
@Override
public WebServer getWebServer(ServletContextInitializer... initializers) {
    // 创建Undertow构建器
    Undertow.Builder builder = createBuilder(getPort());
    
    // 创建Servlet容器
    DeploymentInfo servletBuilder = getDeploymentInfo(initializers);
    DeploymentManager manager = defaultContainer.addDeployment(servletBuilder);
    manager.deploy();
    
    // 获取Servlet上下文
    try {
        ServletContext context = manager.start();
        configureContext(context, initializers);
    }
    catch (ServletException ex) {
        throw new WebServerException("Unable to start embedded Undertow server", ex);
    }
    
    // 添加Servlet部署到构建器
    builder.deploy(servletBuilder);
    
    // 创建并启动Undertow服务器
    Undertow undertow = builder.build();
    startDaemonAwaitThread(undertow);
    
    // 返回Web服务器包装器
    return new UndertowWebServer(undertow, this.autoStart, getPort() >= 0);
}

5.3 嵌入式服务器的配置与定制

嵌入式服务器的配置可以通过多种方式进行定制:

// 通过配置属性定制
@Configuration
public class WebServerCustomizationConfiguration {
    
    @Bean
    public WebServerFactoryCustomizer<TomcatServletWebServerFactory> tomcatCustomizer() {
        return factory -> {
            // 定制Tomcat服务器
            factory.addConnectorCustomizers(connector -> {
                // 配置连接器
                connector.setPort(8080);
                connector.setMaxThreads(200);
                connector.setMaxConnections(8192);
            });
            
            // 配置Tomcat引擎
            factory.addEngineCustomizers(engine -> {
                engine.setRealm(new MemoryRealm());
            });
            
            // 配置上下文
            factory.addContextCustomizers(context -> {
                context.setSessionTimeout(30);
            });
        };
    }
    
    @Bean
    public WebServerFactoryCustomizer<JettyServletWebServerFactory> jettyCustomizer() {
        return factory -> {
            // 定制Jetty服务器
            factory.addServerCustomizers(server -> {
                // 配置Jetty服务器
                server.setStopAtShutdown(true);
                server.setStopTimeout(5000);
            });
            
            // 配置连接器
            factory.addConnectorCustomizers(connector -> {
                connector.setIdleTimeout(30000);
            });
        };
    }
    
    @Bean
    public WebServerFactoryCustomizer<UndertowServletWebServerFactory> undertowCustomizer() {
        return factory -> {
            // 定制Undertow服务器
            factory.addBuilderCustomizers(builder -> {
                // 配置Undertow构建器
                builder.setIoThreads(4);
                builder.setWorkerThreads(20);
            });
            
            // 添加Servlet容器定制器
            factory.addDeploymentInfoCustomizers(deploymentInfo -> {
                deploymentInfo.setSessionTimeout(30);
            });
        };
    }
}

六、应用事件与监听器机制

6.1 应用事件的类型与发布

SpringBoot应用在启动过程中会发布多种事件:

// SpringApplicationRunListeners类
public void starting(DefaultBootstrapContext bootstrapContext, @Nullable Class<?> mainApplicationClass) {
    doWithListeners("spring.boot.application.starting", (listener) -> listener.starting(bootstrapContext),
            (step) -> {
                if (mainApplicationClass != null) {
                    step.tag("mainApplicationClass", mainApplicationClass.getName());
                }
                return step;
            });
}

public void environmentPrepared(DefaultBootstrapContext bootstrapContext, ConfigurableEnvironment environment) {
    doWithListeners("spring.boot.application.environment-prepared",
            (listener) -> listener.environmentPrepared(bootstrapContext, environment),
            (step) -> step.tag("configLocations", getConfigLocations(environment)));
}

public void contextPrepared(ConfigurableApplicationContext context) {
    doWithListeners("spring.boot.application.context-prepared",
            (listener) -> listener.contextPrepared(context), null);
}

public void contextLoaded(ConfigurableApplicationContext context) {
    doWithListeners("spring.boot.application.context-loaded",
            (listener) -> listener.contextLoaded(context), null);
}

public void started(ConfigurableApplicationContext context, Duration timeTaken) {
    doWithListeners("spring.boot.application.started",
            (listener) -> listener.started(context, timeTaken), null);
}

public void running(ConfigurableApplicationContext context, Duration timeTaken) {
    doWithListeners("spring.boot.application.running",
            (listener) -> listener.running(context, timeTaken), null);
}

public void failed(ConfigurableApplicationContext context, Throwable exception) {
    doWithListeners("spring.boot.application.failed",
            (listener) -> listener.failed(context, exception), null);
}

6.2 事件监听器的注册与处理

应用事件监听器可以通过多种方式注册:

// 通过META-INF/spring.factories注册
# Application Listeners
org.springframework.context.ApplicationListener=\
org.springframework.boot.ClearCachesApplicationListener,\
org.springframework.boot.builder.ParentContextCloserApplicationListener,\
org.springframework.boot.context.FileEncodingApplicationListener,\
org.springframework.boot.context.config.AnsiOutputApplicationListener,\
org.springframework.boot.context.config.ConfigFileApplicationListener,\
org.springframework.boot.context.config.DelegatingApplicationListener,\
org.springframework.boot.context.logging.ClasspathLoggingApplicationListener,\
org.springframework.boot.context.logging.LoggingApplicationListener,\
org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener

// 通过@Bean方法注册
@Configuration
public class MyApplicationConfiguration {
    
    @Bean
    public ApplicationListener<ApplicationReadyEvent> readyListener() {
        return event -> {
            // 应用准备就绪后的处理逻辑
            System.out.println("Application is ready!");
        };
    }
    
    @Bean
    public ApplicationListener<ContextRefreshedEvent> refreshedListener() {
        return event -> {
            // 上下文刷新后的处理逻辑
            System.out.println("Context is refreshed!");
        };
    }
}

// 通过@Component和@EventListener注解注册
@Component
public class MyApplicationListener {
    
    @EventListener
    public void handleContextRefreshed(ContextRefreshedEvent event) {
        // 处理上下文刷新事件
        System.out.println("Context refreshed: " + event.getApplicationContext());
    }
    
    @EventListener
    public void handleApplicationReady(ApplicationReadyEvent event) {
        // 处理应用就绪事件
        System.out.println("Application ready: " + event.getApplicationContext());
    }
    
    @EventListener
    public void handleApplicationFailed(ApplicationFailedEvent event) {
        // 处理应用失败事件
        System.out.println("Application failed: " + event.getException().getMessage());
    }
}

6.3 事件处理的顺序与优先级

事件监听器可以通过实现Ordered接口或使用@Order注解来指定处理顺序:

// 实现Ordered接口
@Component
public class MyOrderedListener implements ApplicationListener<ApplicationReadyEvent>, Ordered {
    
    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        // 处理应用就绪事件
        System.out.println("MyOrderedListener: Application is ready!");
    }
    
    @Override
    public int getOrder() {
        // 返回优先级,值越小优先级越高
        return Ordered.HIGHEST_PRECEDENCE + 10;
    }
}

// 使用@Order注解
@Component
@Order(Ordered.HIGHEST_PRECEDENCE + 20)
public class MyAnnotatedListener implements ApplicationListener<ApplicationReadyEvent> {
    
    @Override
    public void onApplicationEvent(ApplicationReadyEvent event) {
        // 处理应用就绪事件
        System.out.println("MyAnnotatedListener: Application is ready!");
    }
}

七、自动配置机制解析

7.1 自动配置的核心原理

SpringBoot的自动配置是基于条件注解实现的:

// EnableAutoConfiguration注解
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
    
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
    
    /**
     * 排除特定的自动配置类
     */
    Class<?>[] exclude() default {};
    
    /**
     * 排除特定的自动配置类名
     */
    String[] excludeName() default {};
}

AutoConfigurationImportSelector会从META-INF/spring.factories文件中加载所有自动配置类:

// AutoConfigurationImportSelector类
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return NO_IMPORTS;
    }
    
    // 获取自动配置元数据
    AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
            .loadMetadata(this.beanClassLoader);
    
    // 获取自动配置类
    AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
            annotationMetadata);
    
    // 返回自动配置类名数组
    return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}

protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
        AnnotationMetadata annotationMetadata) {
    if (!isEnabled(annotationMetadata)) {
        return EMPTY_ENTRY;
    }
    
    // 获取注解属性
    AnnotationAttributes attributes = getAttributes(annotationMetadata);
    
    // 加载候选自动配置类
    List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
    
    // 移除重复项
    configurations = removeDuplicates(configurations);
    
    // 获取需要排除的配置类
    Set<String> exclusions = getExclusions(annotationMetadata, attributes);
    
    // 检查排除的配置类
    checkExcludedClasses(configurations, exclusions);
    
    // 移除需要排除的配置类
    configurations.removeAll(exclusions);
    
    // 应用自动配置过滤器
    configurations = filter(configurations, autoConfigurationMetadata);
    
    // 触发自动配置导入事件
    fireAutoConfigurationImportEvents(configurations, exclusions);
    
    // 返回自动配置条目
    return new AutoConfigurationEntry(configurations, exclusions);
}

7.2 条件注解的应用

SpringBoot使用多种条件注解来控制自动配置的生效条件:

// ConditionalOnClass注解
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)
public @interface ConditionalOnClass {
    
    /**
     * 指定类必须存在于类路径上
     */
    Class<?>[] value() default {};
    
    /**
     * 指定类名必须存在于类路径上
     */
    String[] name() default {};
}

// ConditionalOnMissingBean注解
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnBeanCondition.class)
public @interface ConditionalOnMissingBean {
    
    /**
     * 指定必须不存在的Bean类型
     */
    Class<?>[] value() default {};
    
    /**
     * 指定必须不存在的Bean名称
     */
    String[] name() default {};
    
    /**
     * 指定查找Bean的搜索范围
     */
    SearchStrategy search() default SearchStrategy.ALL;
}

// ConditionalOnProperty注解
@Target({ ElementType.TYPE, ElementType.METHOD })
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnPropertyCondition.class)
public @interface ConditionalOnProperty {
    
    /**
     * 指定属性名前缀
     */
    String prefix() default "";
    
    /**
     * 指定属性名
     */
    String[] name() default {};
    
    /**
     * 指定属性值必须等于的值
     */
    String havingValue() default "";
    
    /**
     * 指定当属性不存在时是否匹配
     */
    boolean matchIfMissing() default false;
}

7.3 自动配置类的结构

自动配置类通常包含多个@Bean方法,并使用条件注解控制这些方法的生效条件:

// DataSourceAutoConfiguration类
@Configuration(proxyBeanMethods = false)
@ConditionalOnClass({ DataSource.class, EmbeddedDatabaseType.class })
@EnableConfigurationProperties(DataSourceProperties.class)
@Import({ DataSourcePoolMetadataProvidersConfiguration.class, DataSourceInitializationConfiguration.class })
public class DataSourceAutoConfiguration {
    
    @Bean
    @ConditionalOnMissingBean
    public DataSourceInitializerInvoker dataSourceInitializerInvoker(
            DataSource dataSource, DataSourceProperties properties) {
        // 创建数据源初始化调用器
        return new DataSourceInitializerInvoker(dataSource, properties);
    }
    
    @Configuration(proxyBeanMethods = false)
    @Conditional(EmbeddedDatabaseCondition.class)
    @ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")
    static class EmbeddedDatabaseConfiguration {
        
        @Bean
        @ConditionalOnMissingBean
        public DataSource dataSource(DataSourceProperties properties) {
            // 创建嵌入式数据源
            return properties.initializeDataSourceBuilder().build();
        }
    }
    
    @Configuration(proxyBeanMethods = false)
    @Conditional(PooledDataSourceCondition.class)
    @ConditionalOnMissingBean(type = "io.r2dbc.spi.ConnectionFactory")
    @Import({ Hikari.class, Tomcat.class, Dbcp2.class, OracleUcp.class,
            Generic.class, DataSourceJmxConfiguration.class })
    static class PooledDataSourceConfiguration {
        
        @Bean
        @ConditionalOnMissingBean
        public DataSource dataSource(DataSourceProperties properties) {
            // 创建连接池数据源
            return properties.initializeDataSourceBuilder().build();
        }
    }
    
    // 其他内部配置类和方法...
}

八、命令行参数处理

8.1 命令行参数的解析

SpringBoot应用启动时会解析命令行参数:

// DefaultApplicationArguments类
public DefaultApplicationArguments(String... args) {
    Assert.notNull(args, "Args must not be null");
    this.source = new Source(args);
    this.args = args;
}

private static class Source extends SimpleCommandLinePropertySource {
    
    Source(String[] args) {
        super(args);
    }
    
    @Override
    public String[] getPropertyNames() {
        synchronized (this) {
            if (this.propertyNames == null) {
                // 初始化属性名数组
                this.propertyNames = extractPropertyNames(getOptionNames());
            }
            return this.propertyNames.clone();
        }
    }
    
    private String[] extractPropertyNames(Set<String> optionNames) {
        // 提取属性名
        return StringUtils.toStringArray(optionNames);
    }
    
    @Override
    public boolean containsProperty(String name) {
        return getOptionNames().contains(name);
    }
    
    @Override
    public Object getProperty(String name) {
        return getOptionValues(name);
    }
}

8.2 命令行参数的优先级

命令行参数的优先级高于其他配置源:

// ConfigFileApplicationListener类
private void addCommandLineProperties(ConfigurableEnvironment environment, String[] args) {
    if (args.length > 0) {
        // 创建命令行属性源
        String name = CommandLinePropertySource.COMMAND_LINE_PROPERTY_SOURCE_NAME;
        CommandLinePropertySource<?> source = new SimpleCommandLinePropertySource(args);
        
        // 移除默认的命令行属性源
        if (environment.getPropertySources().contains(name)) {
            environment.getPropertySources().replace(name, source);
        }
        else {
            // 添加命令行属性源,优先级最高
            environment.getPropertySources().addFirst(source);
        }
    }
}

8.3 自定义命令行参数处理器

开发者可以通过实现ApplicationRunner或CommandLineRunner接口来处理命令行参数:

// 实现CommandLineRunner接口
@Component
public class MyCommandLineRunner implements CommandLineRunner {
    
    @Override
    public void run(String... args) throws Exception {
        // 处理命令行参数
        System.out.println("Command line arguments:");
        
        for (String arg : args) {
            System.out.println("- " + arg);
        }
        
        // 可以在这里执行应用启动后的初始化操作
    }
}

// 实现ApplicationRunner接口
@Component
public class MyApplicationRunner implements ApplicationRunner {
    
    @Override
    public void run(ApplicationArguments args) throws Exception {
        // 处理应用参数
        System.out.println("Application arguments:");
        
        // 获取非选项参数
        List<String> nonOptionArgs = args.getNonOptionArgs();
        System.out.println("Non-option arguments: " + nonOptionArgs);
        
        // 获取选项参数
        Set<String> optionNames = args.getOptionNames();
        
        for (String optionName : optionNames) {
            List<String> optionValues = args.getOptionValues(optionName);
            System.out.println("Option '" + optionName + "': " + optionValues);
        }
        
        // 可以在这里执行应用启动后的初始化操作
    }
}

九、应用启动错误处理

9.1 启动异常的捕获与处理

SpringBoot应用启动过程中的异常会被捕获并处理:

// SpringApplication类
private void handleRunFailure(ConfigurableApplicationContext context, Throwable ex,
        SpringApplicationRunListeners listeners) {
    try {
        try {
            // 发布应用失败事件
            if (listeners != null) {
                listeners.failed(context, ex);
            }
        }
        catch (Throwable ex2) {
            logger.error("Error handling failed event", ex2);
        }
        
        // 记录启动失败日志
        if (context != null) {
            context.close();
        }
        
        // 报告异常
        reportFailure(getExceptionReporters(context), ex);
    }
    catch (Throwable ex3) {
        logger.error("Unable to close ApplicationContext", ex3);
    }
    
    // 重新抛出异常
    ReflectionUtils.rethrowRuntimeException(ex);
}

private void reportFailure(Collection<SpringBootExceptionReporter> exceptionReporters, Throwable failure) {
    try {
        // 尝试使用异常报告器报告异常
        if (!exceptionReporters.isEmpty()) {
            boolean reported = false;
            
            for (SpringBootExceptionReporter reporter : exceptionReporters) {
                if (reporter.reportException(failure)) {
                    reported = true;
                    break;
                }
            }
            
            // 如果没有报告器处理异常,记录错误日志
            if (!reported && logger.isErrorEnabled()) {
                logger.error("Application run failed", failure);
            }

9.1 启动异常的捕获与处理

handleRunFailure方法中,除了发布应用失败事件和关闭上下文外,reportFailure方法负责将异常信息以更友好的方式呈现。Spring Boot通过SpringBootExceptionReporter接口的实现类来完成这一任务,常见的实现包括DefaultErrorPageExceptionReporter等。

private void reportFailure(Collection<SpringBootExceptionReporter> exceptionReporters, Throwable failure) {
    try {
        if (!exceptionReporters.isEmpty()) {
            boolean reported = false;
            for (SpringBootExceptionReporter reporter : exceptionReporters) {
                if (reporter.reportException(failure)) {
                    reported = true;
                    break;
                }
            }
            if (!reported && logger.isErrorEnabled()) {
                logger.error("Application run failed", failure);
            }
        }
    } catch (Throwable ex) {
        if (logger.isDebugEnabled()) {
            logger.debug("Error reporting failure", ex);
        }
        if (logger.isErrorEnabled()) {
            logger.error("Application run failed", failure);
        }
    }
}

上述代码中,会依次尝试让每个SpringBootExceptionReporter处理异常。如果有任何一个报告器处理成功(即reportException方法返回true),则认为异常已被妥善处理;若所有报告器都无法处理,且日志级别允许记录错误,就会直接记录异常信息到日志中。

DefaultErrorPageExceptionReporter为例,它主要用于Web应用场景,会尝试生成一个包含错误信息的HTML页面。在生成错误页面时,它会获取异常的详细堆栈信息、错误类型等内容,并结合Spring Boot默认的错误页面模板,将这些信息渲染到页面上。当应用启动过程中出现异常时,若满足一定条件(如在Web环境下),用户访问应用时就能看到这个详细的错误页面,而不是晦涩难懂的堆栈跟踪信息。

9.2 异常报告器的工作机制

FailureAnalyzers类在异常分析过程中扮演着关键角色。它负责从类路径中加载所有实现了FailureAnalyzer接口的类,并按照一定顺序对异常进行分析。

public class FailureAnalyzers {
    private final List<FailureAnalyzer> analyzers;
    private final Log logger;

    public FailureAnalyzers(ClassLoader classLoader, Log logger) {
        this(classLoader, null, logger);
    }

    public FailureAnalyzers(ClassLoader classLoader, List<FailureAnalyzer> analyzers, Log logger) {
        this.logger = logger;
        List<FailureAnalyzer> loadedAnalyzers = (analyzers != null) ? new ArrayList<>(analyzers)
                : SpringFactoriesLoader.loadFactories(FailureAnalyzer.class, classLoader);
        AnnotationAwareOrderComparator.sort(loadedAnalyzers);
        this.analyzers = Collections.unmodifiableList(loadedAnalyzers);
    }

    public FailureAnalysis analyze(Throwable failure) {
        Throwable rootFailure = findRootCause(failure);
        if (rootFailure != null) {
            for (FailureAnalyzer analyzer : this.analyzers) {
                try {
                    FailureAnalysis analysis = analyzer.analyze(rootFailure);
                    if (analysis != null) {
                        return analysis;
                    }
                } catch (Throwable ex) {
                    if (this.logger.isDebugEnabled()) {
                        String message = ex.getMessage();
                        message = (message != null) ? message : "no error message";
                        this.logger.debug("FailureAnalyzer " + analyzer.getClass().getName()
                                + " failed to analyze cause: " + message, ex);
                    }
                }
            }
        }
        return null;
    }

    private Throwable findRootCause(Throwable failure) {
        Throwable root = failure;
        Throwable cause;
        while ((cause = root.getCause()) != null) {
            root = cause;
        }
        return root;
    }
}

首先,在构造函数中,FailureAnalyzers通过SpringFactoriesLoaderMETA-INF/spring.factories文件中加载所有的FailureAnalyzer实现类,并使用AnnotationAwareOrderComparator进行排序,确保按照优先级顺序处理。

analyze方法中,它会先找到异常的根本原因,然后依次让每个FailureAnalyzer尝试分析该根本原因。每个FailureAnalyzer实现类都有自己的分析逻辑,比如判断异常类型是否是自己能处理的,如果是,则根据异常信息生成一个FailureAnalysis对象,该对象包含了错误描述和解决建议。一旦有FailureAnalyzer成功生成FailureAnalysis对象,就会立即返回,不再继续尝试其他分析器。

例如,DataSourceUnavailableFailureAnalyzer专门用于分析数据库连接相关的异常。当应用启动时无法连接到数据库,抛出SQLException等异常时,DataSourceUnavailableFailureAnalyzer会判断异常是否属于数据库连接失败相关类型。如果是,它会从异常信息中提取出数据库URL、用户名等关键信息,并生成包含错误原因(如“无法连接到指定的数据库URL”)和解决建议(如“检查数据库URL是否正确,确保数据库服务已启动”)的FailureAnalysis对象,帮助开发者快速定位和解决问题。

9.3 常见启动错误及解决方案

9.3.1 Bean定义冲突

当Spring容器中存在多个相同类型或名称的Bean定义时,就会引发Bean定义冲突问题。主要有两种常见情况:

  • 类型冲突:多个@Component@Service@Repository等注解标注的类,或通过@Bean方法定义的Bean,它们的类型相同。例如,定义了两个UserService类:
@Service
public class UserService {
    // 业务逻辑
}

@Service
public class AnotherUserService implements UserService {
    // 另一种实现的业务逻辑
}

当在其他类中通过@Autowired注入UserService时,Spring容器无法确定应该注入哪一个,就会抛出NoUniqueBeanDefinitionException异常。

  • 名称冲突:即使Bean类型不同,但如果通过@Bean方法显式指定了相同的Bean名称,也会产生冲突。比如:
@Configuration
public class AppConfig {
    @Bean("commonBean")
    public Object bean1() {
        return new Object();
    }

    @Bean("commonBean")
    public Object bean2() {
        return new Object();
    }
}

此时会抛出BeanDefinitionOverrideException异常。

解决方案

  • 使用@Primary注解:在多个同类型Bean中,将其中一个标注为@Primary,表示该Bean为首选注入对象。例如,在上述UserService的例子中,可以将其中一个UserService类标注为@Primary
@Service
@Primary
public class UserService {
    // 业务逻辑
}

这样在注入UserService时,Spring会优先选择带有@Primary注解的Bean。

  • 使用@Qualifier注解:通过@Qualifier指定具体的Bean名称或限定条件。在注入时明确指出要使用的Bean,如:
@Service
public class UserService {
    // 业务逻辑
}

@Service
public class AnotherUserService {
    // 业务逻辑
}

@Service
public class UserController {
    private final UserService userService;

    @Autowired
    public UserController(@Qualifier("userService") UserService userService) {
        this.userService = userService;
    }
}

这里通过@Qualifier("userService")指定了要注入的具体UserService Bean。

  • 检查并移除重复定义:仔细检查代码,删除不必要的重复Bean定义,确保每个Bean在容器中具有唯一性。
  • 设置spring.main.allow-bean-definition-overriding=true:在application.propertiesapplication.yml中设置该属性,允许Bean定义覆盖。但这种方式可能会导致一些难以排查的问题,不建议在生产环境中使用,仅用于开发调试阶段临时解决冲突。
9.3.2 端口占用

当Spring Boot应用使用嵌入式服务器(如Tomcat、Jetty、Undertow)启动时,会尝试绑定到指定的端口。如果该端口已经被其他进程占用,就会抛出类似于Address already in use: bind的异常。例如,默认情况下,Spring Boot的Web应用会尝试绑定到8080端口,如果8080端口已被其他Web服务占用,应用启动就会失败。

解决方案

  • 修改端口配置:在application.propertiesapplication.yml文件中,通过server.port属性指定其他未被占用的端口。如在application.properties中设置:
server.port=8081

或在application.yml中设置:

server:
  port: 8081
  • 关闭占用端口的进程:通过系统命令(如在Windows下使用netstat -ano | findstr :端口号查看占用端口的进程ID,然后使用taskkill /pid 进程ID /f关闭进程;在Linux下使用lsof -i :端口号查看进程,使用kill -9 进程ID关闭进程)找到并关闭占用该端口的应用程序。
  • 使用随机端口:将server.port设置为0,此时Spring Boot会随机分配一个可用的端口。不过这种方式在需要明确访问端口的场景下不太适用,一般用于测试环境或对端口没有固定要求的情况。
9.3.3 自动配置冲突

Spring Boot的自动配置机制会根据类路径下的依赖和配置属性,自动配置应用所需的各种组件。但当多个自动配置类都尝试配置同一组件,或者自动配置类与开发者自定义配置产生冲突时,就会出现自动配置冲突问题。例如,同时引入了两个不同的数据访问框架依赖,它们各自的自动配置类都想配置数据源相关组件,就可能导致冲突。

解决方案

  • 使用@SpringBootApplicationexclude属性:在@SpringBootApplication注解中,通过exclude属性排除不需要的自动配置类。比如,不想使用Spring Boot默认的数据库连接池自动配置,可以这样写:
@SpringBootApplication(exclude = DataSourceAutoConfiguration.class)
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}
  • 使用条件注解控制:利用@ConditionalOnMissingBean@ConditionalOnClass@ConditionalOnProperty等条件注解,明确指定Bean创建的条件,避免冲突。例如,只有当类路径中不存在特定的数据源实现类时,才创建自定义的数据源Bean:
@Configuration
public class MyDataSourceConfig {
    @Bean
    @ConditionalOnMissingBean(DataSource.class)
    public DataSource customDataSource() {
        // 自定义数据源创建逻辑
    }
}
  • 检查并排除冲突依赖:查看项目的依赖树,找出导致冲突的依赖项,通过exclusions排除不必要的依赖。例如,在Maven项目中:
<dependency>
    <groupId>com.examplegroupId>
    <artifactId>example-dependencyartifactId>
    <version>1.0.0version>
    <exclusions>
        <exclusion>
            <groupId>冲突依赖的groupIdgroupId>
            <artifactId>冲突依赖的artifactIdartifactId>
        exclusion>
    exclusions>
dependency>
9.3.4 类路径问题

类路径问题主要包括缺少必要的依赖类,或者依赖类的版本不兼容,从而导致ClassNotFoundExceptionNoClassDefFoundError等异常。例如,项目中使用了某个第三方库的特定功能,但没有引入对应的依赖;或者引入的多个依赖之间存在版本冲突,导致某些类无法正确加载。

解决方案

  • 检查并添加缺失依赖:仔细查看异常信息中提示缺失的类,确定对应的依赖库,然后在项目的构建文件(如Maven的pom.xml或Gradle的build.gradle)中添加正确的依赖。比如,在Maven项目中添加Jackson依赖:
<dependency>
    <groupId>com.fasterxml.jackson.coregroupId>
    <artifactId>jackson-databindartifactId>
    <version>2.13.0version>
dependency>
  • 解决依赖版本冲突:使用dependencyManagement统一管理依赖版本,避免不同依赖引入不一致的版本。例如,在Maven的pom.xml中:
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>com.examplegroupId>
            <artifactId>common-dependencyartifactId>
            <version>1.0.0version>
        dependency>
    dependencies>
dependencyManagement>

或者通过exclusions排除冲突版本的依赖,再手动引入正确版本的依赖。

  • 清理并重建项目:有时类路径问题可能是由于项目构建缓存导致的,可以尝试清理项目的构建缓存(如在IDEA中使用File -> Invalidate Caches / Restart),然后重新构建项目,确保类路径正确更新。
9.3.5 配置错误

配置错误涵盖多种情况,包括配置文件格式错误、属性值类型不匹配、属性名拼写错误、配置层级结构混乱等。在Spring Boot中,配置信息通常从application.propertiesapplication.yml等文件加载,并通过@ConfigurationProperties等机制绑定到Java类中。

例如,在application.yml文件中,错误地缩进配置层级:

spring:
  datasource:
url: jdbc:mysql://localhost:3306/mydb
username: root
password: 123456

这里urlusernamepassword的缩进与datasource同级,不符合YAML格式要求,会导致解析错误。

又如,属性值类型不匹配的情况:

myapp:
  max-connections: "ten"

假设MyAppConfig类中max-connections对应的属性是int类型,那么将字符串"ten"绑定到int类型属性时就会失败。

解决方案

  • 检查配置文件格式:确保application.propertiesapplication.yml文件的格式正确。对于application.yml,严格遵循YAML的缩进规则,每个层级的缩进保持一致;对于application.properties,属性名和值之间使用正确的=分隔,且不包含非法字符。可以借助IDE的语法检查功能,及时发现格式错误。
  • 验证属性值类型:仔细核对配置文件中的属性值类型是否与配置类中定义的属性类型相匹配。如果需要进行类型转换,确保转换的可行性。例如,将字符串转换为Date类型时,可以使用@DateTimeFormat注解进行格式化转换。
  • 修正属性名拼写:认真检查属性名的拼写,确保与配置类中的@ConfigurationProperties注解的prefix和属性名一致。对于复杂的配置类,可以使用IDE的重构功能修改属性名,避免手动修改导致的拼写错误。
  • 使用配置验证:利用@Validated注解对配置类进行验证。在配置类的属性上添加验证注解,如@NotNull@Min@Max等,在配置绑定完成后,Spring会自动对配置进行验证,若不满足验证条件,则会抛出异常,提示配置错误信息。例如:
import javax.validation.constraints.NotBlank;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Positive;

import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.validation.annotation.Validated;

@ConfigurationProperties(prefix = "myapp")
@Validated
public class MyAppConfig {
    @NotBlank
    private String name;
    @NotNull
    @Positive
    private int age;

    // 省略getter和setter
}

这样当配置中的name为空字符串,或者age为负数或null时,就会在启动时抛出验证异常,帮助开发者快速定位配置错误。

9.3.6 数据库连接失败

数据库连接失败是Spring Boot应用启动时常见的问题之一,主要原因包括数据库URL错误、用户名密码错误、数据库驱动版本不兼容、数据库服务未启动等。

例如,数据库URL中的IP地址、端口号、数据库名称写错:

spring:
  datasource:
    url: jdbc:mysql://192.168.1.100:3307/mydb
    username: root
    password: 123456

如果实际的数据库IP是192.168.1.101,或者端口是3306,就会导致连接失败。

又或者使用的数据库驱动版本与数据库不兼容,比如使用较旧的MySQL驱动连接新版本的MySQL数据库,可能会出现不支持的协议等问题。

解决方案

  • 检查数据库配置:仔细核对application.propertiesapplication.yml中的数据库连接配置,确保数据库URL、用户名、密码正确无误。可以尝试使用数据库客户端工具(如Navicat、DBeaver)连接数据库,验证配置信息是否能正常连接。
  • 更新数据库驱动:根据所使用的数据库版本,选择合适的数据库驱动版本。在Maven或Gradle项目中,更新对应的依赖版本。例如,对于MySQL数据库,在Maven项目中:

解决方案

  • 更新数据库驱动:根据所使用的数据库版本,选择合适的数据库驱动版本。在Maven或Gradle项目中,更新对应的依赖版本。例如,对于MySQL数据库,在Maven项目中:
<dependency>
    <groupId>mysqlgroupId>
    <artifactId>mysql-connector-javaartifactId>
    <version>8.0.26version> 
dependency>

如果使用的是HikariCP连接池(Spring Boot默认),确保其版本与数据库驱动兼容。例如,HikariCP 4.x版本与MySQL Connector/J 8.x兼容。

  • 检查数据库服务状态:确认数据库服务已启动且正常运行。可以通过系统服务管理工具(如Windows服务、Linux的systemctl命令)检查数据库服务状态。例如,在Linux上检查MySQL服务状态:
systemctl status mysql

如果服务未启动,使用相应命令启动服务:

systemctl start mysql
  • 验证网络连接:确保应用服务器能够访问数据库服务器,检查网络连接是否正常,防火墙是否放行数据库端口。例如,MySQL默认使用3306端口,需要确保该端口在防火墙中是开放的。

  • 配置连接属性:在数据库URL中添加必要的连接参数,如字符集、时区等。例如,对于MySQL:

spring:
  datasource:
    url: jdbc:mysql://localhost:3306/mydb?useUnicode=true&characterEncoding=utf-8&useSSL=false&serverTimezone=UTC

其中,useUnicodecharacterEncoding确保字符集正确,useSSL设置是否使用SSL连接,serverTimezone设置时区以避免时间相关问题。

  • 使用连接测试工具:可以编写简单的测试代码,单独测试数据库连接,排除Spring Boot应用本身的问题。例如:
import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.SQLException;

public class DatabaseConnectionTest {
    public static void main(String[] args) {
        String url = "jdbc:mysql://localhost:3306/mydb";
        String username = "root";
        String password = "123456";

        try (Connection connection = DriverManager.getConnection(url, username, password)) {
            System.out.println("数据库连接成功!");
        } catch (SQLException e) {
            System.out.println("数据库连接失败:" + e.getMessage());
            e.printStackTrace();
        }
    }
}
9.3.7 组件扫描问题

Spring Boot通过组件扫描(Component Scanning)自动发现并注册应用中的组件(如@Component@Service@Repository等注解标注的类)。当组件扫描配置不正确时,可能导致组件未被正确注册,从而在运行时出现NoSuchBeanDefinitionException等异常。

常见原因

  • 组件类不在@SpringBootApplication(或@ComponentScan)指定的扫描包路径下。默认情况下,Spring Boot会扫描@SpringBootApplication所在类的同级包及其子包。
  • 自定义了@ComponentScan注解,排除了需要扫描的包或类。
  • 使用了错误的包名或类名。

解决方案

  • 调整扫描路径:确保组件类位于@SpringBootApplication所在类的同级包或子包中,或者通过@ComponentScan显式指定扫描路径。例如:
@SpringBootApplication
@ComponentScan(basePackages = "com.example.myapp")
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}

这里指定了扫描com.example.myapp包及其子包。

  • 检查排除配置:如果使用了@ComponentScanexcludeFilters属性,确保没有错误地排除了需要的组件。例如:
@ComponentScan(excludeFilters = {
    @ComponentScan.Filter(type = FilterType.REGEX, pattern = "com.example.myapp.excluded.*")
})

确保正则表达式或其他过滤条件不会意外排除关键组件。

  • 使用明确的组件注册:对于无法通过组件扫描自动发现的组件,可以通过@Configuration类和@Bean方法手动注册。例如:
@Configuration
public class AppConfig {
    @Bean
    public MyService myService() {
        return new MyService();
    }
}
9.3.8 依赖注入问题

依赖注入是Spring框架的核心特性之一,但在实际应用中可能会遇到各种问题,如循环依赖、注入类型不匹配、找不到合适的Bean等。

常见问题及解决方案

  • 循环依赖:当两个或多个Bean之间存在循环依赖关系时,Spring容器在创建这些Bean时会陷入无限循环,导致BeanCurrentlyInCreationException异常。例如:
@Service
public class ServiceA {
    @Autowired
    private ServiceB serviceB;

    // 构造函数或方法使用serviceB
}

@Service
public class ServiceB {
    @Autowired
    private ServiceA serviceA;

    // 构造函数或方法使用serviceA
}

解决方案

  • 重构代码:重新设计类的结构,消除循环依赖。例如,提取公共逻辑到一个新的类中,让两个类都依赖于这个新类。
  • 使用Setter注入或字段注入:将构造函数注入改为Setter注入或字段注入,允许Spring在Bean创建后再设置依赖。不过这种方法只是掩盖了设计问题,并没有真正解决它。
  • 使用@Lazy注解:在其中一个依赖上使用@Lazy注解,延迟加载该依赖,直到实际使用时才创建。例如:
@Service
public class ServiceA {
    private final ServiceB serviceB;

    @Autowired
    public ServiceA(@Lazy ServiceB serviceB) {
        this.serviceB = serviceB;
    }
}
  • 注入类型不匹配:当注入的目标类型与实际Bean的类型不匹配时,会抛出BeanNotOfRequiredTypeException异常。例如:
@Service
public class MyService {
    // 业务逻辑
}

@Service
public class MyController {
    @Autowired
    private AnotherService myService; // 类型不匹配,AnotherService与MyService不兼容
}

解决方案

  • 检查注入点的类型声明,确保与实际Bean的类型一致。

  • 如果需要注入接口的实现类,确保实现类正确实现了该接口,并且在容器中注册为该接口类型的Bean。

  • 找不到合适的Bean:当没有找到符合条件的Bean时,会抛出NoSuchBeanDefinitionException异常。例如:

@Service
public class MyService {
    @Autowired
    private MyRepository repository; // 容器中没有MyRepository类型的Bean
}

解决方案

  • 确保MyRepository类被正确标注为@Repository或其他组件注解,并且在组件扫描路径内。
  • 检查是否存在拼写错误或包路径问题。
  • 如果是自定义的Bean,确保在配置类中通过@Bean方法正确注册。
9.3.9 版本冲突问题

在Spring Boot应用中,版本冲突是常见的问题,特别是当引入多个第三方依赖时,可能会导致依赖库的不同版本之间存在兼容性问题。例如,不同的依赖可能引用了同一库的不同版本,导致类加载冲突或方法调用异常。

常见症状

  • NoClassDefFoundErrorClassNotFoundException:表示找不到某个类,可能是因为依赖版本不一致导致某些类被覆盖或缺失。
  • NoSuchMethodError:表示调用了不存在的方法,通常是由于依赖库版本不兼容,方法签名发生了变化。
  • 运行时异常:如IllegalArgumentExceptionNullPointerException等,可能是由于不同版本的库行为不一致导致的。

解决方案

  • 查看依赖树:使用Maven或Gradle的依赖树命令查看项目的依赖结构,找出冲突的依赖。例如,在Maven中使用:
mvn dependency:tree

在Gradle中使用:

gradle dependencies
  • 统一依赖版本:使用dependencyManagement(Maven)或resolutionStrategy(Gradle)统一管理依赖版本,确保同一库只使用一个版本。例如,在Maven的pom.xml中:
<dependencyManagement>
    <dependencies>
        <dependency>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-coreartifactId>
            <version>5.3.18version>
        dependency>
    dependencies>
dependencyManagement>
  • 排除冲突依赖:在依赖声明中排除冲突的传递依赖。例如:
<dependency>
    <groupId>com.examplegroupId>
    <artifactId>example-libraryartifactId>
    <version>1.0.0version>
    <exclusions>
        <exclusion>
            <groupId>org.springframeworkgroupId>
            <artifactId>spring-coreartifactId>
        exclusion>
    exclusions>
dependency>
  • 使用Spring Boot的依赖管理:Spring Boot的spring-boot-starter-parent提供了一组默认的依赖版本,这些版本经过了测试,相互兼容。尽量使用Spring Boot推荐的版本,避免手动指定版本号,除非必要。
9.3.10 初始化Bean失败

当Bean的初始化过程中发生错误时,会导致应用启动失败。常见的原因包括:

  • Bean的构造函数或初始化方法抛出异常。
  • Bean依赖的其他组件未正确初始化。
  • Bean的配置属性无效或不完整。

解决方案

  • 检查初始化方法:确保@PostConstruct注解的方法或InitializingBean接口的afterPropertiesSet方法没有抛出异常。例如:
@Service
public class MyService implements InitializingBean {
    @Override
    public void afterPropertiesSet() throws Exception {
        // 初始化逻辑,确保不会抛出异常
    }
}
  • 检查依赖组件:确保Bean依赖的其他组件已经正确初始化。可以通过日志或调试工具查看初始化顺序,找出依赖关系中的问题。

  • 验证配置属性:确保注入到Bean中的配置属性有效且完整。可以使用@Validated注解和JSR-303验证注解对配置属性进行验证。

  • 使用@DependsOn注解:如果Bean之间存在隐式依赖关系,可以使用@DependsOn注解显式指定依赖顺序。例如:

@Service
@DependsOn("dataSource")
public class MyService {
    // 业务逻辑
}

这里确保dataSource Bean在MyService之前初始化。

9.4 调试技巧

当遇到Spring Boot应用启动失败的问题时,可以使用以下调试技巧来快速定位和解决问题:

9.4.1 启用调试日志

application.propertiesapplication.yml中添加以下配置,启用Spring的调试日志:

logging.level.root=INFO
logging.level.org.springframework=DEBUG

或在application.yml中:

logging:
  level:
    root: INFO
    org.springframework: DEBUG

这样可以获得更详细的启动过程日志,帮助分析问题所在。

9.4.2 使用断点调试

在IDE中设置断点,逐步调试应用的启动过程。特别是在SpringApplication.run()方法和关键组件的初始化方法中设置断点,观察变量值和执行流程,找出异常发生的位置。

9.4.3 使用--debug参数

在启动应用时添加--debug参数,可以启用调试模式,输出更多的调试信息:

java -jar myapp.jar --debug
9.4.4 禁用自动配置

如果怀疑某个自动配置类导致了问题,可以使用@SpringBootApplicationexclude属性暂时禁用该自动配置类,观察应用是否能正常启动:

@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class, HibernateJpaAutoConfiguration.class})
public class MyApplication {
    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class, args);
    }
}
9.4.5 使用条件断点

在复杂的应用中,可以使用条件断点,只在满足特定条件时暂停执行。例如,在异常抛出的代码行设置条件断点,当特定的异常类型或条件满足时触发断点。

9.4.6 检查环境配置

确保应用运行的环境配置正确,包括系统变量、环境变量、配置文件等。可以使用以下代码在应用启动时打印当前的环境配置:

@SpringBootApplication
public class MyApplication {
    public static void main(String[] args) {
        ConfigurableApplicationContext context = SpringApplication.run(MyApplication.class, args);
        Environment env = context.getEnvironment();
        
        System.out.println("Active profiles: " + Arrays.asList(env.getActiveProfiles()));
        System.out.println("Application properties: " + env.getPropertySources());
    }
}

9.5 总结

Spring Boot应用启动失败可能由多种原因导致,包括配置错误、依赖问题、组件扫描问题、数据库连接问题等。通过深入理解Spring Boot的启动机制和常见错误模式,结合有效的调试技巧,可以快速定位和解决启动问题。

在排查问题时,建议按照以下步骤进行:

  1. 查看详细的错误日志,确定异常类型和错误信息。
  2. 分析可能的原因,如配置错误、依赖冲突、组件缺失等。
  3. 使用调试工具和技巧,逐步定位问题所在。
  4. 针对具体问题,采取相应的解决方案,如修正配置、调整依赖版本、修改代码结构等。
  5. 验证解决方案的有效性,确保应用能够正常启动。

通过不断积累经验和掌握有效的排查方法,可以更加高效地解决Spring Boot应用启动过程中遇到的各种问题。

你可能感兴趣的:(SpringBoot框架详解,spring,boot,后端,java,spring,restful)