前言 通常来说,我们接触过的SpringBoot程序,只需要运行主启动类的main方法就可以启动应用了。
1 2 3 4 5 6 @SpringBootApplication public class DemoApplication { public static void main (String[] args) { SpringApplication.run(DemoApplication.class, args); } }
所以来研究一下@SpringBootApplication注解。
进入@SpringBootApplication 1 2 3 4 5 @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication {}
可见,在启动类上标注的注解应该是三个:@Configuration + @EnableAutoConfiguration + @ComponentScan,组合而来的。 我们挨个来看他们都起什么作用:
@ComponentScan 这个注解Spring框架里就有了,它可以指定包扫描的跟路径,让Spring来扫描指定的包及其子包下的组件,如果不指定路径,默认扫描当前配置了所在包及其子包里的所有组件。
@SpringBootConfiguration 它被@Configuration标注,说明它实际上是标注配置类的,而且是标注主启动类的。被它标注的类,会被IOC容器认定为配置类。 一个被@Configuration标注的类,相当于一个applicationContext.xml的配置文件。
@EnableAutoConfiguration 重点来了,SpringBoot的自动装配完全由它来开启,所以下面重点看看它。
@EnableAutoConfiguration 1 2 3 @AutoConfigurationPackage @Import(AutoConfigurationImportSelector.class) public @interface EnableAutoConfiguration {}
可见它也是一个组合注解,分别来看。
@AutoConfigurationPackage 1 2 @Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage {}
它的实现原理是在注解上标注了@Import,导入了一个AutoConfigurationPackages.Registrar。
AutoConfigurationPackages.Registrar 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 static class Registrar implements ImportBeanDefinitionRegistrar , DeterminableImports { @Override public void registerBeanDefinitions (AnnotationMetadata metadata, BeanDefinitionRegistry registry) { register(registry, new PackageImport (metadata).getPackageName()); } @Override public Set<Object> determineImports (AnnotationMetadata metadata) { return Collections.singleton(new PackageImport (metadata)); } }
很明显,它就是实现把主配置所在根包保存起来以便后期扫描用的。分析源码:Registrar实现了ImportBeanDefinitionRegistrar接口,它向IOC容器中要手动注册组件。
在重写的registerBeanDefinitions方法中,它要调用外部类AutoConfigurationPackages的register方法。
register方法 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 private static final String BEAN = AutoConfigurationPackages.class.getName();public static void register (BeanDefinitionRegistry registry, String... packageNames) { if (registry.containsBeanDefinition(BEAN)) { BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN); ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues(); constructorArguments.addIndexedArgumentValue(0 , addBasePackages(constructorArguments, packageNames)); } else { GenericBeanDefinition beanDefinition = new GenericBeanDefinition (); beanDefinition.setBeanClass(BasePackages.class); beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0 , packageNames); beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); registry.registerBeanDefinition(BEAN, beanDefinition); } }
划重点:它要判断当前IOC容器中是否包含AutoConfigurationPackages。如果有,就会拿到刚才传入的包名,设置到一个 basePackage里,basePackage的意义很明显是根包。
换句话说,它要取主启动类所在包及子包下的组件,用于之后进行的扫描。
@Import(AutoConfigurationImportSelector.class)
它导入了一个ImportSelector,来向容器中导入组件。
AutoConfigurationImportSelector 它的核心部分就是selectImport方法:
1 2 3 4 5 6 7 8 9 10 11 12 13 @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader .loadMetadata(this .beanClassLoader); AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata); return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations()); }
关键的源码在getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata)
getAutoConfigurationEntry(autoConfigurationMetadata, annotationMetadata) 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 protected AutoConfigurationEntry getAutoConfigurationEntry (AutoConfigurationMetadata autoConfigurationMetadata, AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return EMPTY_ENTRY; } AnnotationAttributes attributes = getAttributes(annotationMetadata); 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); }
这个方法里有一个非常关键的集合:configurations(最后直接拿他来返回出去了,给selectImports方法转成 String[])
既然最后拿它返回出去,必然它是导入其他组件的核心。
这个 configurations 集合的数据,都是通过 getCandidateConfigurations 方法来获取:
1 2 3 4 5 6 7 8 9 10 11 12 protected Class<?> getSpringFactoriesLoaderFactoryClass() { return EnableAutoConfiguration.class; } protected List<String> getCandidateConfigurations (AnnotationMetadata metadata, AnnotationAttributes attributes) { 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; }
这个方法又调用了SpringFactoriesLoader.loadFactoryNames方法,传入的Class就是@EnableAutoConfiguration
SpringFactoriesLoader.loadFactoryNames 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories" ;public static List<String> loadFactoryNames (Class<?> factoryClass, @Nullable ClassLoader classLoader) { 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 { 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); } }
源码中使用classLoader去加载了指定常量路径下的资源:FACTORIES_RESOURCE_LOCATION,而这个常量指定的路径实际是:META-INF/spring.factories。
这个文件在spring-boot-autoconfiguration包下可以找到。
spring-boot-autoconfiguration包下META-INF/spring.factories节选:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 # Initializers org.springframework.context.ApplicationContextInitializer=\ org.springframework.boot.autoconfigure.SharedMetadataReaderFactoryContextInitializer,\ org.springframework.boot.autoconfigure.logging.ConditionEvaluationReportLoggingListener # Application Listeners org.springframework.context.ApplicationListener=\ org.springframework.boot.autoconfigure.BackgroundPreinitializer # Auto Configuration Import Listeners org.springframework.boot.autoconfigure.AutoConfigurationImportListener=\ org.springframework.boot.autoconfigure.condition.ConditionEvaluationReportAutoConfigurationImportListener # Auto Configuration Import Filters org.springframework.boot.autoconfigure.AutoConfigurationImportFilter=\ org.springframework.boot.autoconfigure.condition.OnBeanCondition,\ org.springframework.boot.autoconfigure.condition.OnClassCondition,\ org.springframework.boot.autoconfigure.condition.OnWebApplicationCondition # 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,\ ......
之后拿到这个资源文件,以Properties的形式加载,并取出org.springframework.boot.autoconfigure.EnableAutoConfiguration指定的所有自动配置类(是一个很大的字符串,里面都是自动配置类的全限定类名), 装配到IOC容器中,之后自动配置类就会通过ImportSelector和@Import的机制被创建出来, 之后就生效了。
这也就解释了为什么即便没有任何配置文件,SpringBoot的Web应用都能正常运行。
文章作者: 米兰
原始链接: https://blog.milanchen.site/posts/springboot-auto-configuration.html
版权声明: 转载请声明出处