您的位置:首页 > 编程语言 > Java开发

springboot 自动配置类原理

2019-08-07 22:35 555 查看
版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明。 本文链接:https://blog.csdn.net/jue6628/article/details/98762224

一、环境 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的值,将这些值作为自动配置类导入到容器中,自动配置类就生效,帮我们进行自动配置工作;以前我们需要自己配置的东
西,自动配置类都帮我们;

官方文档
spring boot 源码分析

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: