【SpringBoot系列-02】自动配置机制源码剖析

【SpringBoot系列-02】自动配置机制源码剖析

咱们天天用Spring Boot,一个@SpringBootApplication注解扔进去,啥配置都不用写,项目就跑起来了。你有没有过这种疑惑:那些DispatcherServlet、DataSource是从哪冒出来的?今天咱们就扒开自动配置的底裤,看看Spring Boot到底在背后干了些啥。

1. @EnableAutoConfiguration工作原理:自动配置的"总开关"

咱们先看启动类上的@SpringBootApplication,点进去瞅瞅(用Ctrl+鼠标点,IDE都支持这操作):

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration  // 重点在这
@ComponentScan(excludeFilters = { @ComponentScan.Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),@ComponentScan.Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class)
})
public @interface SpringBootApplication { // 排除特定的自动配置类@AliasFor(annotation = EnableAutoConfiguration.class)Class<?>[] exclude() default {};@AliasFor(annotation = EnableAutoConfiguration.class)String[] excludeName() default {};
}

这注解就是个"组合拳",真正负责自动配置的是@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 {};
}

看到没?@Import注解导入了AutoConfigurationImportSelector.class,这哥们就是自动配置的"搬运工"。简单说,它的作用就是:从一堆配置类里挑出符合条件的,塞给Spring容器

自动配置整体流程图

Spring Boot应用启动
解析@SpringBootApplication
处理@EnableAutoConfiguration
AutoConfigurationImportSelector开始工作
扫描所有jar包的META-INF/spring.factories
获取EnableAutoConfiguration对应的配置类列表
去重和排除处理
应用条件过滤器
按顺序排序配置类
注册到Spring容器
Spring容器实例化配置类
处理@Conditional条件
条件满足?
创建Bean定义
跳过该配置

调试小技巧:

  • 启动时在AutoConfigurationImportSelector的selectImports()方法打个断点,观察返回的String[]数组
  • 添加VM参数-Ddebug或在配置文件设置debug=true,查看控制台的自动配置报告
  • 使用Spring Boot Actuator的/actuator/conditions端点查看条件评估报告

2. AutoConfigurationImportSelector源码解析:配置类的"筛选器"

这哥们的核心方法是selectImports(),咱们看关键代码:

@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return NO_IMPORTS;}// 1. 获取自动配置条目(核心步骤)AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);// 2. 提取配置类全类名return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {if (!isEnabled(annotationMetadata)) {return EMPTY_ENTRY;}// 获取@EnableAutoConfiguration注解的属性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);// 应用过滤器(Spring Boot 2.x后新增的机制)configurations = getConfigurationClassFilter().filter(configurations);// 触发自动配置导入事件fireAutoConfigurationImportEvents(configurations, exclusions);return new AutoConfigurationEntry(configurations, exclusions);
}

配置类筛选流程图

获取候选配置类列表
去重处理
处理exclude排除
应用AutoConfigurationImportFilter
检查OnBeanCondition
检查OnClassCondition
检查OnWebApplicationCondition
过滤后的配置类列表
触发导入事件
返回最终配置类列表

重点看getCandidateConfigurations()方法

protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {List<String> configurations = new ArrayList<>();// 1. 从新位置加载(META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports)configurations.addAll(ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()).getCandidates());// 2. 为了兼容,仍然从spring.factories加载configurations.addAll(SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), getBeanClassLoader()));return configurations;
}

3. 条件注解处理器实现原理:配置类的"门禁卡"

光把配置类捞出来还不够,得看它们能不能生效。这就轮到@Conditional家族出场了。

条件注解家族关系图

@Conditional
@ConditionalOnClass
@ConditionalOnMissingClass
@ConditionalOnBean
@ConditionalOnMissingBean
@ConditionalOnProperty
@ConditionalOnResource
@ConditionalOnWebApplication
@ConditionalOnNotWebApplication
@ConditionalOnExpression
@ConditionalOnJava
@ConditionalOnJndi
OnClassCondition
OnBeanCondition
OnPropertyCondition
OnResourceCondition
OnWebApplicationCondition

