1. 首页
  2. 后端

10个点介绍SpringBoot3工作流程与核心组件源码解析

  10个点介绍SpringBoot3工作流程与核心组件源码解析

==============================

Spring Boot 的重要性 V 哥就不多说了,在学习的过程中,如果我们只知道它用来简化 SSM 的开发,用用 API,那是万万不够的,V 哥的逻辑是,先会用,再深入,这个企业应用的重量级框架非常值得你好好深入研究,今天的文章,将会帮助你梳理 SpringBoot3的工作流程,核心组件分析等内容:

一、SpringBoot3工作流程

1. 启动阶段:

  • 当Spring Boot应用启动时,SpringApplication类会被实例化。这个类负责创建和配置应用上下文,并启动应用。
  • SpringApplicationRunListeners 被用来通知监听器在启动的不同阶段(如开始、环境准备、上下文初始化、应用启动完成)。

2. 环境配置:

  • SpringEnvironment 接口的实现类(如StandardEnvironment)被用来加载外部配置(application.properties 或 application.yml)和系统属性。
  • PropertySources 用于管理不同来源的配置属性。

3. 上下文初始化:

  • ApplicationContextInitializer 接口的实现类可以在这里介入,用于在上下文创建之前进行自定义初始化。
  • ConfigurableEnvironment 允许在上下文中添加额外的 PropertySource 或 BeanDefinition。

4. Bean定义加载:

  • BeanDefinitionRegistryPostProcessor 允许在所有的 BeanDefinition 加载完成后进行修改或添加新的 BeanDefinition。
  • @Configuration 注解的类被扫描并处理,这些类中的 @Bean 方法会被调用,生成相应的 BeanDefinition。

5. 依赖注入:

  • BeanFactory 或 ApplicationContext 用于实例化和组装 Bean,处理依赖注入。
  • @Autowired 和其他相关的注解(如 @Inject)被处理,以实现依赖注入。

6. 后处理:

  • BeanPostProcessor 允许在 Bean 初始化前后进行额外的处理。
  • @PostConstruct 注解的方法会在 Bean 初始化完成后执行。

7. 启动完成:

  • 所有的 Bean 都已经被加载和初始化,ApplicationRunner 和 CommandLineRunner 接口的实现类会被执行。
  • 应用完全启动,可以处理请求。

二、相关核心类源码分析

以下是一些核心类的简化源码示例和解释:

1.SpringApplication

SpringApplication是Spring Boot应用程序的入口点:

public class SpringApplication extends SpringBootServletInitializer {
    private ConfigurableEnvironment environment = new StandardEnvironment();
    private SpringApplicationRunListeners listeners = new SpringApplicationRunListeners();

    public static ConfigurableApplicationContext run(Class<?>... primarySources) {
        return new SpringApplication(primarySources).run();
    }

    // ... 其他方法
}
  • SpringApplication 是启动Spring Boot应用的主类,它封装了应用的启动流程。
  • 它初始化环境、监听器,并负责创建和刷新应用上下文。

SpringApplication 逻辑过程

实例化 SpringApplication:

public static ConfigurableApplicationContext run(Class<?>... primarySources) {
    return new SpringApplication(primarySources).run();
}
  • 这是SpringApplication的静态工厂方法,用于创建一个新的SpringApplication实例,并传入主要的应用程序类(primarySources)。
  • 这个方法最终会调用run方法来启动应用程序。

配置环境:

Environment prepareEnvironment(MutablePropertySources propertySources, ConfigurableEnvironment environment) {
    // 配置环境变量、系统属性等
}
  • 在启动过程中,prepareEnvironment方法会被调用,用于准备应用程序的环境,包括加载系统属性和环境变量。

设置监听器:

SpringApplicationRunListeners listeners = new SpringApplicationRunListeners();
listeners.starting();
  • SpringApplicationRunListeners用于在应用程序的不同生命周期阶段发布事件。
  • 在启动时,会通知所有注册的监听器应用程序即将启动。

加载应用程序上下文:

ConfigurableApplicationContext createApplicationContext() {
    // 创建并配置ApplicationContext
}
  • createApplicationContext方法负责创建应用程序上下文(ApplicationContext),这是Spring容器的核心。
  • 上下文会被配置为使用SpringApplication中定义的属性源和其他配置。

刷新上下文:

void refresh(ContextRefreshScope refreshScope) {
    // 刷新ApplicationContext
}
  • refresh方法用于刷新上下文,这包括注册Bean后处理器、初始化所有的Bean以及触发Bean的生命周期回调。

运行应用程序:

public ConfigurableApplicationContext run() {
    // 启动应用程序
}
  • run方法是启动Spring Boot应用程序的核心方法。
  • 它首先调用prepareEnvironment,然后创建和刷新上下文,最后调用listeners.started()通知监听器应用程序已启动。

调用 CommandLineRunner 和 ApplicationRunner:

private void callRunners(ApplicationContext context, ApplicationArguments args) {
    // 调用Runner接口的实现
}
  • 在上下文刷新后,callRunners方法会被调用,用于执行实现了CommandLineRunner或ApplicationRunner接口的组件。

关闭应用程序:

public void stop() {
    // 关闭应用程序
}
  • stop方法用于优雅地关闭应用程序,它会通知监听器应用程序即将关闭,并关闭上下文。

SpringApplication类是Spring Boot应用程序的启动类,它负责整个应用程序的启动流程。从创建环境、配置资源、初始化Spring容器,到调用启动后的回调,SpringApplication提供了一个简单而强大的方式来管理Spring Boot应用程序的生命周期。通过理解SpringApplication的工作原理,开发者可以更好地控制和定制Spring Boot应用程序的行为。

2.ApplicationContextInitializer

ApplicationContextInitializer 是 Spring Boot 中的一个接口,它允许开发者在 Spring 应用上下文初始化之前进行自定义的初始化操作。这个接口对于执行一些预启动任务非常有用,比如添加额外的 BeanDefinition、设置特定的环境属性、或者注册自定义的 BeanFactoryPostProcessor。

