济宁官方网站,国外购物网站建设,wordpress邀请 返佣,曼联官方发文文章目录 前言一、SpringBoot优势概要二、SpringBoot自动配置1. ☠注意☠2.自动配置详解 三、Starter#xff08;场景启动器#xff09;原理总结 前言
本文详细解析面试重点—SpringBoot自动配置原理、场景启动器原理#xff0c;深入源码#xff0c;直接上干货、绝不拖泥带… 文章目录 前言一、SpringBoot优势概要二、SpringBoot自动配置1. ☠注意☠2.自动配置详解 三、Starter场景启动器原理总结 前言
本文详细解析面试重点—SpringBoot自动配置原理、场景启动器原理深入源码直接上干货、绝不拖泥带水。 一、SpringBoot优势概要
一句话约定大于配置简化Spring的简化
内嵌Web服务器环境启动器starter的使用简化构建配置无需编写繁琐XML文件
二、SpringBoot自动配置
1. ☠注意☠
首先要明确自动配置和自动装配的区别。 自动装配是SpringBoot的概念是通过条件注解和配置元数据的方式根据应用程序的依赖和环境自动为应用程序进行配置。 自动装配是Spring的概念是Spring框架通过依赖注入Dependency Injection机制来实现的它通过自动根据类型或名称匹配来自动关联和装配Bean。
网络上有很多文章、视频张嘴就是SpringBoot的自动装配这些基本不用看了属实有些乐色
2.自动配置详解
创建一个SpringBoot2项目用作示例
主配置类
package com.example;import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;SpringBootApplication
public class SpringBootTestApplication {public static void main(String[] args) {SpringApplication.run(SpringBootTestApplication.class, args);}}POM文件
?xml version1.0 encodingUTF-8?
project xmlnshttp://maven.apache.org/POM/4.0.0 xmlns:xsihttp://www.w3.org/2001/XMLSchema-instancexsi:schemaLocationhttp://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsdmodelVersion4.0.0/modelVersionpackagingpom/packagingparentgroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-parent/artifactIdversion2.5.0/versionrelativePath/ !-- lookup parent from repository --/parentgroupIdcom.example/groupIdartifactIdSpringBoot-Test/artifactIdversion0.0.1-SNAPSHOT/versionnameSpringBoot-Test/namedescriptionSpringBoot-Test/descriptionpropertiesjava.version8/java.version/propertiesdependenciesdependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-web/artifactId/dependencydependencygroupIdorg.springframework.boot/groupIdartifactIdspring-boot-starter-test/artifactIdscopetest/scope/dependencybuildpluginsplugingroupIdorg.springframework.boot/groupIdartifactIdspring-boot-maven-plugin/artifactId/plugin/plugins/build/project
项目结构分析
通过主配置类可以看出SpringBoot的最重要注解是SpringBootApplication 通过POM文件可知SpringBoot项目的父项目是spring-boot-starter-parent
SpringBootApplication详解
在IDEA中按住CTRL键进入SpringBootApplication注解:
package org.springframework.boot.autoconfigure;import ...;Target({ElementType.TYPE})
Retention(RetentionPolicy.RUNTIME)
Documented
Inherited
SpringBootConfiguration
EnableAutoConfiguration
ComponentScan(excludeFilters {Filter(type FilterType.CUSTOM,classes {TypeExcludeFilter.class}
), Filter(type FilterType.CUSTOM,classes {AutoConfigurationExcludeFilter.class}
)}
)
public interface SpringBootApplication {AliasFor(annotation EnableAutoConfiguration.class)Class?[] exclude() default {};......
}
可以看到SpringBootApplication主要由 SpringBootConfiguration、EnableAutoConfiguration、ComponentScan注解构成
我们逐个分析以上三个注解
1. SpringBootConfiguration注解 源码
Target({ElementType.TYPE})
Retention(RetentionPolicy.RUNTIME)
Documented
Configuration
Indexed
public interface SpringBootConfiguration {AliasFor(annotation Configuration.class)boolean proxyBeanMethods() default true;
}可以看出该注解由Configuration注解和其他注解组成所以该注解的作用和Configuration注解作用相似代表当前是一个SpringBoot配置类。
2. 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 {};
}该注解包含的注解主要有:AutoConfigurationPackage、Import({AutoConfigurationImportSelector.class})
AutoConfigurationPackage源码:
Target({ElementType.TYPE})
Retention(RetentionPolicy.RUNTIME)
Documented
Inherited
Import({Registrar.class})
public interface AutoConfigurationPackage {String[] basePackages() default {};Class?[] basePackageClasses() default {};
}可以看出AutoConfigurationPackage通过内部的Import注解向IOC容器中导入了类 Registrar.class
Registrar.class源码 static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {Registrar() {}public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));}public SetObject determineImports(AnnotationMetadata metadata) {return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata));}}发现AutoConfigurationPackage利用Registrar给容器中导入一系列组件将主配置类所在包下的所有组件导入容器中。
Import({AutoConfigurationImportSelector.class})源码
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered{.......
}发现AutoConfigurationImportSelector实现了DeferredImportSelector接口这就不得不提一下Import注解的使用方式了可以看看这篇文章 Import三种使用方式 Import作用是向IOC容器中导入组建其中有一种就是实现ImportSelector接口并实现其中的selectImports方法该方法的作用是返回一个由想要导入IOC容器的对象组成的数组。
查看AutoConfigurationImportSelector中实现的selectImports方法:
public String[] selectImports(AnnotationMetadata annotationMetadata) {if (!this.isEnabled(annotationMetadata)) {return NO_IMPORTS;} else {AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry this.getAutoConfigurationEntry(annotationMetadata);return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());}}发现其中调用了getAutoConfigurationEntry方法继续跟进 protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {if (!this.isEnabled(annotationMetadata)) {return EMPTY_ENTRY;} else {AnnotationAttributes attributes this.getAttributes(annotationMetadata);ListString configurations this.getCandidateConfigurations(annotationMetadata, attributes);configurations this.removeDuplicates(configurations);SetString exclusions this.getExclusions(annotationMetadata, attributes);this.checkExcludedClasses(configurations, exclusions);configurations.removeAll(exclusions);configurations this.getConfigurationClassFilter().filter(configurations);this.fireAutoConfigurationImportEvents(configurations, exclusions);return new AutoConfigurationImportSelector.AutoConfigurationEntry(configurations, exclusions);}}很明显getAutoConfigurationEntry方法又调用了getCandidateConfigurations方法继续深入
protected ListString getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {ListString configurations SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader());Assert.notEmpty(configurations, No auto configuration classes found in META-INF/spring.factories. If you are using a custom packaging, make sure that file is correct.);return configurations;}发现getCandidateConfigurations调用了SpringFactoriesLoader的loadFactoryNames方法 public static ListString loadFactoryNames(Class? factoryType, Nullable ClassLoader classLoader) {ClassLoader classLoaderToUse classLoader;if (classLoader null) {classLoaderToUse SpringFactoriesLoader.class.getClassLoader();}String factoryTypeName factoryType.getName();return (List)loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList());}此方法又调用了loadSpringFactories方法
private static MapString, ListString loadSpringFactories(ClassLoader classLoader) {MapString, ListString result (Map)cache.get(classLoader);if (result ! null) {return result;} else {HashMap result new HashMap();try {Enumeration urls classLoader.getResources(META-INF/spring.factories);while(urls.hasMoreElements()) {URL url (URL)urls.nextElement();UrlResource resource new UrlResource(url);Properties properties PropertiesLoaderUtils.loadProperties(resource);Iterator var6 properties.entrySet().iterator();while(var6.hasNext()) {Entry?, ? entry (Entry)var6.next();String factoryTypeName ((String)entry.getKey()).trim();String[] factoryImplementationNames StringUtils.commaDelimitedListToStringArray((String)entry.getValue());String[] var10 factoryImplementationNames;int var11 factoryImplementationNames.length;for(int var12 0; var12 var11; var12) {String factoryImplementationName var10[var12];((List)result.computeIfAbsent(factoryTypeName, (key) - {return new ArrayList();})).add(factoryImplementationName.trim());}}}result.replaceAll((factoryType, implementations) - {return (List)implementations.stream().distinct().collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList));});cache.put(classLoader, result);return result;} catch (IOException var14) {throw new IllegalArgumentException(Unable to load factories from location [META-INF/spring.factories], var14);}}}可以看出其从META-INF/spring.factories位置来加载一个文件。默认扫描我们当前系统里面所有META-INF/spring.factories位置的文件。经过一层层返回之后使用Import注解导入到IOC容器 3. ComponentScan注解
ComponentScan(excludeFilters {Filter(type FilterType.CUSTOM,classes {TypeExcludeFilter.class}
), Filter(type FilterType.CUSTOM,classes {AutoConfigurationExcludeFilter.class}
)}此注解作用是默认扫描主配置类所在包及其子包中的Component注解及其衍生注解
三、Starter场景启动器原理
1. 什么是场景启动器
Spring Boot场景启动器Starter是Spring Boot框架提供的一种便利机制用于快速引入和配置特定场景下所需的依赖项。每个场景启动器都是一个独立的Maven项目它提供了一组相关的依赖和配置以便简化和加速特定场景下的应用程序开发。
2. 场景启动器特点
集成依赖项每个场景启动器都定义了一组相关的依赖项。这些依赖项被认为在特定场景中非常有用比如Web开发、数据库访问、消息队列等等。通过使用场景启动器开发者可以快速引入和配置所需的依赖项而无需手动添加和管理这些依赖项。自动配置场景启动器还提供了一些默认的自动配置旨在简化特定场景下的配置工作。这些自动配置通常基于Spring Boot的条件注解和配置元数据机制根据应用程序的依赖和环境来自动配置必要的组件和选项。通过使用场景启动器开发者可以快速启动和运行基本的应用程序而无需过多关注和编写配置代码。统一引入和管理场景启动器通过简化和标准化依赖项的引入和配置提供了一种统一的方式来解决常见的开发场景。开发者可以通过在项目的pom.xml文件中添加相应的场景启动器依赖一次性引入和管理所有相关的依赖项从而简化了项目的依赖管理和版本冲突的问题。
3. 以spring-boot-starter-web为示例
import ....;Configuration(proxyBeanMethods false
)
ConditionalOnWebApplication(type Type.SERVLET
)
ConditionalOnClass({Servlet.class, DispatcherServlet.class, WebMvcConfigurer.class})
ConditionalOnMissingBean({WebMvcConfigurationSupport.class})
AutoConfigureOrder(-2147483638)
AutoConfigureAfter({DispatcherServletAutoConfiguration.class, TaskExecutionAutoConfiguration.class, ValidationAutoConfiguration.class})
public class WebMvcAutoConfiguration {public static final String DEFAULT_PREFIX ;public static final String DEFAULT_SUFFIX ;private static final String SERVLET_LOCATION /;public WebMvcAutoConfiguration() {}BeanConditionalOnMissingBean({HiddenHttpMethodFilter.class})ConditionalOnProperty(prefix spring.mvc.hiddenmethod.filter,name {enabled})public OrderedHiddenHttpMethodFilter hiddenHttpMethodFilter() {return new OrderedHiddenHttpMethodFilter();}BeanConditionalOnMissingBean({FormContentFilter.class})ConditionalOnProperty(prefix spring.mvc.formcontent.filter,name {enabled},matchIfMissing true)public OrderedFormContentFilter formContentFilter() {return new OrderedFormContentFilter();}static class OptionalPathExtensionContentNegotiationStrategy implements ContentNegotiationStrategy {private static final String SKIP_ATTRIBUTE PathExtensionContentNegotiationStrategy.class.getName() .SKIP;private final ContentNegotiationStrategy delegate;OptionalPathExtensionContentNegotiationStrategy(ContentNegotiationStrategy delegate) {this.delegate delegate;}public ListMediaType resolveMediaTypes(NativeWebRequest webRequest) throws HttpMediaTypeNotAcceptableException {Object skip webRequest.getAttribute(SKIP_ATTRIBUTE, 0);return skip ! null Boolean.parseBoolean(skip.toString()) ? MEDIA_TYPE_ALL_LIST : this.delegate.resolveMediaTypes(webRequest);}}static class ResourceChainResourceHandlerRegistrationCustomizer implements WebMvcAutoConfiguration.ResourceHandlerRegistrationCustomizer {private final Resources resourceProperties;ResourceChainResourceHandlerRegistrationCustomizer(Resources resourceProperties) {this.resourceProperties resourceProperties;}
以上文中的SpringBoot自动配置为基础可知SpringBoot会扫描我们引入的依赖中的META-INF目录下的spring.factories文件中以EnableAutoConfiguration为key的值将其加入到IOC容器中但是并不是所有的对象都会加入到IOC容器中应为这些starter的主配置类中都会有Conditional注解来指定约束条件
ConditionalOnBean当容器里有指定 Bean 的条件下ConditionalOnMissingBean当容器里没有指定 Bean 的情况下ConditionalOnSingleCandidate当指定 Bean 在容器中只有一个或者虽然有多个但是指定首选 BeanConditionalOnClass当类路径下有指定类的条件下ConditionalOnMissingClass当类路径下没有指定类的条件下ConditionalOnProperty指定的属性是否有指定的值ConditionalOnResource类路径是否有指定的值ConditionalOnExpression基于 SpEL 表达式作为判断条件ConditionalOnJava基于 Java 版本作为判断条件ConditionalOnJndi在 JNDI 存在的条件下差在指定的位置ConditionalOnNotWebApplication当前项目不是 Web 项目的条件下ConditionalOnWebApplication当前项目是 Web 项 目的条件下
4. starter整体结构 xxxAutoConfigurationstarter中的主配置类其中定义了特定条件和默认配置文件的指定 xxxProperties默认文件可以在我们使用starter的时候进行配置覆盖这里重点体现了springboot的约定大于配置。
总结
SpringBoot 使用EnableAutoConfiguration开启自动配置功能通过Import注解加载实现了ImportSelector接口的类并最终加载META-INF/spring.factories中的自动配置类实现自动装配自动配置类其实就是通过Conditional按需加载的配置类想要其生效必须引入spring-boot-starter-xxx包实现起步依赖。