咱们以@ConditionalOnClass为例,看看它的源码:

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnClassCondition.class)  // 关键:指定了条件处理器
public @interface ConditionalOnClass {Class<?>[] value() default {};String[] name() default {};  // 支持字符串类名,避免强依赖
}

真正干活的是OnClassCondition类:

@Order(Ordered.HIGHEST_PRECEDENCE)  // 优先级最高,最先执行
class OnClassCondition extends FilteringSpringBootCondition {@Overrideprotected final ConditionOutcome[] getOutcomes(String[] autoConfigurationClasses,AutoConfigurationMetadata autoConfigurationMetadata) {// 批量处理,提高性能ConditionOutcome[] outcomes = new ConditionOutcome[autoConfigurationClasses.length];for (int i = 0; i < outcomes.length; i++) {String autoConfigurationClass = autoConfigurationClasses[i];if (autoConfigurationClass != null) {// 从元数据中获取条件值Set<String> candidates = autoConfigurationMetadata.getSet(autoConfigurationClass,"ConditionalOnClass");if (candidates != null) {outcomes[i] = getOutcome(candidates);}}}return outcomes;}private ConditionOutcome getOutcome(Set<String> candidates) {try {List<String> missing = new ArrayList<>();for (String candidate : candidates) {if (!isPresent(candidate, this.beanClassLoader)) {missing.add(candidate);}}if (!missing.isEmpty()) {return ConditionOutcome.noMatch(ConditionMessage.forCondition(ConditionalOnClass.class).didNotFind("required class", "required classes").items(Style.QUOTE, missing));}} catch (Exception ex) {// ...}return ConditionOutcome.match();}
}

条件评估时机和流程

Spring容器AutoConfigurationImportSelectorConfigurationClassFilterConditionAutoConfigurationMetadata导入自动配置类过滤配置类读取spring-autoconfigure-metadata.properties批量评估条件检查类是否存在返回匹配结果返回过滤后的配置类注册配置类到容器实例化时再次评估@ConditionalSpring容器AutoConfigurationImportSelectorConfigurationClassFilterConditionAutoConfigurationMetadata

咱们自己写个条件注解试试:

// 1. 定义条件注解
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Conditional(OnSystemPropertyCondition.class)
public @interface ConditionalOnSystemProperty {String name();String value();
}// 2. 实现条件处理器
public class OnSystemPropertyCondition implements Condition {@Overridepublic boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {Map<String, Object> attributes = metadata.getAnnotationAttributes(ConditionalOnSystemProperty.class.getName());String propertyName = (String) attributes.get("name");String expectedValue = (String) attributes.get("value");String actualValue = System.getProperty(propertyName);return expectedValue.equals(actualValue);}
}// 3. 使用条件注解
@Configuration
@ConditionalOnSystemProperty(name = "app.env", value = "production")
public class ProductionConfiguration {@Beanpublic DataSource productionDataSource() {// 生产环境数据源配置HikariDataSource dataSource = new HikariDataSource();dataSource.setMaximumPoolSize(50);return dataSource;}
}

性能优化提示:Spring Boot使用了spring-autoconfigure-metadata.properties文件来存储条件注解的元数据,这样可以在不加载类的情况下就进行条件判断,大大提升了启动速度。

4. Spring Factories加载机制:配置类的"花名册"

Spring Factories文件位置变化

让我们看看不同版本的配置文件格式:

META-INF/spring.factories格式:

# Auto Configure
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration,\
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration,\
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration,\
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration,\
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration,\
org.springframework.boot.autoconfigure.cassandra.CassandraAutoConfiguration,\
org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\
org.springframework.boot.autoconfigure.context.LifecycleAutoConfiguration,\
org.springframework.boot.autoconfigure.context.MessageSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.context.PropertyPlaceholderAutoConfiguration,\
org.springframework.boot.autoconfigure.couchbase.CouchbaseAutoConfiguration,\
org.springframework.boot.autoconfigure.dao.PersistenceExceptionTranslationAutoConfiguration,\
org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration,\
org.springframework.boot.autoconfigure.data.redis.RedisAutoConfiguration,\
org.springframework.boot.autoconfigure.data.web.SpringDataWebAutoConfiguration,\
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.ServletWebServerFactoryAutoConfiguration,\
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration

META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports格式:

org.springframework.boot.autoconfigure.admin.SpringApplicationAdminJmxAutoConfiguration
org.springframework.boot.autoconfigure.aop.AopAutoConfiguration
org.springframework.boot.autoconfigure.amqp.RabbitAutoConfiguration
org.springframework.boot.autoconfigure.batch.BatchAutoConfiguration
org.springframework.boot.autoconfigure.cache.CacheAutoConfiguration
org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.DispatcherServletAutoConfiguration
org.springframework.boot.autoconfigure.web.servlet.WebMvcAutoConfiguration

SpringFactoriesLoader加载机制的核心代码:

public final class SpringFactoriesLoader {private static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";// 缓存,避免重复加载private static final Map<ClassLoader, Map<String, List<String>>> cache = new ConcurrentReferenceHashMap<>();public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {ClassLoader classLoaderToUse = classLoader;if (classLoaderToUse == null) {classLoaderToUse = SpringFactoriesLoader.class.getClassLoader();}String factoryTypeName = factoryType.getName();return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());}private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {Map<String, List<String>> result = cache.get(classLoader);if (result != null) {return result;}result = new HashMap<>();try {// 加载所有jar包中的spring.factories文件Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION);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.computeIfAbsent(factoryTypeName, key -> new ArrayList<>()).add(factoryImplementationName.trim());}}}// 去重并保持原有顺序result.replaceAll((factoryType, implementations) -> implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList)));cache.put(classLoader, result);} catch (IOException ex) {throw new IllegalArgumentException("Unable to load factories from location [" +FACTORIES_RESOURCE_LOCATION + "]", ex);}return result;}
}

Spring Factories加载流程图

SpringFactoriesLoader.loadFactoryNames
缓存中存在?
返回缓存结果
扫描类路径
加载所有META-INF/spring.factories
解析Properties文件
按key分组整理
去重处理
存入缓存
返回结果

实战技巧:想知道哪些自动配置类生效了,可以通过以下方式:

  1. 启动时添加debug参数:
java -jar myapp.jar --debug
# 或者
java -Ddebug -jar myapp.jar
  1. 配置文件开启debug:
debug: true
# 或者只看自动配置报告
logging:level:org.springframework.boot.autoconfigure: DEBUG
  1. 使用Actuator端点(推荐):
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-actuator</artifactId>
</dependency>

访问 http://localhost:8080/actuator/conditions 查看详细的条件评估报告。

5. 自动配置顺序控制:谁先谁后有讲究

有时候配置类之间有依赖关系,需要控制加载顺序。Spring Boot提供了多种方式:

顺序控制注解关系图

配置类顺序控制
@AutoConfigureBefore
@AutoConfigureAfter
@AutoConfigureOrder
@DependsOn
在指定配置类之前加载
在指定配置类之后加载
指定加载优先级数值
Bean级别的依赖控制

举个实际的例子:

// 基础配置
@Configuration
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
public class InfrastructureAutoConfiguration {@Beanpublic ConnectionPool connectionPool() {return new HikariConnectionPool();}
}// 数据源配置(依赖基础配置)
@Configuration
@AutoConfigureAfter(InfrastructureAutoConfiguration.class)
@ConditionalOnBean(ConnectionPool.class)
public class DataSourceAutoConfiguration {@Beanpublic DataSource dataSource(ConnectionPool connectionPool) {return new PooledDataSource(connectionPool);}
}// JPA配置(依赖数据源配置)
@Configuration
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
@ConditionalOnBean(DataSource.class)
public class JpaAutoConfiguration {@Beanpublic EntityManagerFactory entityManagerFactory(DataSource dataSource) {LocalContainerEntityManagerFactoryBean factory = new LocalContainerEntityManagerFactoryBean();factory.setDataSource(dataSource);return factory.getObject();}
}// MyBatis配置(也依赖数据源,但要在JPA之前)
@Configuration
@AutoConfigureAfter(DataSourceAutoConfiguration.class)
@AutoConfigureBefore(JpaAutoConfiguration.class)
@ConditionalOnBean(DataSource.class)
public class MyBatisAutoConfiguration {@Beanpublic SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {SqlSessionFactoryBean factory = new SqlSessionFactoryBean();factory.setDataSource(dataSource);return factory.getObject();}
}

源码层面,这些注解是怎么起作用的?看AutoConfigurationSorter类:

class AutoConfigurationSorter {private final MetadataReaderFactory metadataReaderFactory;private final AutoConfigurationMetadata autoConfigurationMetadata;List<String> getInPriorityOrder(Collection<String> classNames) {// 1. 构建配置类的元数据final AutoConfigurationClasses classes = new AutoConfigurationClasses(this.metadataReaderFactory, this.autoConfigurationMetadata, classNames);// 2. 按字母顺序初始排序List<String> orderedClassNames = new ArrayList<>(classNames);Collections.sort(orderedClassNames);// 3. 按@AutoConfigureOrder排序orderedClassNames.sort((o1, o2) -> {int i1 = classes.get(o1).getOrder();int i2 = classes.get(o2).getOrder();return Integer.compare(i1, i2);});// 4. 按@AutoConfigureBefore和@AutoConfigureAfter排序orderedClassNames = sortByAnnotation(classes, orderedClassNames);return orderedClassNames;}private List<String> sortByAnnotation(AutoConfigurationClasses classes, List<String> classNames) {// 使用拓扑排序处理before/after依赖关系List<String> toSort = new ArrayList<>(classNames);toSort.addAll(classes.getAllNames());Set<String> sorted = new LinkedHashSet<>();Set<String> processing = new LinkedHashSet<>();while (!toSort.isEmpty()) {doSortByAfterAnnotation(classes, toSort, sorted, processing, null);}return new ArrayList<>(sorted);}
}

配置类加载顺序决策流程

开始排序
按字母顺序初始排序
处理@AutoConfigureOrder
有Order值?
按Order值排序
保持当前顺序
处理@AutoConfigureBefore/After
构建依赖图
拓扑排序
存在循环依赖?
抛出异常
返回排序后的列表

重要提醒: 这三个顺序控制注解(@AutoConfigureBefore、@AutoConfigureAfter、@AutoConfigureOrder)只对自动配置类生效,即那些通过spring.factories或.imports文件加载的类。对于通过@ComponentScan扫描到的普通@Configuration类无效!

如果需要控制普通配置类的加载顺序,可以使用:

  • @DependsOn:控制Bean的创建顺序
  • @Order + @Configuration:在某些场景下可以控制配置类的处理顺序
  • BeanFactoryPostProcessor:自定义Bean定义的处理顺序

6. 自动配置失效分析:为啥它又不干活了?

自动配置失效是开发中的常见问题,我们来系统地分析排查方法:

自动配置失效排查流程图

自动配置失效
查看启动日志
开启debug模式
查看自动配置报告
Positive matches中有目标配置?
查看Negative matches
找到未匹配原因?
解决条件不满足问题
查看Exclusions
被排除了?
检查exclude配置
检查spring.factories
配置类已加载
Bean创建成功?
检查Bean条件
检查Bean覆盖
问题解决

常见失效场景及解决方案

场景1:条件不满足

问题表现:

RedisAutoConfiguration:Did not match:- @ConditionalOnClass did not find required class 'org.springframework.data.redis.core.RedisOperations'

解决方案:

<!-- 添加必要的依赖 -->
<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>
场景2:Bean被覆盖

问题代码:

@Configuration
public class MyDataSourceConfig {@Beanpublic DataSource dataSource() {// 自定义的DataSource会覆盖自动配置的return new HikariDataSource();}
}

解决方案:

@Configuration
public class MyDataSourceConfig {@Bean@ConditionalOnMissingBean(DataSource.class)  // 添加条件,避免覆盖public DataSource customDataSource() {return new HikariDataSource();}// 或者使用@Primary指定主要的Bean@Bean@Primarypublic DataSource primaryDataSource() {return new HikariDataSource();}
}
场景3:扫描路径问题

问题代码:

@SpringBootApplication(scanBasePackages = "com.example.service")  // 限制了扫描范围
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}

解决方案:

@SpringBootApplication  // 使用默认扫描(当前包及子包)
@ComponentScan(basePackages = {"com.example.service", "com.example.config"})  // 或明确指定多个包
public class Application {public static void main(String[] args) {SpringApplication.run(Application.class, args);}
}
场景4:依赖冲突

问题表现:
同时引入spring-boot-starter-web和spring-boot-starter-webflux导致WebMvcAutoConfiguration失效。

排查命令:

# Maven查看依赖树
mvn dependency:tree# Gradle查看依赖
gradle dependencies

解决方案:

<dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-webflux</artifactId><exclusions><exclusion><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-reactor-netty</artifactId></exclusion></exclusions>
</dependency>

实用的排查工具和技巧

  1. 使用Spring Boot Actuator:
@RestController
@RequestMapping("/debug")
public class AutoConfigDebugController {@Autowiredprivate ConfigurableApplicationContext context;@GetMapping("/beans")public List<String> getBeans() {return Arrays.stream(context.getBeanDefinitionNames()).sorted().collect(Collectors.toList());}@GetMapping("/autoconfig/{className}")public Map<String, Object> checkAutoConfig(@PathVariable String className) {Map<String, Object> result = new HashMap<>();try {Class<?> clazz = Class.forName(className);result.put("isLoaded", context.containsBean(clazz.getSimpleName()));result.put("beanNames", context.getBeanNamesForType(clazz));} catch (ClassNotFoundException e) {result.put("error", "Class not found");}return result;}
}
  1. 自定义条件评估监听器:
@Component
public class ConditionEvaluationListener implements ApplicationListener<ContextRefreshedEvent> {@Overridepublic void onApplicationEvent(ContextRefreshedEvent event) {ConditionEvaluationReport report = ConditionEvaluationReport.get(event.getApplicationContext().getBeanFactory());System.out.println("=== Positive Matches ===");report.getConditionAndOutcomesBySource().entrySet().stream().filter(entry -> entry.getValue().isFullMatch()).forEach(entry -> System.out.println(entry.getKey()));System.out.println("\n=== Negative Matches ===");report.getConditionAndOutcomesBySource().entrySet().stream().filter(entry -> !entry.getValue().isFullMatch()).forEach(entry -> {System.out.println(entry.getKey() + ":");entry.getValue().forEach(condition -> System.out.println("  - " + condition.getMessage()));});}
}
  1. 配置文件控制:
# 排除特定的自动配置
spring:autoconfigure:exclude:- org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration- org.springframework.boot.autoconfigure.orm.jpa.HibernateJpaAutoConfiguration# 或者使用环境变量
# SPRING_AUTOCONFIGURE_EXCLUDE=org.springframework.boot.autoconfigure.jdbc.DataSourceAutoConfiguration# 条件属性控制
spring:datasource:# 通过设置属性影响@ConditionalOnPropertyinitialize: falsejpa:hibernate:ddl-auto: none

7. 总结:自动配置的本质就是"约定大于配置"

通过源码分析,我们可以总结出Spring Boot自动配置的核心机制:

自动配置核心要素总结

在这里插入图片描述

Spring Boot自动配置的本质就是:

