springboot 自动配置类原理
一、环境 springboot2.17 版本
二、 @SpringBootApplication注解主体、主要的结构图如下(省略@)
SpringBootApplicationSpringBootConfigurationConfigurationComponentComponentScanEnableAutoConfigurationAutoConfigurationPackageImportImport三、源码
@SpringBootApplication // 来标注一个主程序类,说明这是一个Spring Boot应用 public class SpringBootDemo01Application { public static void main(String[] args) { SpringApplication.run(SpringBootDemo01Application.class, args); } }
1、@SpringBootApplication注解组成如下:
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited //这是一个springboot的配置类,底层有@Configuration,可以跟@Configuration互换,底层自带@component @SpringBootConfiguration //开启自动配置,导入场景需要的自动配置类到容器中,并配置类参数,所以我们可以少配置 @EnableAutoConfiguration //组件扫描,没有配置包,只能注解注入@Controller、@Service、@Configuration、@Componen、@Repository //excludeFilters是设置不注入哪些bean,@Filter设置过滤条件,FilterType.CUSTOM是自定义过滤条件 @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication { }
2、@SpringBootConfiguration包含:@Configuration (包含@Configuration)如下:
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented //标注在配置类上,表示这是一个配置类,相当于一个applicationContext.xml文件,里面的bean标签也相当于 //-->配置类中的@bean,底层还包含@Component,所以会注入到容器中 @Configuration public @interface SpringBootConfiguration { ===================================================================== @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Component//组件,扫描到容器中 public @interface Configuration {
3、@EnableAutoConfiguration组成由:
@Import(AutoConfigurationImportSelector.class)和@AutoConfigurationPackage
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited //自动配置包,将SpringBootDemo01Application的包目录及子目录下组件扫描到容器,前面@ComponentScan //-->没有配置路径 @AutoConfigurationPackage //核心场景自动配置类注入 @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration {
4、 @AutoConfigurationPackage中的 Registrar.class
/** * {@link ImportBeanDefinitionRegistrar} to store the base package from the importing * configuration. */ static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports { @Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { //获取到注解位置信息,及springboot应用的目录 register(registry, new PackageImport(metadata).getPackageName()); } @Override public Set<Object> determineImports(AnnotationMetadata metadata) { return Collections.singleton(new PackageImport(metadata)); } }
5、 @Import(AutoConfigurationImportSelector.class)
//将所有需要导入的组件以全类名的方式返回;这些组件就会被添加到容器中;会给容器中导入非常多的自动配置 //-->类(xxxAutoConfiguration);就是给容器中导入这个场景需要的所有组件,并配置好这些组件 @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this.beanClassLoader); //获取自动配置类key-value的entry信息 AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); } protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } AnnotationAttributes attributes = getAttributes(annotationMetadata); //获取所有全类名,spring-boot-autoconfigure-2.1.7.RELEASE.jar的META-INF/spring.factories List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes); configurations = removeDuplicates(configurations); Set<String> exclusions = getExclusions(annotationMetadata, attributes); checkExcludedClasses(configurations, exclusions); configurations.removeAll(exclusions); configurations = filter(configurations, autoConfigurationMetadata); fireAutoConfigurationImportEvents(configurations, exclusions); return new AutoConfigurationEntry(configurations, exclusions); }
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) { //核心是通过springFactoriesLoader机制加载以EnableAutoConfiguration注解全限定类名为key的class。 //这里打断点 List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(), 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; } /** * Return the class used by {@link SpringFactoriesLoader} to load configuration * candidates. * @return the factory class */ protected Class<?> getSpringFactoriesLoaderFactoryClass() { //核心是通过springFactoriesLoader机制加载以EnableAutoConfiguration注解全限定类名为key的class。 return EnableAutoConfiguration.class; }
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) { //factoryClassName=org.springframework.boot.autoconfigure.EnableAutoConfiguration String factoryClassName = factoryClass.getName(); return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList()); } private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { MultiValueMap<String, String> result = cache.get(classLoader); if (result != null) { return result; } try { // 扫描所有jar包下的META-INF/spring.factories,其实就是spring-boot-autoconfigure-2.1.7.RELEASE.jar //-->才有,因为它的key是EnableAutoConfiguration Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); result = new LinkedMultiValueMap<>(); while (urls.hasMoreElements()) { URL url = urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry<?, ?> entry : properties.entrySet()) { String factoryClassName = ((String) entry.getKey()).trim(); for (String factoryName : StringUtils.commaDelimitedListToStringArray((String) entry.getValue())) { result.add(factoryClassName, factoryName.trim()); } } } cache.put(classLoader, result); return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } }
将 类路径下 META-INF/spring.factories 里面配置的所有EnableAutoConfiguration的值加入到了容器中;
# 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.cloud.CloudServiceConnectorsAutoConfiguration,\ org.springframework.boot.autoconfigure.context.ConfigurationPropertiesAutoConfiguration,\ 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.cassandra.CassandraReactiveDataAutoConfiguration,\ org.springframework.boot.autoconfigure.data.cassandra.CassandraReactiveRepositoriesAutoConfiguration
DeBug调试如下,将全类名配置类注入容器的有118个
四、解析HttpEncodingAutoConfiguration自动配置类
@Configuration //表示这是一个配置类,以前编写的配置文件一样,也可以给容器中添加组件 //启动指定类的ConfigurationProperties功能;将配置文件中对应的值和HttpEncodingProperties绑定起来; //-->并把HttpEncodingProperties加入到ioc容器中 @EnableConfigurationProperties(HttpEncodingProperties.class) //Spring底层@Conditional注解,只有满足指定的条件,整个配置类里面 //-->的配置就会生效; 判断当前应用是否是web应用,如果是,当前配置类生效 @ConditionalOnWebApplication //项目必须有CharacterEncodingFilter乱码解决的过滤器的这个类,才生效这个配置类; @ConditionalOnClass(CharacterEncodingFilter.class) //判断配置文件中是否存在某个配置 spring.http.encoding.enabled; //matchIfMissing表示没有的时候默认true,判断也是成立的 @ConditionalOnProperty(prefix = "spring.http.encoding", value = "enabled", matchIfMissing = true) public class HttpEncodingAutoConfiguration { //他已经和SpringBoot的配置文件映射了 private final HttpEncodingProperties properties; //只有一个有参构造器的情况下,参数的值就会从容器中拿 public HttpEncodingAutoConfiguration(HttpEncodingProperties properties) { this.properties = properties; } @Bean //给容器中添加一个组件,这个组件的某些值需要从properties中获取 @ConditionalOnMissingBean(CharacterEncodingFilter.class) //判断容器没有这个组件? public CharacterEncodingFilter characterEncodingFilter() { CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter(); filter.setEncoding(this.properties.getCharset().name()); filter.setForceRequestEncoding(this.properties.shouldForce(Type.REQUEST)); filter.setForceResponseEncoding(this.properties.shouldForce(Type.RESPONSE)); return filter; }
如果配置类生效;这个配置类就会给容器中添加各种组件;这些组件的属性是从对应的properties类中获取的,这些类里面的每一个属性又是和配置文件绑定的(@EnableConfigurationProperties(HttpEncodingProperties.class) );我们可以在application.yml或application.properties文件中配置
/从配置文件中获取指定的值和bean的属性进行绑定 @ConfigurationProperties(prefix = "spring.http.encoding") / public class HttpEncodingProperties { public static final Charset DEFAULT_CHARSET = Charset.forName("UTF‐8");
@Conditional派生注解(Spring注解版原生的@Conditional作用)
作用:必须是@Conditional指定的条件成立,才给容器中添加组件,配置配里面的所有内容才生效;
@Conditional扩展注解 | 作用(判断是否满足当前指定条件) |
---|---|
@ConditionalOnJava | 系统的java版本是否符合要求 |
@ConditionalOnBean | 容器中存在指定Bean; |
@ConditionalOnMissingBean | 容器中不存在指定Bean; |
@ConditionalOnExpression | 满足SpEL表达式指定 |
@ConditionalOnClass | 系统中有指定的类 |
@ConditionalOnMissingClass | 系统中没有指定的类 |
@ConditionalOnSingleCandidate | 容器中只有一个指定的Bean,或者这个Bean是首选Bean |
@ConditionalOnProperty | 系统中指定的属性是否有指定的值 |
@ConditionalOnResource | 类路径下是否存在指定资源文件 |
@ConditionalOnWebApplication | 当前是web环境 |
@ConditionalOnNotWebApplication | 当前不是web环境 |
@ConditionalOnJndi | JNDI存在指定项 |
五、总结:
1、Spring Boot在启动的时候从类路径下的META-INF/spring.factories中获取EnableAutoConfiguration为key,对应value的值,将这些值作为自动配置类导入到容器中,自动配置类就生效,帮我们进行自动配置工作;以前我们需要自己配置的东
西,自动配置类都帮我们;
- SpringBoot学习笔记(3) Spring Boot 运行原理,自动配置
- 全面解析SpringBoot自动配置的实现原理
- SpringBoot自动配置原理
- SpringBoot学习_SpringMVC自动配置原理
- 超简单的springboot自动配置原理分析
- Spring Boot之自动配置原理以及自定义starter
- Spring Boot核心原理-自动配置
- spring boot自动配置原理
- Spring Boot自动配置原理(转)
- SpringBoot学习 (四) - 自动配置原理 静态资源的映射规则 模板引擎 SpringMVC自动配置-扩展-全面接管
- Spring Boot核心原理-自动配置
- 【springboot】基于springboot运行原理实现springboot的自动配置
- SpringBoot之自动配置原理
- 三、SpringBoot——自动配置原理
- SpringBoot自动配置的实现原理
- Spring Boot自动配置原理
- Spring Boot核心原理-自动配置
- SpringBoot自动配置原理(转)
- SpringBoot学习笔记(3) Spring Boot 运行原理,自动配置
- Spring boot 自动配置原理