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;
}
}
在这个例子中,我们定义了两个配置属性:name
和port
。我们使用@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