  1. 约定配置类的位置:从spring.factories或.imports文件找配置类
  2. 约定生效的条件:通过@Conditional系列注解控制配置类是否生效
  3. 约定加载的顺序:通过@AutoConfigure系列注解控制加载顺序
  4. 约定优于配置:提供合理的默认值,同时允许自定义覆盖

实战建议

  1. 开发自定义Starter时

    • 务必提供spring.factories或.imports文件
    • 合理使用条件注解,避免强制依赖
    • 提供配置属性类(@ConfigurationProperties)
    • 编写自动配置测试类
  2. 遇到自动配置问题时

    • 第一时间开启debug模式查看报告
    • 使用Actuator的conditions端点
    • 检查依赖版本兼容性
    • 查看官方文档的配置属性说明
  3. 性能优化时

    • 排除不需要的自动配置类
    • 使用懒加载(@Lazy)
    • 合理设置组件扫描范围
    • 考虑使用配置类的条件化加载

动手练习

现在,让我们来完成一个自定义starter的练习:

// 1. 创建配置属性类
@ConfigurationProperties(prefix = "custom.cache")
public class CustomCacheProperties {private boolean enabled = true;private int maxSize = 1000;private long ttl = 3600;// getters and setters
}// 2. 创建自动配置类
@Configuration
@ConditionalOnClass(name = "com.github.benmanes.caffeine.cache.Caffeine")
@ConditionalOnProperty(prefix = "custom.cache", name = "enabled", havingValue = "true", matchIfMissing = true)
@EnableConfigurationProperties(CustomCacheProperties.class)
@AutoConfigureAfter(CacheAutoConfiguration.class)
public class CustomCacheAutoConfiguration {@Bean@ConditionalOnMissingBeanpublic CacheManager customCacheManager(CustomCacheProperties properties) {CaffeineCacheManager cacheManager = new CaffeineCacheManager();cacheManager.setCaffeine(Caffeine.newBuilder().maximumSize(properties.getMaxSize()).expireAfterWrite(properties.getTtl(), TimeUnit.SECONDS));return cacheManager;}
}// 3. 创建spring.factories文件
// resources/META-INF/spring.factories
org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
com.example.autoconfigure.CustomCacheAutoConfiguration// 4. 创建配置元数据(可选但推荐)
// resources/META-INF/spring-configuration-metadata.json
{"properties": [{"name": "custom.cache.enabled","type": "java.lang.Boolean","defaultValue": true,"description": "Enable custom cache configuration."},{"name": "custom.cache.max-size","type": "java.lang.Integer","defaultValue": 1000,"description": "Maximum cache size."},{"name": "custom.cache.ttl","type": "java.lang.Long","defaultValue": 3600,"description": "Cache TTL in seconds."}]
}

通过这个练习,你就能真正掌握Spring Boot自动配置的精髓了!


理解了这些原理,以后再遇到自动配置相关的问题,我们就能像老中医一样,望(看日志)闻(debug断点)问(查依赖)切(改配置),轻松搞定!记住,Spring Boot的魔法并不神秘,它只是帮我们把繁琐的配置工作自动化了。掌握了原理,你也能写出优雅的自动配置代码。

有问题欢迎在评论区讨论,让我们一起深入Spring Boot的源码世界!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/92953.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/92953.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

51单片机-51单片机最小系统

本章概述思维导图&#xff1a;51单片机最小系统51单片机最小系统是51系列单片机&#xff08;如AT89C51、STC89C52等&#xff09;能够独立工作的最简电路配置&#xff0c;它为单片机提供了运行所需的基本条件。51单片机最小系统板是嵌入式系统开发的基础平台&#xff0c;集成了单…

git学习1

目录引入版本控制集中式和分布式版本控制git工作机制代码托管中心Git常用命令设置用户签名初始化本地库查看库状态add和提交版本穿梭git分支操作分支定义分支好处分支操作查看分支创建分支切换分支分支合并&#x1f495;✨&#x1fa77;合并冲突git团队协作团队内协作跨团队协作…

redis原理篇--Dict

Dict数据结构一、Redis字典的核心组件Redis字典由三部分构成&#xff1a;dictht&#xff08;哈希表&#xff09;&#xff1a;存储桶数组与元数据dictEntry&#xff08;哈希节点&#xff09;&#xff1a;存储键值对dict&#xff08;字典主体&#xff09;&#xff1a;包含双哈希表…

静态路由主备切换

在网络中&#xff0c;静态路由的主备切换是实现网络冗余的基础方案之一&#xff0c;通过配置不同优先级的静态路由&#xff0c;确保主用路径故障时&#xff0c;流量能自动切换到备用路径&#xff0c;提升网络可靠性。以下从知识讲解和实验配置两部分详细说明。一、静态路由主备…

PDF处理控件Aspose.PDF教程:在C#、Java、Python中快速缩小PDF

如果您的PDF太大&#xff0c;无法通过电子邮件发送&#xff0c;或者在线加载时间过长&#xff0c;您可以在几秒钟内缩小 PDF 大小。本教程介绍了借助Aspose.PDF使用 C#、Java 和 Python 编程快速缩小PDF的方法。 Aspose.PDF官方试用版下载 通过编程缩小 PDF 尺寸 如果您需要…

AWS EKS 常用命令大全:从基础管理到高级运维

前言 Amazon Elastic Kubernetes Service (EKS) 是 AWS 提供的托管 Kubernetes 服务,大大简化了 K8s 集群的部署和管理工作。作为 EKS 管理员或开发者,熟练掌握 kubectl 命令是日常工作的基础。本文将详细介绍 EKS 环境中常用的 kubectl 命令,涵盖集群管理、工作负载操作、…

GitHub Browser-Use 的部署失败记录:失败了,失败了。。。。

一、项目背景与核心作用 browser-use 是一个开源的浏览器自动化工具&#xff0c;通过集成 AI 智能体&#xff08;如 GPT、Claude、DeepSeek 等大型语言模型&#xff09;&#xff0c;实现用自然语言控制浏览器操作。其核心目标是 简化网页交互自动化&#xff0c;尤其适合复杂、…

调用springboot接口返回403,问题定位及总结

背景在一次与前端联调后端接口时前端返回接口返回状态码是403&#xff0c;前端返回说已经带了请求token。排查 查看后端控制台没有出现任何错误信息。自己postman手动调用接口&#xff0c;发现接口正常。仔细核对前端调用接口与postman请求的区别&#xff0c;没有发现任何问题。…

布隆过滤器原理分析、应用场景、与redis使用案例

一、核心结构与工作原理1.1 数据结构布隆过滤器由以下两部分组成&#xff1a;位数组&#xff08;Bit Array&#xff09;&#xff1a;一个长度为 m 的二进制数组&#xff0c;初始所有位为0。哈希函数组&#xff1a;k 个独立的哈希函数&#xff0c;每个函数将输入元素映射到位数组…

异步并发×编译性能:Dart爬虫的实战突围

Dart凭借其高效的异步并发模型、AOT编译性能和现代化的语法&#xff0c;正成为爬虫开发中值得关注的新选择。特别是对于Flutter应用开发者而言&#xff0c;Dart提供了一种"全栈同语言"的独特优势。 本文我将通过实战代码展示如何利用Dart的核心优势——包括基于Futur…

Day 8: 深度学习综合实战与进阶技术 - 从优化到部署的完整流程

Day 8: 深度学习综合实战与进阶技术 - 从优化到部署的完整流程 🎯 学习目标: 掌握深度学习模型优化、调试、迁移学习等工业级技能,能够构建高性能的深度学习应用 📚 核心概念概览 核心概念解释: 模型优化: 通过正则化、学习率调度等技术提升模型性能和泛化能力 为什么需…

特征工程--机器学习

1、特征工程1.1 概念特征工程&#xff08;Feature Engineering&#xff09;是机器学习项目中非常关键的一步&#xff0c;它是指通过领域知识来选择、创建或修改能够使机器学习模型更好地工作的特征&#xff08;即输入变量&#xff09;。特征工程的目标是提高模型的性能&#xf…

支持任意 MCP 协议的客户端

支持任意 MCP 协议的客户端&#xff08;如&#xff1a;Cursor、Claude、Cline&#xff09;可方便使用高德地图 MCP server。目前支持Streamable HTTP, SSE 和 Node.js I/O 三种接入方式(推荐用户使用Streamable HTTP)。 快速接入-MCP Server|高德地图API

【线性代数】目录

【线性代数】线性方程组与矩阵——&#xff08;1&#xff09;线性方程组与矩阵初步【线性代数】线性方程组与矩阵——行列式【线性代数】线性方程组与矩阵——&#xff08;2&#xff09;矩阵与线性方程组的解【线性代数】线性方程组与矩阵——&#xff08;3&#xff09;线性方程…

豆包新模型+PromptPilot:AI应用开发全流程实战指南

> 当深度推理的豆包大模型遇上智能提示词引擎,传统AI开发中**70%的调试时间被压缩至几分钟**,一场从“手工调参”到“智能优化”的开发范式革命正在发生。 ## 一、技术架构解析:双引擎驱动智能进化 ### 1.1 豆包新模型的技术突破 2025年火山引擎推出的**豆包1.6系列模型…

Day13 Vue工程化

1.介绍&环境准备 npm两项全局配置2.项目介绍&开发流程 npm create vue3.3.4 / install / run dev3.API风格 setup ref() onMounted()两种风格选项式API写法转为组合式API写法在根组件App.vue中引用写好的xxx.vue4.案例1.引入组件2.完整代码<script></script&g…

Linux中配置DNS

Linux中配置DNS服务 一、什么是DNS DNS (Domain Name System) 是域名服务 &#xff0c;它是由解析器和域名服务器组成的。 域名服务器是指保存有该网络中所有主机的域名和对应IP地址&#xff0c; 并具有将域名转换为IP地址功能的服务器。&#xff08;将网址解析成IP&#xff…

Redis应⽤-缓存与分布式锁

&#x1f308; 个人主页&#xff1a;Zfox_ &#x1f525; 系列专栏&#xff1a;Redis &#x1f525; 什么是缓存 缓存(cache)是计算机中的⼀个经典的概念.在很多场景中都会涉及到 核⼼思路就是把⼀些常⽤的数据放到触⼿可及 (访问速度更快) 的地⽅,⽅便随时读取 对于计算机…

TCP、HTTP/HTTPS、FTP 解析 + 面试回答参考

TCP、HTTP/HTTPS、FTP 解析 面试回答参考 在后端开发、网络编程以及运维面试中&#xff0c;TCP 协议、HTTP/HTTPS、FTP 是高频考点。本文将从原理、流程、面试常问问题出发&#xff0c;帮你一次性搞懂这些核心知识点。一、TCP 三次握手 1. 作用 建立可靠连接&#xff0c;确保双…

ATF(TF-A)安全通告 TFV-13(CVE-2024-7881)

安全之安全(security)博客目录导读 ATF(TF-A)安全通告汇总 目录 一、漏洞描述 二、缓解措施与建议 三、补丁修改 关于该漏洞的具体细节,可参考【CVE-2024-7881】ARM CPU漏洞安全通告】 Title 非特权上下文可以触发数据相关的预取引擎,从而获取特权位置的内容,并将这些…