# springboot3 **Repository Path**: losermly/springboot3 ## Basic Information - **Project Name**: springboot3 - **Description**: 学习springboot3 - **Primary Language**: Java - **License**: MulanPSL-2.0 - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2025-08-15 - **Last Updated**: 2025-08-20 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # springboot3 #### 介绍 学习springboot3 springboot倡导约定大于配置,简化了配置 #### 软件架构 1. springBoot 3.3.5 2. jdk21 ## springBoot的jar和普通jar的区别 springBoot的jar是可执行jar,普通jar是普通jar,运行时需要指定启动类 ### springBoot的jar目录结构 |- springboot.jar: |- BOOT-INF |- META-INF |- org |- x.jar |- META-INF |- org 1. BOOT-INF:自己编写的应用程序一级应用程序需要的依赖 2. META-INF 3. org:当执行java -jar时,在该目录下找到程序入口并执行(main(String[] args)) ## springBoot外部化配置 配置存储在代码之外 ### 外部化配置的优先级 按照优先级进行读取,如果一下路径的都有配置文件,那么程序会读取优先级最高的配置文件中的文件属性进行使用 后续的相同属性就不会使用 优先找properties文件,没有的话再找yaml文件 1. file: ./config/ 2. file: ./ 3. classpath: /config/ 4. classpath: / 5. 通过运行参数查找:java -jar xxx.jar --spring.config.location=file://D:/path/to/my.properties #### application.properties 这个配置可以不写,不写的话springboot就引用自动配置的默认配置 #### application.yaml ## springBoot自动配置 ### 自动配置概述 #### spring boot的两大核心 springBoot框架的两大核心特性可以概括为“启动器”(starter),“自动配置”(auto-configuration) 1. 启动器(Starter):
springBoot提供了一系列的Stater POMs,他们是一组预定义的依赖关系; 当在项目中引入一个start pom时,他会自动包含所有必要的spring组件以及合理的默认设置。开发者不 需要手动管理复杂的依赖关系,也不需要担心版本冲突的问题,减少了配置上的出错可能 2. 自动配置(Auto-Configuration):
当添加了特定的starter pom后,springBoot会根据类路 径上存在的jar包来自动配置bean(比如:springBoot发现类路径上存在mybatis相关的类,例如 SqlSessionFactory.class,那么SpringBoot将自动配置mybatis相关的所有bean)。
如果开发者没有显示的提供任何域特定功能相关的配置,springboot将使用其默认配置来自动设置这些功 能。当然,如果需要的话,用户也可以覆盖这些默认设置。 这两个特性结合在一起,使得使用spring boot开发应用程序变得更加简单快速,减少了大量的样板代码 和重复配置的工作。让程序员专注业务逻辑的开发,在环境方面耗费最少的时间。 #### 自动配置带来的便捷(示例) 1. spring继承Mybatis框架
applicationContext.xml配置 ~~~xml ~~~ 2. springBoot配置Mybatis框架
引用starter
application.yaml ~~~yaml spring: datasource: url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf-8&useSSL=false&allowPublicKeyRetrieval=true&zeroDateTimeBehavior=convertToNull&transformedBitIsBoolean=true&allowMultiQueries=true&allowLoadLocalInfile=true&allowUrlInLocalInfile=true&allowPublicKeyRetrieval=true&allowMultiQueries=true&allowLoadLocalInfile=true&allowUrlInLocalInfile=true&allowPublicKeyRetrieval=true&allowMultiQueries=true&allowLoadLocalInfile=true&allow username: root password: root driver-class-name: com.mysql.cj.jdbc.Driver type: com.alibaba.druid.pool.DruidDataSource ~~~ #### 默认配置 springboot为了实现功能提供了许多的默认配置
例如:tomcat的默认端口是8080,可以通过**application.yaml**文件进行覆盖修改 ~~~yaml server: # tomcat端口 port: 8888 # thymeleaf配置 thymeleaf: prefix: classpath:/templates/ suffix: .html ~~~ 这些配置都会通过**@ConfigurationProperties(prefix = "")**注解绑定到对应的bean的属性上,这个bean一般称为属性类。例如:
**ServerProperties**: 服务器属性类,专门负责配置服务器相关配置 ~~~java @ConfigurationProperties(prefix = "server", ignoreUnknownFields = true) public class ServerProperties { private Integer port; } ~~~ #### springboot框架提供的条件注解 springboot依靠框架中的条件注解来实现按需加载,满足条件时才会实例化一个bean
条件注解以 **@ConditionalOnxxxxxx**开头 + @ConditionalOnClass:当指定的类位于类路径上时,才会实例化一个bean + @ConditionalOnMissingClass:当指定类不存在时,才创建Bean + @ConditionalOnBean:当容器中有指定的Bean时,才会实例化一个Bean + @ConditionalOnMissingBean:当容器中没有指定的Bean时,才会实例化一个Bean + @ConditionalOnProperty:当配置文件中存在指定属性时,才会实例化一个Bean + @ConditionalOnResource:当自定资源存在时,才会创建Bean + @ConditionalOnWebApplication:当是web应用时,才会创建Bean + @ConditionalOnNotWebApplication:当不是web应用时,才会创建Bean ### 自动配置的实现原理 #### 程序没有开始执行前导入的依赖 1. 导入 **spring-boot-starter-web** 2. 关联导入 **spring-boot-starter**、**spring-boot-starter-json**、**spring-boot-starter-tomcat**、**spring-web**、**spring-webmvc** 1. **spring-boot-starter**: 是springboot的核心启动器,任何启动器导入时都会关联导入springboot核心启动器 3. 核心启动器导入后,关联了一个jar包: **spring-boot-autoconfigure** 1. 这个jar中存放的是springboot框架官方支持的自动配置类 #### 从main方法开始执行之后发生的流程 1. 在main方法执行时,主入口类上使用 **@SpringBootApplication**进行了标注 2. **@SpringBootApplication**注解是复合注解,它被一下3个注解标注 1. **@SpringBootConfiguration**: 它被 **@Configuration**标注,表示主入口类就是一个配置类,这时开始加载该配置 2. **@ComponentScan**:默认扫描的是主入口所在包及其子包下的文件。此时 **spring-boot-autoconfigure**包是扫描不到的,其中的 **XxxAutoConfiguration**自动配置类是无法加载的 3. **@EnableAutoConfiguration**:启动自动配置,自动配置类的加载和生效全靠这个注解 3. **@EnableAutoConfiguration**:被 **@Import(AutoConfigurationImportSelector.class)** 标注 1. **@Import(AutoConfigurationImportSelector.class): 将 **AutoConfigurationImportSelector** 作为一个Bean加载到IoC容器中 2. 这个Bean负责手机和选择所有符合条件的自动配置类 4. 添加断点,跟踪 **AutoConfigurationImportSelector** 源码:负责自动配置类加载,并选择那些生效 ~~~java public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, RourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered { protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } AnnotationAttributes attributes = getAttributes(annotationMetadata); // 获取自动配置类的类名 List configurations = getCandidateConfigurations(annotationMetadata, attributes); // 移除重复的 configurations = removeDuplicates(configurations); // 获取排除的 Set exclusions = getExclusions(annotationMetadata, attributes); // 检查排除的,确保排除的类不会被加载 checkExcludedClasses(configurations, exclusions); // 移除需要排除的自动配置类 configurations.removeAll(exclusions); // 进行筛选,选择只需要的自动配置类。在这里会读取@ConditionalOnxxx的注解进行处理 configurations = getConfigurationClassFilter().filter(configurations); // fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); } /** * 获取自动配置类的全类名 */ protected List getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { List configurations = ImportCandidates.load(AutoConfiguration.class, getBeanClassLoader()) .getCandidates(); Assert.notEmpty(configurations, "No auto configuration classes found in " + "META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you " + "are using a custom packaging, make sure that file is correct."); return configurations; } } public final class ImportCandidates implements Iterable { /** * 获取自动配置类信息的地址 */ private static final String LOCATION = "META-INF/spring/%s.imports"; public static ImportCandidates load(Class annotation, ClassLoader classLoader) { Assert.notNull(annotation, "'annotation' must not be null"); ClassLoader classLoaderToUse = decideClassloader(classLoader); // 拼接全类名 // location = "META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports" String location = String.format(LOCATION, annotation.getName()); Enumeration urls = findUrlsInClasspath(classLoaderToUse, location); List importCandidates = new ArrayList<>(); while (urls.hasMoreElements()) { URL url = urls.nextElement(); importCandidates.addAll(readCandidateConfigurations(url)); } return new ImportCandidates(importCandidates); } } ~~~ #### 自动配置类处理的事情 **自动配置类导入了一堆相关的组件(一个组件一个功能),而每个组件获取配置时都是从属性类中获取,而属性类恰好又和配置文件绑定。** 以 **SslAutoConfiguration** 自动配置类为例 ~~~java /** * 通过AutoConfigurationImportSelector#getAutoConfigurationEntry()获取到对应的需要创建的XxxAutoConfiguration的类 */ // 标记为自动配置类,让springBoot进行处理。 // 在AutoConfigurationImportSelector#getAutoConfigurationEntry()方法中指定了使用AutoConfiguration注解,去加载这个注解标注的类 @AutoConfiguration // springboot中约定的配置,使用这个注解的话表示启用ConfigurationProperties注解 // ConfigurationProperties注解表示读取配置文件中的信息放入类中,并且交给IoC容器进行管理 // 读取配置文件给SslProperties类进行初始化 @EnableConfigurationProperties(SslProperties.class) public class SslAutoConfiguration { // 自动的配置,创建相关联的bean和属性 } // 导入EnableConfigurationPropertiesRegistrar类作为bean加入IoC容器中,并作用于当前类 // 和@Bean作用类似 @Import(EnableConfigurationPropertiesRegistrar.class) public @interface EnableConfigurationProperties { } ~~~ #### 自动配置的原理总结 1. 运行环境准备阶段 2. 引入web启动器 3. 传递引用了自动配置的jar包 4. 自动配置的jar包中有152个自动配置类,运行环境准备完毕 2. 运行阶段 3. @EnableAutoConfiguration启用自动配置,将152个自动配置类全部加载到IoC容器中,然后根据开发场景筛选出必须的自动配置类 4. 自动配置类加载了一堆组件 5. 每个组件需要的数据来自属性类 6. 属性类又和配置文件(application.yaml)绑定在一起 3. 因此,最终结果就是:导入启动器,配置配置文件,启动项目,就可以使用对应功能进行开发了 ### 自动配置的加载WebMvc中的AutoConfigurationImportSelector方法的实现流程 执行的方法顺序 ~~~text SpringApplication: ConfigurableApplicationContext run(Class primarySource, String... args); public static ConfigurableApplicationContext run(Class[] primarySources, String[] args); public ConfigurableApplicationContext run(String... args); private void refreshContext(ConfigurableApplicationContext context); protected void refresh(ConfigurableApplicationContext applicationContext); AbstractApplicationContext: public void refresh(); protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory); PostProcessorRegistrationDelegate: public static void invokeBeanFactoryPostProcessors; private static void invokeBeanDefinitionRegistryPostProcessors(Collection postProcessors, BeanDefinitionRegistry registry, ApplicationStartup applicationStartup); ConfigurationClassPostProcessor: public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry); public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) ConfigurationClassParser: public void parse(Set configCandidates); ~~~ 1. 程序从主入口进入,执行SpringApplication.run()方法 ~~~java @SpringBootApplication public class SSMApplication { public static void main(String[] args) { SpringApplication.run(SSMApplication.class, args); } } public class SpringApplication { 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); } public ConfigurableApplicationContext run(String... args) { // 创建容器 context = createApplicationContext(); // 刷新springIoC信息,spring容器已经在上面初始化完成,并放入了spring需要的核心bean工厂类信息 refreshContext(context); } private void refreshContext(ConfigurableApplicationContext context) { if (this.registerShutdownHook) { shutdownHook.registerApplicationContext(context); } refresh(context); } protected void refresh(ConfigurableApplicationContext applicationContext) { // 调用容器的刷新方法 applicationContext.refresh(); } } ~~~ 2. 刷新容器,将需要的bean注册到容器中 ~~~java public class ServletWebServerApplicationContext extends GenericWebApplicationContext implements ConfigurableWebServerApplicationContext { public final void refresh() throws BeansException, IllegalStateException { // 调用父类GenericWebApplicationContext的方法 super.refresh(); } } public class GenericWebApplicationContext extends GenericApplicationContext implements ConfigurableWebApplicationContext, ThemeSource {} // 设置beanFactory属性 public class GenericApplicationContext extends AbstractApplicationContext implements BeanDefinitionRegistry {} public abstract class AbstractApplicationContext extends DefaultResourceLoader implements ConfigurableApplicationContext { @Override public void refresh() throws BeansException, IllegalStateException { // 执行bean工厂的后置处理 invokeBeanFactoryPostProcessors(beanFactory); } protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) { PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors()); } } final class PostProcessorRegistrationDelegate{ public static void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory, List beanFactoryPostProcessors) { // 注册bean invokeBeanDefinitionRegistryPostProcessors(currentRegistryProcessors, registry, beanFactory.getApplicationStartup()); } private static void invokeBeanDefinitionRegistryPostProcessors( Collection postProcessors, BeanDefinitionRegistry registry, ApplicationStartup applicationStartup) { for (BeanDefinitionRegistryPostProcessor postProcessor : postProcessors) { postProcessor.postProcessBeanDefinitionRegistry(registry); } } } // bean注册接口 public interface BeanDefinitionRegistryPostProcessor extends BeanFactoryPostProcessor { void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException; } // 类配置的处理 public class ConfigurationClassPostProcessor implements BeanDefinitionRegistryPostProcessor{ @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { processConfigBeanDefinitions(registry); } public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { // 解析@Configuration注解标注的类 ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry); do { // candidates中存储的是springBoot的启动类,是通过SpringApplication.run(Class clazz, args)中传入的 // 启动类使用@SpringBootApplication进行标注的,而SpringBootApplication中又使用了@SpringBootConfiguration // @SpringBootConfiguration又使用了@Configuration进行标注 parser.parse(candidates); } while (!candidates.isEmpty()); } } ~~~ 3. 解析@Configuration注解。 1. 先获取@Import注解标注指定的ImportSelector类: ```java protected final void parse(AnnotationMetadata metadata, String beanName); protected void processConfigurationClass(ConfigurationClass configClass, Predicate filter); protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass, Predicate filter); private Set getImports(SourceClass sourceClass); private void collectImports(SourceClass sourceClass, Set imports, Set visited); ``` 2. 调用ImportSelector实现类的getImports方法获取指定要加载的类进行初始化 ```java public void parse(Set configCandidates); DeferredImportSelectorHandler{ public void process(); public void processGroupImports(); } private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection importCandidates, Predicate exclusionFilter, boolean checkForCircularImports); ``` ~~~java class ConfigurationClassParser { // 解析@Configuration标签 public void parse(Set configCandidates) { // 处理ImportSelector的实现类,其中就包含了核心类AutoConfigurationImportSelector类 this.deferredImportSelectorHandler.process(); } // 通过原注解解析获取配置类 protected final void parse(AnnotationMetadata metadata, String beanName) throws IOException { processConfigurationClass(new ConfigurationClass(metadata, beanName), DEFAULT_EXCLUSION_FILTER); } protected void processConfigurationClass(ConfigurationClass configClass, Predicate filter) throws IOException { // 通过注解获取对应的类 sourceClass = doProcessConfigurationClass(configClass, sourceClass, filter); } protected final SourceClass doProcessConfigurationClass( ConfigurationClass configClass, SourceClass sourceClass, Predicate filter) throws IOException { // 处理@Import注解 // 启动类使用@SpringBootApplication进行标注的,而SpringBootApplication中又使用了@EnableAutoConfiguration注解进行标注 // @EnableAutoConfiguration中使用了@Import(AutoConfigurationImportSelector.class)和@AutoConfigurationPackage // @AutoConfigurationPackage中使用了@Import(AutoConfigurationPackages.Registrar.class) processImports(configClass, sourceClass, getImports(sourceClass), filter, true); } // 返回所有带有Import注解指定需要导入的类,也就是:AutoConfigurationImportSelector和AutoConfigurationPackages.Registrar private Set getImports(SourceClass sourceClass) throws IOException { Set imports = new LinkedHashSet<>(); Set visited = new LinkedHashSet<>(); collectImports(sourceClass, imports, visited); return imports; } private void collectImports(SourceClass sourceClass, Set imports, Set visited) throws IOException { if (visited.add(sourceClass)) { for (SourceClass annotation : sourceClass.getAnnotations()) { String annName = annotation.getMetadata().getClassName(); // 判断注解是否为Import if (!annName.equals(Import.class.getName())) { collectImports(annotation, imports, visited); } } // 类包含有Import注解,将其放入集合中进行返回 imports.addAll(sourceClass.getAnnotationAttributes(Import.class.getName(), "value")); } } private void processImports(ConfigurationClass configClass, SourceClass currentSourceClass, Collection importCandidates, Predicate exclusionFilter, boolean checkForCircularImports) { try { for (SourceClass candidate : importCandidates) { if (candidate.isAssignable(ImportSelector.class)) { Class candidateClass = candidate.loadClass(); // 实例化bean ImportSelector selector = ParserStrategyUtils.instantiateClass(candidateClass, ImportSelector.class, this.environment, this.resourceLoader, this.registry); } } } catch (BeanDefinitionStoreException ex) { throw ex; } } private class DeferredImportSelectorHandler { // 存入importSelector的映射关系 private final Map groupings = new LinkedHashMap<>(); public void process() { List deferredImports = this.deferredImportSelectors; this.deferredImportSelectors = null; try { if (deferredImports != null) { DeferredImportSelectorGroupingHandler handler = new DeferredImportSelectorGroupingHandler(); deferredImports.sort(DEFERRED_IMPORT_COMPARATOR); // 将deferredImport注册到gropingHandler中 deferredImports.forEach(handler::register); // 调用处理器处理importSelectorHandler(AutoConfigurationImportSelector) handler.processGroupImports(); } } finally { this.deferredImportSelectors = new ArrayList<>(); } } public void processGroupImports() { for (DeferredImportSelectorGrouping grouping : this.groupings.values()) { Predicate exclusionFilter = grouping.getCandidateFilter(); // 调用AutoConfigurationImportSelector中的getImports()方法获取所有要加载的自动配置类全路径 grouping.getImports().forEach(entry -> { ConfigurationClass configurationClass = this.configurationClasses.get(entry.getMetadata()); try { // 该方法通过类的全路径名去通过反射获取到类,去进行对应的处理 processImports(configurationClass, asSourceClass(configurationClass, exclusionFilter), Collections.singleton(asSourceClass(entry.getImportClassName(), exclusionFilter)), exclusionFilter, false); } catch (Exception e) {} }); } } } } ~~~