赞
踩
Spring Boot通过@EnableAutoConfiguration注解开启自动配置,对jar包下的spring.factories文件进行扫描,这个文件中包含了可以进行自动配置的类,当满足@Condition注解指定的条件时,便在依赖的支持下进行实例化,注册到Spring容器中。
作用:标注在某个类上说明这个类是SpringBoot的主配置类 , SpringBoot就应该运行这个类的main方法来启动SpringBoot应用;
@SpringBootApplication是一个复合注解或派生注解,进入该注解:
该类主要包括三个注解:
作用:SpringBoot的配置类,标注在某个类上,表示这是一个SpringBoot的配置类。
进入查看源码:
@Configuration标注在某个类上,表示这是一个 springboot的配置类,可以向容器中注入组件
作用:自动扫描组件,默认扫描该类所在包及其子包下所有带有指定注解的类,将它们自动装配到bean容器中,会被自动装配的注解包括@Controller、@Service、@Component、@Repository等。也可以指定扫描路径。
开启自动配置功能,工程中需要配置的东西,SpringBoot会帮我们自动配置;@EnableAutoConfiguration告诉SpringBoot开启自动配置功能。这样自动配置才会生效。
进入注解查看:
可以发现这个注解也是个复合注解。其中有两个较为重要的注解:
自动导入配置包
@import :Spring底层注解@import , 给容器中导入一个组件
进入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 Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new AutoConfigurationPackages.PackageImports(metadata));
}
}
打断点测试可以得到:
(String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0])的值为com.ljw.springbootwork:当前启动类所在的包名。
总结:@AutoConfigurationPackage就是将主启动类的所在包及包下面所有子包里面的所有组件扫描到Spring容器中 ;
给容器导入组件
AutoConfigurationImportSelector.class:开启自动配置类的导包的选择器,即是导入哪些类
进入AutoConfigurationImportSelector.class查看源码:
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationImportSelector.AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
根据导入的@Configuration类的AnnotationMetadata返回AutoConfigurationImportSelector.AutoConfigurationEntry
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); } }
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> 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;
}
Assert(断言):在META-INF/spring.factories中没有找到自动配置类。如果您正在使用自定义打包,请确保该文件是正确的。
所以loadFactoryNames()方法要找到自动配置类返回才不会报错。
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
结论:获取一个加载自动配置类的类,即SpringBoot默认自动配置类为EnableAutoConfiguration
getCandidateConfigurations()方法又调用了SpringFactoriesLoader类的静态方法!我们进入SpringFactoriesLoader类loadFactoryNames() 方法。
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
//这里它又调用了 loadSpringFactories 方法
return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) { //获得classLoader , 我们返回可以看到这里得到的就是EnableAutoConfiguration标注的类本身 MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader); if (result != null) { return result; } else { try { //去获取一个资源 "META-INF/spring.factories" Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories"); LinkedMultiValueMap result = new LinkedMultiValueMap(); //将读取到的资源遍历,封装成为一个Properties 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 factoryClassName = ((String)entry.getKey()).trim(); String[] var9 = StringUtils.commaDelimitedListToStringArray((String)entry.getValue()); int var10 = var9.length; for(int var11 = 0; var11 < var10; ++var11) { String factoryName = var9[var11]; result.add(factoryClassName, factoryName.trim()); } } } cache.put(classLoader, result); return result; } catch (IOException var13) { throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", var13); } } }
该方法作用是加载所有依赖的路径META-INF/spring.factories文件,通过map结构保存,key为文件中定义的一些标识工厂类,value就是能自动配置的一些工厂实现的类,value用list保存并去重
打开spring.factories , 看到了很多自动配置的文件;这就是自动配置根源所在.
这个@EnableAutoConfiguration注解通过@SpringBootApplication被间接的标记在了Spring Boot的启动类上。在SpringApplication.run(…)的内部就会执行selectImports()方法,找到所有JavaConfig自动配置类的全限定名对应的class,然后将所有自动配置类加载到Spring容器中。
自动配置类必须在一定的条件下才能生效使用,虽然在SpringBoot中加载了很多的自动配置,但是并不是每个自动配置类都会生效,但是在使用的时候需要在一定条件下才会生效,这个就需要通过使用这些判断条件来实现。
依赖于Configuration类中的@Conditional注解,注解指定的条件成立时,才会给容器中添加组件,yml文件中的配置才会生效。
我们如何知道哪些配置类生效了?
在yml文件中写上debug=true属性,来让控制台打印自动配置报告,控制台会显示对应信息。
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。