关于springboot的自动配置,简单来说就是为了从spring.factories文件中获取到对应的需要进行自动装配的类,并生成相应的Bean对象,然后将它们交给spring容器来帮我们进行管理。
众所周知,springboot框架中集成了很多我们常用到的技术,如持久层的JDBC技术,视图层的SpringMVC技术等,如果没有自动配置,就需要我们人为去手动配置,这样一来就会很麻烦,而通过自动配置,就很好的解决了这个问题。当我们创建一个Springboot项目时,springboot框架会自动帮我们配置好框架所集成的所有技术,我们只管使用即可,从而也大大提升了我们的开发效率。
这个注解是springboot项目主启动类上的一个注解,是一个组合注解,也就是由其他注解组合起来,它的主要作用就是标记说明这个类是springboot的主启动类,springboot应该运行这个类里面的main()方法来启动程序。
@SpringBootApplication
public class Enable04Application {
public static void main(String[] args) {
SpringApplication.run(Enable04Application.class, args);
}
}
@SpringBootApplication注解中有几个子注解,我们一起分析一下:
@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 {
《1》@SpringBootConfiguration:
这个注解包含了@Configuration,@Configuration里面又包含了一个@Component注解,也就是说,这个注解标注在哪个类上,就表示当前这个类是一个配置类,而配置类也是spring容器中的组件。
//@SpringBootConfiguration注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
//@SpringBootConfiguration注解中的子注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface Configuration {
@AliasFor(
annotation = Component.class
)
String value() default "";
boolean proxyBeanMethods() default true;
}
此注解是springboot框架中实现自动配置最关键的注解,主要用于开启自动配置。
此注解主要用于自动扫描符合条件的组件或bean对象,并将其注入IOC容器中,这个注解我们也曾在spring框架中单独使用过,主要用于通过注解指定spring在创建容器时要扫描的包。
上面我们说过了,此注解用于开启springboot框架的自动配置,在此注解中有几个子注解,
我们一起分析一下:
//@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 {};
}
此注解主要用于自动配置包:将启动类同级所在包及此包下所有组件都自动扫描到容器中,这也是我们在springboot项目中将所有代码都写在启动类所在的包下的原因。
//@AutoConfigurationPackage注解详情
//主要通过@import注解指向的配置类完成扫描到的包内的组件注入IOC容器中
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
此注解主要用于通过AutoConfigurationImportSelector类中的selectImports()方法将方法的返回值注入容器中。
我们前面了解过@import注解的用法中有一种用法就是导入ImportSelector实现类:根据实现类中重写方法的返回值String类型数组中的内容,导入对应的类。而对于上面的类我们通过类名也能发现它是ImportSelector接口的实现类,因此在此@import注解的作用也是如此。
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
此方法主要用于将方法返回值对应的配置类导入容器中,但是要导入的配置类 是怎么找到的呢?
在 selectImports()方法中调用了一个getAutoConfigurationEntry()方法
此方法主要用于获取自动配置的入口信息,这些信息将用于决定哪些自动配置类应该被加载到 Spring 容器中。
//getAutoConfigurationEntry方法详情:
protected AutoConfigurationImportSelector.AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
Set<String> 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(annotationMetadata, attributes)方法
此方法确保了系统能够找到并考虑所有可能的自动配置选项,以便根据应用程序的实际情况进行智能的配置选择 。
//getCandidateConfigurations方法详情:
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = new ArrayList(SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader()));
ImportCandidates.load(AutoConfiguration.class, this.getBeanClassLoader()).forEach(configurations::add);
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories nor 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;
}
在此方法中又调用了loadFactoryNames():
//loadFactoryNames方法详解
public static List<String> 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());
}
在loadFactoryNames()方法中又调用了loadSpringFactories()方法:
//loadSpringFactories方法详情:
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) {
Map<String, List<String>> 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);
}
}
}
重点就在这个loadSpringFactories()方法中,在此方法中它会查找所有在META-INF路
径下的spring.factories文件 ,此文件中的内容都是以键值对的形式存在。其中有一个以EnableAutoConfiguration为key,以要加载的配置类的全路径为value的键值对,方法通过加载此键值对进而加载注入springboot自动配置的多个配置类,实现springboot的自动配置。
1.通过@SpringBootApplication注解声明springboot项目的启动类
2.通过@SpringBootConfiguration注解声明此类也是一个配置类
3.通过@EnableAutoConfiguration注解开启springboot的自动配置
4.通过@Import({AutoConfigurationImportSelector.class})注解将要自动配置的组件注入容 器 中。
5. 通过AutoConfigurationImportSelector类中的selectImports()方法完成自动配置组件的 注 入。
6.在selectImports()方法中调用getAutoConfigurationEntry()方法。
7.在getAutoConfigurationEntry()中又调用getCandidateConfigurations(annotationMetadata, attributes)方法。
8.在上面方法中又调用了loadFactoryNames()方法。
9.在loadFactoryNames()方法中又调用了loadSpringFactories()。
10. 在loadSpringFactories()方法中会找到需要加载的配置类的位置:在META-INF路径下 的 spring.factories文件中以EnableAutoConfiguration为key所映射的value值,此value 值 即表示springboot自动配置时所加载的所有配置类。
(1)如果新建的springboot项目中有新的坐标添加,则将坐标对应的配置类也一并加载。
(2)虽然这些配置类都会加载,但是否初始化bean还取决于这些配置类上的@condition注解 的 判断结果,而并非全部初始化bean对象。
(3)自动配置所加载的配置类的位置可能不一定在上述路径位置
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you are using a custom packaging, make sure that file is correct.");
上述代码说明了此配置类可能还存在于另一个位置:
META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports.
此文件中的所有内容即是springboot框架在自动配置时所加载的所有配置类的全类名。
因篇幅问题不能全部显示,请点此查看更多更全内容