ApplicationContextInitializer 接口定义

public interface ApplicationContextInitializer<C extends ConfigurableApplicationContext> {
    void initialize(C applicationContext);
}

逻辑过程和源码分析

实现 ApplicationContextInitializer 接口:

开发者需要创建一个类实现 ApplicationContextInitializer 接口,并实现 initialize 方法。在这个方法中,可以对传入的 applicationContext 进行操作,比如添加 BeanDefinition 或者修改环境配置。

public class MyCustomApplicationContextInitializer implements ApplicationContextInitializer<ConfigurableApplicationContext> {
    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        // 在这里可以对 applicationContext 进行操作
    }
}

注册 ApplicationContextInitializer:

在 Spring Boot 应用启动时,SpringApplication 类会查找所有的 ApplicationContextInitializer 实现,并注册它们。这通常通过 SpringFactoriesLoader 类来完成,它会加载 META-INF/spring.factories 文件中指定的 ApplicationContextInitializer 实现。

private static List<ApplicationContextInitializer<?>> getInitializers(Class<?>... types) {
    List<ApplicationContextInitializer<?>> initializers = new ArrayList<>();
    // 从 spring.factories 加载 ApplicationContextInitializer 实现
    for (Class<?> type : types) {
        String[] initializers = SpringFactoriesLoader.loadFactoryNames(type, ApplicationContextInitializer.class.getClassLoader());
        for (String initializer : initializers) {
            try {
                initializers.add((ApplicationContextInitializer) Class.forName(initializer).newInstance());
            } catch (Exception ex) {
                throw new IllegalArgumentException("Cannot instantiate ApplicationContextInitializer", ex);
            }
        }
    }
    return initializers;
}

执行 ApplicationContextInitializer:

当 Spring 应用上下文创建并准备好初始化时,SpringApplication 会遍历所有注册的 ApplicationContextInitializer 实现,并调用它们的 initialize 方法。

private void prepareContext(ConfigurableApplicationContext context, SpringApplicationRunListeners listeners) {
    // ... 其他初始化代码 ...
    getInitializers(context).forEach(initializer -> initializer.initialize(context));
    // ... 其他初始化代码 ...
}

在 prepareContext 方法中,getInitializers 方法会获取所有可用的 ApplicationContextInitializer 实现,并通过 forEach 循环依次调用它们的 initialize 方法。

自定义初始化逻辑:

在 initialize 方法中,开发者可以添加自定义的 BeanDefinition,或者修改 Environment 对象中的属性。这些更改将在后续的上下文刷新过程中生效。

    @Override
    public void initialize(ConfigurableApplicationContext applicationContext) {
        BeanDefinitionRegistry registry = (BeanDefinitionRegistry) applicationContext.getBeanFactory();
        // 添加自定义的 BeanDefinition
        BeanDefinition beanDefinition = new RootBeanDefinition(MyCustomBean.class);
        registry.registerBeanDefinition("myCustomBean", beanDefinition);
    }

ApplicationContextInitializer 是 Spring Boot 提供的一个扩展点,允许开发者在 Spring 应用上下文初始化之前进行自定义的初始化操作。通过实现这个接口并注册到应用中,可以在上下文创建过程中注入自定义的逻辑,从而增强或修改应用的行为。这种机制为 Spring Boot 应用提供了高度的可定制性。

3.BeanDefinitionRegistryPostProcessor

BeanDefinitionRegistryPostProcessor 是 Spring 框架中的一个扩展点,它允许开发者在 BeanFactory 完成所有的 BeanDefinition 加载之后,但在 Bean 实例化之前,进行自定义的处理。这个接口可以用来修改现有的 BeanDefinition、添加新的 BeanDefinition 或者删除某些 BeanDefinition。

BeanDefinitionRegistryPostProcessor 接口定义

public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor {
    void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException;
}

逻辑过程和源码分析

实现 BeanDefinitionRegistryPostProcessor 接口:

开发者需要创建一个类实现 BeanDefinitionRegistryPostProcessor 接口,并实现 postProcessBeanDefinitionRegistry 方法。在这个方法中,可以对 BeanDefinitionRegistry 进行操作,比如添加、修改或删除 BeanDefinition。

public class MyBeanDefinitionRegistryPostProcessor implements BeanDefinitionRegistryPostProcessor {
    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        // 在这里可以对 BeanDefinitionRegistry 进行操作
    }
}

注册 BeanDefinitionRegistryPostProcessor:

与 ApplicationContextInitializer 类似,BeanDefinitionRegistryPostProcessor 也可以通过 spring.factories 文件进行注册。Spring 容器会在启动过程中自动检测并注册所有的 BeanDefinitionRegistryPostProcessor 实现。

执行 BeanDefinitionRegistryPostProcessor:

在 Spring 容器的初始化过程中,一旦所有的 BeanDefinition 被加载完成,BeanDefinitionRegistryPostProcessor 的 postProcessBeanDefinitionRegistry 方法就会被调用。

public class BeanDefinitionRegistryPostProcessorBeanFactory extends ConfigurableListableBeanFactory {
    // ... 其他代码 ...

    @Override
    protected void processBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        // ... 其他代码 ...

        // 调用所有 BeanDefinitionRegistryPostProcessor 实现
        for (BeanDefinitionRegistryPostProcessor postProcessor : getBeanDefinitionRegistryPostProcessors()) {
            postProcessor.postProcessBeanDefinitionRegistry(registry);
        }
    }

    // ... 其他代码 ...
}

在 processBeanDefinitionRegistry 方法中,BeanDefinitionRegistryPostProcessorBeanFactory 会遍历所有注册的 BeanDefinitionRegistryPostProcessor 实现,并调用它们的 postProcessBeanDefinitionRegistry 方法。

自定义处理逻辑:

在 postProcessBeanDefinitionRegistry 方法中,开发者可以编写自定义的逻辑来修改 BeanDefinition。例如,可以修改 Bean 的作用域、改变 Bean 的类、或者添加额外的属性等。

    @Override
    public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
        // 修改现有的 BeanDefinition
        BeanDefinition myBeanDefinition = registry.getBeanDefinition("myBean");
        myBeanDefinition.setScope(ConfigurableBeanFactory.SCOPE_PROTOTYPE);

        // 添加新的 BeanDefinition
        BeanDefinition newBeanDefinition = new RootBeanDefinition(MyNewBean.class);
        registry.registerBeanDefinition("newBean", newBeanDefinition);
    }

BeanDefinitionRegistryPostProcessor 提供了一个强大的钩子,允许开发者在 Spring 容器的 BeanDefinition 加载阶段进行自定义处理。这个接口的实现可以在 BeanFactory 完成 BeanDefinition 的加载后,但在 Bean 实例化之前,对 BeanDefinition 进行修改或扩展。

通过使用 BeanDefinitionRegistryPostProcessor,开发者可以改变 Spring 容器的行为,或者根据特定的需求定制 Bean 的定义。这种机制为 Spring 容器的灵活性和可扩展性提供了重要的支持。

4.BeanFactory

BeanFactory 是 Spring 框架的核心接口,它负责实例化、配置和管理 bean 的生命周期。BeanFactory 是 Spring 容器的底层接口,而 ApplicationContext 是基于 BeanFactory 扩展的高级接口,提供了更多的功能和便利性。

BeanFactory 接口定义

BeanFactory 接口定义了一系列用于访问和操作 bean 的方法:

public interface BeanFactory {
    Object getBean(String name) throws BeansException;
    <T> T getBean(Class<T> requiredType) throws BeansException;
    <T> T getBean(String name, Class<T> requiredType) throws BeansException;
    Object getBean(String name, Object... args) throws BeansException;
    <T> T getBean(String name, Class<T> requiredType, Object... args) throws BeansException;
    // ... 其他方法 ...
}

逻辑过程和源码分析

BeanDefinition 的加载:

在 Spring 容器启动时,BeanFactory 首先需要加载和解析所有的 BeanDefinition 配置信息。这些信息可以来自 XML 文件、注解、Java 配置类等。

public class XmlBeanFactory extends DefaultListableBeanFactory {
    public XmlBeanFactory() {
        super();
    }

    public void loadBeanDefinitions(InputSource inputSource) throws BeanDefinitionStoreException {
        // 使用 XMLBeanDefinitionReader 加载 BeanDefinition
        XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this);
        reader.loadBeanDefinitions(inputSource);
    }
}

Bean 的实例化:

当调用 getBean 方法请求一个 bean 时,BeanFactory 会根据 BeanDefinition 创建 bean 实例。这个过程可能包括使用构造器注入、属性注入、方法注入等方式来配置 bean。

public class DefaultSingletonBeanRegistry extends SimpleListableBeanFactory {
    // ... 其他代码 ...

    protected Object createBean(String beanName) throws BeansException {
        // 获取 BeanDefinition
        BeanDefinition beanDefinition = getMergedLocalBeanDefinition(beanName);
        // 使用 BeanDefinition 来实例化和配置 bean
        Object bean = beanDefinition.getInstance();
        // 初始化 bean,包括调用初始化方法、应用 BeanPostProcessors 等
        initializeBean(beanName, bean);
        return bean;
    }
}

依赖注入:

BeanFactory 负责处理 bean 之间的依赖关系。它会解析 BeanDefinition 中定义的依赖,并递归地获取所需的依赖 bean。

public class DefaultListableBeanFactory extends BeanFactory {
    // ... 其他代码 ...

    protected void populateBean(String beanName, RootBeanDefinition beanDefinition, Object bean) {
        // 处理属性依赖注入
        for (MutablePropertyValues mpv : beanDefinition.getPropertyValues().getPropertyValues()) {
            String propertyName = mpv.getName();
            ObjectPropertyValue pv = mpv.getValue();
            if (pv.isNested()) {
                // 处理嵌套属性
            } else {
                // 注入依赖的值
                applyProperty PropertyValueUtils.getPropertyValue(bean, propertyName, pv);
            }
        }
    }
}

BeanPostProcessor 的应用:
BeanFactory 允许开发者通过 BeanPostProcessor 接口来自定义 bean 的初始化前后处理。这些处理器可以在 bean 初始化前后执行额外的逻辑。

public class DefaultListableBeanFactory extends BeanFactory {
    // ... 其他代码 ...

    protected void invokeAwareInterfaces(Object bean) {
        // 调用 bean 的 Aware 接口方法,例如 BeanFactoryAware
        if (bean instanceof BeanFactoryAware) {
            ((BeanFactoryAware) bean).setBeanFactory(BeanFactory);
        }
        // ... 其他 Aware 接口的调用
    }
}

Bean 生命周期管理:

BeanFactory 管理 bean 的整个生命周期,包括创建、初始化、使用和销毁。

    public class DefaultSingletonBeanRegistry extends SimpleListableBeanFactory {
        // ... 其他代码 ...

        protected void destroySingleton(String beanName) {
            // 销毁单例 bean
            Object bean = getSingleton(beanName);
            if (bean != null) {
                destroyBean(beanName, (DefaultSingletonBean) bean);
            }
        }
    }

BeanFactory 是 Spring 框架中用于管理 bean 生命周期的接口。通过 BeanFactory,Spring 容器可以加载 bean 的定义、创建和配置 bean 实例、处理依赖注入、应用 BeanPostProcessor 以及管理 bean 的整个生命周期。

BeanFactory 的实现类(如 XmlBeanFactory、DefaultListableBeanFactory 等)提供了具体的逻辑来实现这些功能。理解 BeanFactory 的工作原理对于深入掌握 Spring 框架至关重要,它为开发者提供了强大的控制能力来自定义和优化应用程序的行为。

5.AutowiredAnnotationBeanPostProcessor

AutowiredAnnotationBeanPostProcessor 是 Spring 框架中的一个 BeanPostProcessor 实现,它的作用是处理带有 @Autowired 注解的字段、setter 方法和构造器的自动装配。这个组件确保了在 bean 创建过程中,所有标记了 @Autowired 的依赖都会被自动注入。

AutowiredAnnotationBeanPostProcessor 接口定义

BeanPostProcessor 接口定义了两个方法:postProcessBeforeInitialization 和 postProcessAfterInitialization,它们分别在 bean 初始化前后被调用。

public interface BeanPostProcessor {
    Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException;
    Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException;
}

AutowiredAnnotationBeanPostProcessor 逻辑过程和源码分析

实例化 AutowiredAnnotationBeanPostProcessor:

当 Spring 容器启动时,AutowiredAnnotationBeanPostProcessor 会被实例化,并注册为 BeanPostProcessor。

public class AutowiredAnnotationBeanPostProcessor implements BeanPostProcessor, PriorityOrdered, ApplicationContextAware {
    private final InjectionMetadataSource metadataSource = new InjectionMetadataSource();
    private ApplicationContext applicationContext;

    public AutowiredAnnotationBeanPostProcessor() {
        // 构造函数中初始化一些成员变量
    }

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        this.applicationContext = applicationContext;
    }

    // ... 其他方法 ...
}

处理前置初始化:
当 bean 的实例化完成后,postProcessBeforeInitialization 方法会被调用。这个方法通常用于处理字段注入。

@Override
public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException {
    InjectionMetadata metadata = metadataSource.getMetadata(bean.getClass());
    return metadata.inject(bean, beanName, null);
}

在这个方法中,InjectionMetadata 被用来获取当前 bean 类的注入元数据,然后调用 inject 方法来处理字段注入。

处理后置初始化:

当 bean 的初始化方法(如 @PostConstruct 注解的方法或 init-method 指定的方法)执行完成后,postProcessAfterInitialization 方法会被调用。这个方法通常用于处理 setter 方法注入和构造器注入。

@Override
public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
    InjectionMetadata metadata = metadataSource.getMetadata(bean.getClass());
    return metadata.inject(bean, beanName, null);
}

同样地,InjectionMetadata 被用来获取注入元数据,并通过 inject 方法处理依赖注入。

依赖注入的执行:

InjectionMetadata 类负责存储和处理依赖注入的元数据。它会在第一次处理注入时解析依赖,并在后续的注入过程中重用这些信息。

    public class InjectionMetadata implements Serializable {
        private final MethodInjector[] methodInjectors;
        private final FieldInjector[] fieldInjectors;
        // ... 其他成员变量 ...

        public void inject(Object target, @Nullable String beanName, @Nullable Object[] args) throws BeansException {
            // 执行字段注入
            for (FieldInjector fieldInjector : this.fieldInjectors) {
                fieldInjector.inject(target, beanName, args);
            }
            // 执行方法注入
            for (MethodInjector methodInjector : this.methodInjectors) {
                methodInjector.inject(target, beanName, args);
            }
        }
    }

在 inject 方法中,会遍历所有的字段注入器 (FieldInjector) 和方法注入器 (MethodInjector),然后执行实际的注入操作。

AutowiredAnnotationBeanPostProcessor 是 Spring 框架中处理 @Autowired 注解的关键组件。它通过实现 BeanPostProcessor 接口,在 bean 初始化前后处理依赖注入。
这个组件的工作过程��及到解析元数据、处理字段注入、方法注入和构造器注入。通过这种方式,Spring 确保了所有的依赖都能够被正确地注入到 bean 中,从而实现了控制反转和依赖注入的设计理念。理解 AutowiredAnnotationBeanPostProcessor 的工作原理有助于深入理解 Spring 框架的自动装配机制。

以上是Spring Boot 3的一些核心类和工作流程的概述。实际的源码会更加复杂,包含更多的细节和扩展点。要深入了解,建议直接查看Spring Boot的源码,并结合官方文档进行学习。

三、条件化配置

Spring Boot 3引入了条件化配置的概念,允许开发者根据不同的条件来配置和启用或禁用特定的Bean。这是通过@Conditional注解来实现的。

条件化配置是Spring框架提供的一种机制,允许开发者根据不同的条件来配置和启用或禁用特定的Bean。这是通过@Conditional注解来实现的,它使得配置更加灵活,可以根据环境、系统属性、类路径等条件来控制Bean的创建。

1.理解 @Conditional 注解

@Conditional 是一个特殊的注解,可以用于类或者方法上。当用于类上时,整个类作为一个Bean的条件化;当用于方法上时,方法的返回值决定了某个Bean的创建。

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Conditional {
    Class<?>[] value();
}

2.条件化配置的逻辑过程

定义条件注解:

开发者可以创建自定义的注解,继承自SpringCondition类,实现条件逻辑。

public class MyCustomCondition extends SpringCondition {
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // 定义匹配逻辑
        return true; // 条件满足,Bean将被创建
    }
}

使用 @Conditional 注解:

在配置类或Bean上使用@Conditional注解,并指定自定义的条件类。

    @Configuration
    @Conditional(MyCustomCondition.class)
    public class MyConfig {
        // 配置Bean
    }

条件判断执行:

Spring容器在启动时,会检查所有的条件注解,并执行相应的条件匹配逻辑。如果条件返回true,则对应的Bean将被创建和注册到容器中。

3.示例

假设我们想要根据系统属性来决定是否创建一个Bean。我们可以创建一个自定义的条件注解OnPropertyCondition,它会检查一个特定的系统属性是否存在。

public class OnPropertyCondition implements Condition {

    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        String propertyValue = context.getEnvironment().getProperty("my.property");
        return "enabled".equals(propertyValue);
    }
}

现在,我们可以使用@Conditional注解结合我们的自定义条件来配置一个Bean:

@Configuration
@Conditional(OnPropertyCondition.class)
public class MyConditionalConfig {
    @Bean
    public MyBean myBean() {
        // 创建并返回MyBean实例
        return new MyBean();
    }
}

在这个例子中,如果系统属性my.property被设置为enabled,那么MyConditionalConfig中的myBean方法将会被调用,并且MyBean的实例将被注册到Spring容器中。如果my.property不是enabled,则MyBean不会被创建。

条件化配置是Spring框架中一个非常强大的特性,它提供了一种灵活的方式来控制Bean的创建。通过自定义条件注解和@Conditional注解,开发者可以根据环境、配置、系统属性等多种条件来决定哪些Bean应该被创建,哪些不应该。这种机制使得Spring应用可以更好地适应不同的运行环境和配置需求。

四、事件发布

Spring Boot 3使用ApplicationEventPublisher来发布和监听应用事件。这允许开发者在应用的不同阶段接收通知,以便执行自定义逻辑。

在Spring框架中,事件发布是一个重要的特性,它允许应用组件之间的松耦合交互。Spring通过ApplicationEventPublisher和ApplicationListener接口来实现事件发布和监听的机制。

1.事件发布逻辑过程

定义事件类:

首先,需要定义一个事件类,该类通常继承自ApplicationEvent。这个类包含了事件相关的数据和信息。

public class CustomEvent extends ApplicationEvent {
    private String message;

    public CustomEvent(Object source, String message) {
        super(source);
        this.message = message;
    }

    public String getMessage() {
        return message;
    }
}

发布事件:

当需要发布一个事件时,可以通过ApplicationEventPublisher接口的实现来做到这一点。ApplicationContext是ApplicationEventPublisher的一个实现,因此可以直接用来发布事件。

@Autowired
private ApplicationContext applicationContext;

public void publishEvent() {
    CustomEvent event = new CustomEvent(this, "Hello World!");
    applicationContext.publishEvent(event);
}

在这个例子中,publishEvent方法创建了一个CustomEvent实例,并将其发布到Spring容器中。

监听事件:

要监听发布的事件,需要实现ApplicationListener接口,并重写onApplicationEvent方法。然后,可以通过@EventListener注解将这个方法注册为事件监听器。

@Component
public class CustomEventListener implements ApplicationListener<CustomEvent> {
    @Override
    public void onApplicationEvent(CustomEvent event) {
        System.out.println("Received CustomEvent with message: " + event.getMessage());
    }
}

在这个例子中,CustomEventListener类实现了ApplicationListener<CustomEvent>接口,并重写了onApplicationEvent方法来处理CustomEvent事件。

使用 @EventListener 注解:

为了简化监听器的注册过程,可以使用@EventListener注解来代替实现ApplicationListener接口。

    @Component
    public class CustomEventListener {
        @EventListener
        public void handleCustomEvent(CustomEvent event) {
            System.out.println("Received CustomEvent with message: " + event.getMessage());
        }
    }

这个例子中,handleCustomEvent方法将自动注册为CustomEvent事件的监听器。

事件发布和监听是Spring框架中实现组件间通信的一种方式。通过定义事件类和发布事件,以及实现监听器和注册事件监听器,Spring应用可以响应和处理各种运行时事件。
这种机制使得应用的各个部分可以保持松耦合,提高了代码的可维护性和可扩展性。通过使用@EventListener注解,Spring Boot进一步简化了事件监听器的创建和管理,使得开发者可以更加专注于业务逻辑的实现。

五、外部化配置

Spring Boot 3提供了强大的外部化配置支持,允许配置信息从应用代码中分离出来,便于管理和动态调整。

外部化配置是Spring Boot提供的一个重要特性,它允许开发者将应用的配置信息从代码中分离出来,存储在外部文件中,如application.properties或application.yml。这样做的好处是可以让应用更容易地适应不同的运行环境,而无需修改代码。

1.外部化配置的逻辑过程

配置文件准备:

开发者需要准备一个或多个配置文件,通常是application.properties或application.yml。这些文件可以放在项目的src/main/resources目录下,也可以放在外部的配置服务器上。

# application.properties
myapp.database.url=jdbc:mysql://localhost:3306/mydb
myapp.database.username=user
myapp.database.password=pass
# application.yml
myapp:
  database:
    url: jdbc:mysql://localhost:3306/mydb
    username: user
    password: pass

配置文件加载:

Spring Boot启动时,会自动加载application.properties或application.yml文件。这些文件中的配置会被解析并存储在Environment对象中。

使用配置属性:

开发者可以通过@Value注解或@ConfigurationProperties注解来将配置文件中的属性注入到Bean中。

@Component
public class DatabaseConfig {
    @Value("${myapp.database.url}")
    private String url;

    @Value("${myapp.database.username}")
    private String username;

    @Value("${myapp.database.password}")
    private String password;

    // getters and setters
}

@Component
@ConfigurationProperties(prefix = "myapp.database")
public class DatabaseConfigProperties {
    private String url;
    private String username;
    private String password;

    // getters and setters
}

动态刷新配置:
Spring Boot允许在运行时动态刷新配置。当外部配置发生变化时,可以通过Environment对象来获取最新的配置值。

    @Autowired
    private Environment environment;

    public void someMethod() {
        String url = environment.getProperty("myapp.database.url");
        // 使用最新的配置值
    }

外部化配置使得Spring Boot应用的配置更加灵活和可配置。通过将配置信息存储在外部文件中,开发者可以轻松地修改应用的行为,而无需重新编译或部署应用。这在多环境部署、持续集成和持续部署等场景中尤其有用。

使用@Value注解和@ConfigurationProperties注解,开发者可以将配置文件中的属性注入到Bean中,从而实现配置的自动化管理。此外,Spring Boot还提供了@RefreshScope注解,用于标记那些需要支持配置动态刷新的Bean。当配置发生变化时,这些Bean会被重新创建,以应用新的配置。

六、健康检查

Spring Boot Actuator提供了健康检查功能,允许开发者定义应用的健康状况,并在启动时进行自动检查。

健康检查是Spring Boot Actuator模块提供的一个重要特性,它允许开发者定义应用程序的健康状况,并在应用程序启动时或定期检查应用程序的健康状况。这通常用于监控系统,以便在出现问题时快速响应。

1.健康检查的逻辑过程

定义健康指标:

开发者可以通过实现HealthIndicator接口来定义应用程序的健康指标。每个健康指标都可以检查应用程序的不同方面,如数据库连接、内存使用情况、外部服务的可用性等。

    @Component
    public class CustomHealthIndicator implements HealthIndicator {
        @Override
        public Health health() {
            // 检查应用程序的健康状况
            if (/* 健康状况良好 */) {
                return Health.up().build();
            } else {
                return Health.down().withDetail("error", "Custom error message").build();
            }
        }
    }

注册健康指标:

Spring Boot Actuator会自动扫描应用程序上下文中的HealthIndicator实现,并在需要时调用它们的health方法。

触发健康检查:

健康检查可以通过多种方式触发,例如,可以通过HTTP端点(/health)来触发。当访问这个端点时,Spring Boot Actuator会调用所有注册的HealthIndicator,并汇总它们的健康状态。

处理健康检查结果:

根据HealthIndicator的实现,健康检查结果会是一个Health对象,它包含了应用程序的健康状态和可能的错误详情。这个结果可以通过HTTP响应返回给调用者,或者用于其他监控和报警系统。

2.示例

以下是一个简单的健康检查示例,它检查一个假设的外部服务是否可用:

@Component
public class ExternalServiceHealthIndicator implements HealthIndicator {

    private final ExternalService externalService;

    public ExternalServiceHealthIndicator(ExternalService externalService) {
        this.externalService = externalService;
    }

    @Override
    public Health health() {
        try {
            // 尝试与外部服务通信来检查其可用性
            externalService.checkAvailability();
            return Health.up().build();
        } catch (Exception e) {
            // 如果通信失败,返回DOWN状态
            return Health.down(e).build();
        }
    }
}

在这个示例中,ExternalServiceHealthIndicator实现了HealthIndicator接口,并尝试与一个外部服务通信。如果通信成功,它返回一个UP的健康状态;如果通信失败,它返回一个DOWN的健康状态,并包含异常信息。

为了使这个健康指标生效,你需要在应用程序中启用健康检查端点。这通常通过在application.properties或application.yml中设置management.endpoint.health.show-details属性为always来完成:

management.endpoint.health.show-details=always

现在,当你访问/health端点时,你将看到所有注册的健康指标的状态,包括我们自定义的ExternalServiceHealthIndicator。

健康检查是Spring Boot Actuator提供的一个强大功能,它允许开发者监控应用程序的健康状况。通过实现HealthIndicator接口并注册自定义的健康指标,开发者可以检查应用程序的不同方面,并在问题发生时快速响应。
健康检查结果可以通过HTTP端点访问,也可以集成到其他监控系统中。这使得应用程序的运维更加简单,提高了应用程序的可靠性和稳定性。

七、配置元数据

Spring Boot 3允许开发者通过@Metadata注解来为配置属性添加额外的元数据,如描述、默认值等。

配置元数据(Configuration Metadata)是Spring Boot提供的一个特性,它允许开发者为配置属性添加额外的信息,例如描述、默认值、可能的枚举值等。这些信息可以用于生成配置文件的文档,或者在IDE中提供更丰富的提示信息,从而帮助开发者更好地理解和使用配置属性。

1.配置元数据的逻辑过程

定义配置属性类:

开发者需要创建一个类,使用@ConfigurationProperties注解,并为其添加prefix属性,以指定配置文件中相应前缀的属性。

@Component
@ConfigurationProperties(prefix = "app")
public class AppConfigProperties {
    private String name;
    private int port;
    // 其他属性...
    // getter and setter methods
}

使用 @Metadata 注解:

在配置属性类中,可以使用@Metadata注解为属性添加元数据。这个注解可以包含属性的描述、示例值等信息。

    @Component
    @ConfigurationProperties(prefix = "app")
    public class AppConfigProperties {
        @Metadata("The name of the application")
        private String name = "defaultName";

        @Metadata("The port on which the application runs")
        private int port = 8080;

        // 其他属性...
        // getter and setter methods
    }

生成配置元数据:

Spring Boot在启动时会自动扫描带有@ConfigurationProperties注解的类,并根据@Metadata注解和其他信息生成配置元数据。

使用配置元数据:

生成的配置元数据可以用于多种场景,例如:

  • 自动生成配置文件的文档。
  • 在IDE中提供属性的提示和自动完成。
  • 在运行时验证配置属性的有效性。

2.示例

假设我们有一个应用程序,它需要配置应用的名称和端口号。我们可以使用@ConfigurationProperties和@Metadata注解来定义这些配置属性,并提供额外的元数据。

@Component
@ConfigurationProperties(prefix = "app")
public class AppConfigProperties {
    @Metadata("The name of the application, used as a header in logs and UI.")
    private String name = "MyApp";

    @Metadata("The port on which the application runs, default is 8080.")
    private int port = 8080;

    // Getters and setters
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getPort() {
        return port;
    }

    public void setPort(int port) {
        this.port = port;
    }
}

在这个例子中,我们定义了两个配置属性:nameport。我们使用@Metadata注解为每个属性添加了描述信息。这些描述信息可以在生成配置文件文档时提供帮助,也可以在IDE中作为属性的提示信息。

为了在IDE中使用这些元数据,你可能需要使用支持Spring Boot配置元数据的IDE插件,例如Spring Boot IDE插件系列。
总结

配置元数据是Spring Boot提供的一个有用特性,它使得配置属性更加易于理解和使用。通过@ConfigurationProperties@Metadata注解,开发者可以为配置属性添加额外的信息,如描述、默认值等。这些信息不仅可以帮助生成配置文件的文档,还可以在IDE中提供更好的提示和自动完成功能,从而提高开发效率和配置的准确性。

八、缓存管理

Spring Boot 3提供了缓存抽象,允许开发者轻松地使用缓存来提高应用性能。

缓存管理是Spring框架提供的一个重要特性,它允许开发者轻松地为方法的调用结果添加缓存,以提高应用程序的性能。Spring的缓存抽象主要通过@EnableCaching注解和@Cacheable、@CachePut、@CacheEvict等注解来实现。

1.缓存管理的逻辑过程

启用缓存支持:

首先,需要在配置类上使用@EnableCaching注解来启用缓存支持。

@Configuration
@EnableCaching
public class CacheConfig {
    // 定义缓存管理器和其他缓存相关的配置
}

定义缓存注解:

接下来,可以在需要缓存的方法上使用@Cacheable注解。这个注解会使得方法的返回值被缓存。

@Cacheable(value = "myCache", key = "#id")
public MyObject findById(Long id) {
    // 方法逻辑,例如从数据库中获取对象
    return myObject;
}

在这个例子中,@Cacheable注解指定了缓存的名称(myCache)和缓存键(#id),这意味着方法的返回值将根据传入的id参数被缓存。

缓存管理器配置:

Spring缓存抽象允许配置多种缓存管理器,例如SimpleCacheManager、EhCacheCacheManager、RedisCacheManager等。这些缓存管理器负责实际的缓存存储和检索。

@Bean
public CacheManager cacheManager() {
    SimpleCacheManager cacheManager = new SimpleCacheManager();
    cacheManager.setCaches(Arrays.asList(new ConcurrentMapCache("myCache")));
    return cacheManager;
}

在这个例子中,我们定义了一个SimpleCacheManager,并为其添加了一个名为myCache的缓存。

缓存注解的其他操作:

除了@Cacheable之外,还有其他的缓存相关注解可以用来执行不同的缓存操作。例如,@CachePut用于更新缓存,@CacheEvict用于清除缓存。

    @CachePut(value = "myCache", key = "#result.id")
    public MyObject update(MyObject result) {
        // 更新对象的逻辑
        return result;
    }

    @CacheEvict(value = "myCache", key = "#id")
    public void delete(Long id) {
        // 删除对象的逻辑
    }

2.示例

假设我们有一个服务类MyService,它提供了根据ID查找对象的方法findById。我们希望这个方法的结果能够被缓存,以减少对数据库的重复查询。

@Service
public class MyService {
    @Autowired
    private MyObjectRepository repository;

    @Cacheable(value = "myCache", key = "#id")
    public MyObject findById(Long id) {
        return repository.findById(id).orElse(null);
    }
}

在这个例子中,每次调用findById方法时,Spring会首先检查名为myCache的缓存中是否已经存在键为id的缓存项。如果存在,就直接返回缓存的结果,而不执行数据库查询。如果不存在,就执行数据库查询,并将结果存入缓存中,以便下次调用时直接使用。

缓存管理是提高应用程序性能的有效手段。通过Spring的缓存抽象,开发者可以轻松地为方法的调用结果添加缓存。@EnableCaching注解启用缓存支持,而@Cacheable@CachePut@CacheEvict等注解用于定义具体的缓存操作。通过配置不同的缓存管理器,Spring缓存抽象可以与多种缓存技术(如EhCache、Redis等)集成,为应用程序提供灵活的缓存解决方案。

九、REST DSL

Spring Boot 3支持使用REST DSL来声明式地定义RESTful API。

REST DSL(Domain Specific Language)是一种领域特定语言,用于简化RESTful服务的开发。在Spring框架中,REST DSL 通常指的是使用注解来定义RESTful接口的一种约定,这些注解提供了一种声明式的方式来配置RESTful路由和行为。

1.REST DSL 的逻辑过程

定义控制器:

在Spring MVC中,控制器(Controller)是处理HTTP请求的核心组件。开发者通过创建控制器类,并在类上添加@RestController注解来定义一个RESTful服务。

@RestController
public class MyRestController {
    // 控制器方法
}

定义路由:

使用@RequestMapping或其衍生注解(如@GetMapping、@PostMapping等)来定义路由。这些注解将HTTP请求映射到控制器中的具体方法上。

@RestController
public class MyRestController {
    @GetMapping("/items")
    List<Item> getAllItems() {
        // 获取所有物品的逻辑
    }
}

在这个例子中,@GetMapping(“/items”)注解将HTTP GET请求映射到/items路径上,并且调用getAllItems方法来处理请求。

处理请求:
控制器中的方法会根据注解定义的路由和HTTP方法来处理请求。方法的参数可以通过注解(如@RequestParam、@PathVariable、@RequestBody等)来绑定请求数据。

@GetMapping("/items/{id}")
Item getItem(@PathVariable Long id) {
    // 根据ID获取物品的逻辑
}

在这个例子中,@PathVariable注解用于将URL中的{id}部分绑定到方法的id参数上。

返回响应:

控制器方法的返回值会被转换为HTTP响应体。Spring MVC提供了多种转换器(如MappingJackson2HttpMessageConverter)来将返回的对象转换为JSON或XML格式。

    @GetMapping("/items")
    public ResponseEntity<List<Item>> getAllItems() {
        List<Item> items = // 获取所有物品的逻辑
        return ResponseEntity.ok(items);
    }

在这个例子中,ResponseEntity包装了返回的对象,并允许开发者定义HTTP响应的状态码、头部信息等。

2.示例

假设我们需要开发一个简单的RESTful服务,用于管理物品(Item)的列表。

@RestController
@RequestMapping("/items")
public class ItemController {

    @Autowired
    private ItemService itemService;

    @GetMapping
    public List<Item> getAllItems() {
        return itemService.getAllItems();
    }

    @PostMapping
    public Item createItem(@RequestBody Item item) {
        return itemService.createItem(item);
    }

    @GetMapping("/{id}")
    public Item getItemById(@PathVariable Long id) {
        return itemService.getItemById(id);
    }

    @PutMapping("/{id}")
    public Item updateItem(@PathVariable Long id, @RequestBody Item item) {
        return itemService.updateItem(id, item);
    }

    @DeleteMapping("/{id}")
    public ResponseEntity<Void> deleteItem(@PathVariable Long id) {
        itemService.deleteItem(id);
        return ResponseEntity.ok().build();
    }
}

在这个例子中,我们定义了一个ItemController,它包含了五个方法来处理不同的HTTP请求:

  • getAllItems方法处理GET请求,返回所有物品的列表。
  • createItem方法处理POST请求,创建一个新的物品。
  • getItemById方法处理GET请求,根据ID获取一个物品。
  • updateItem方法处理PUT请求,更新一个物品的信息。
  • deleteItem方法处理DELETE请求,删除一个物品。

每个方法都使用了相应的REST DSL注解来定义路由和处理逻辑。这样,我们就可以通过HTTP请求来与这个RESTful服务交互。

REST DSL是Spring MVC中用于简化RESTful服务开发的一种约定。通过使用@RequestMapping及其衍生注解,开发者可以轻松地定义路由和处理HTTP请求。这种方式减少了模板化的代码,使得开发更加高效,同时也提高了代码的可读性和可维护性。通过这种方式,开发者可以快速地构建出符合REST原则的Web服务。

十、国际化

Spring Boot 3支持国际化,允许开发者为应用提供多语言支持。

国际化(Internationalization,简称i18n)是软件开发中的一个关键特性,它使得软件能够支持多种语言和地区。Spring框架通过提供一套国际化支持,使得开发者能够轻松地实现多语言功能。

1.国际化的逻辑过程

配置消息源:

Spring的国际化支持基于MessageSource接口。开发者需要配置一个或多个消息源(MessageSource的实现),用于加载不同语言的消息文件。

@Configuration
public class MessageSourceConfig {
    @Bean
    public MessageSource messageSource() {
        ResourceBundleMessageSource messageSource = new ResourceBundleMessageSource();
        messageSource.setBasenames("messages", "errors");
        messageSource.setDefaultEncoding("UTF-8");
        return messageSource;
    }
}

在这个例子中,我们配置了一个ResourceBundleMessageSource,它将加载名为messages和errors的资源文件,这些文件通常位于src/main/resources目录下,并按照语言和地区进行分组,如messages_en.properties、messages_zh_CN.properties等。

创建消息文件:

开发者需要为每种支持的语言创建消息文件。这些文件包含了键值对,其中键是消息的标识符,值是消息的文本。

# messages_en.properties
greeting=Hello, {0}!
# messages_zh_CN.properties
greeting=你好,{0}!

使用消息文本:

在代码中,可以使用MessageFormat类或MessageSource接口来检索和格式化消息文本。

@Service
public class GreetingService {
    @Autowired
    private MessageSource messageSource;

    public String getGreeting(String name) {
        return messageSource.getMessage("greeting", new Object[]{name}, LocaleContextHolder.getLocale());
    }
}

在这个例子中,getGreeting方法根据当前的地区(通过LocaleContextHolder.getLocale()获取)来检索和格式化greeting消息。

在视图中使用国际化消息:

在Thymeleaf等视图模板中,可以使用特定的语法来显示国际化消息。

  <!-- Thymeleaf -->
  <p th:text="#{greeting(name=${name})}"></p>

2.示例

假设我们正在开发一个Web应用程序,需要支持英语和简体中文两种语言。我们首先创建两个消息文件,分别用于英语和简体中文。

src/main/resources/messages_en.properties:

greeting=Hello, {0}!

src/main/resources/messages_zh_CN.properties:

greeting=你好,{0}!

然后,我们创建一个服务类GreetingService,它使用MessageSource来获取问候语。

@Service
public class GreetingService {
    @Autowired
    private MessageSource messageSource;

    public String getGreeting(String name) {
        return messageSource.getMessage("greeting", new Object[]{name}, LocaleContextHolder.getLocale());
    }
}

在控制器中,我们可以调用GreetingService来获取问候语,并将其添加到模型中。

@RestController
public class GreetingController {
    @Autowired
    private GreetingService greetingService;

    @GetMapping("/greeting")
    public Map<String, String> greeting(@RequestParam String name) {
        Map<String, String> model = new HashMap<>();
        model.put("message", greetingService.getGreeting(name));
        return model;
    }
}

最后,在视图中,我们可以使用Thymeleaf模板引擎来显示问候语。

<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
    <title>Greeting</title>
</head>
<body>
    <p th:text="${message}"></p>
</body>
</html>

国际化是使应用程序能够适应不同语言和地区用户的关键特性。Spring框架通过MessageSource接口和相关注解,提供了一套强大的国际化支持。开发者可以通过配置消息源、创建消息文件、在代码中使用MessageSource来检索消息文本,以及在视图中使用特定的语法来显示国际化消息。这样,开发者可以轻松地为应用程序添加多语言支持,提高应用程序的可用性和吸引力。

最后

Spring Boot 是Java开发中100%会使用到的框架,开发者不仅要熟练使用,对其中的核心源码也要了解,正所谓知其然知其所以然,V 哥建议小伙伴们在学习的过程中,一定要去研读一下源码,这有助于你在开发中游刃有余。欢迎一起交流学习心得,一起成长。

原文链接: https://juejin.cn/post/7351399718909968411

文章收集整理于网络,请勿商用,仅供个人学习使用,如有侵权,请联系作者删除,如若转载,请注明出处:http://www.cxyroad.com/17013.html

QR code