SpringBoot自动装配简述 最近在学习SpringBoot的相关技术,因为还没有很认真的阅读源码,所以对于自动装配进行一个简单的总结。
xxx-spring-boot-starter
自动装配 springboot
和spring
最大的区别就是springboot
的开箱即用,直接在pom
文件中引入对应的依赖启动就可以了,比如引入springmvc
我们可以引入spring-boot-starter-web
来一起和springmvc
相关的一些依赖,包括我们使用文件上传的commons-io
等等,就不用像spring
中一个一个的引入依赖了。
那么这种不用配置xml
并且自动装配的原理又是什么呢?在SpringBoot中,有一种自动装配的过程,是通过在文件classpath:/META-INF/spring.factories
中添加配置org.springframework.boot.autoconfigure.EnableAutoConfiguration
的方式来配置Bean
。换句话说,我们引入的依赖中会在对应文件classpath:/META-INF/spring.factories
中添加配置org.springframework.boot.autoconfigure.EnableAutoConfiguration
,SpringBoot会根据这个配置里面的字符串(包的全路径名)来反射,创建Bean
。
自动装配,还得是从我们的入口来看,我们进入SpringBootApplication
注解。
再进入EnableAutoConfiguration
注解。
可以看见这里面我们import
了一个Bean
叫做AutoConfigurationImportSelector
。
那么我们直接看EnableAutoConfiguration
功能实现的类org.springframework.boot.autoconfigure.AutoConfigurationImportSelector
, AutoConfigurationImportSelector
依赖于方法selectImports
实现,方法如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 @Override public String[] selectImports(AnnotationMetadata annotationMetadata) { if (!isEnabled(annotationMetadata)) { return NO_IMPORTS; } AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader.loadMetadata(this .beanClassLoader); 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 StringUtils.toStringArray(configurations); }
后面的都可以忽略,最主要的就是List<String> configurations = getCandidateConfigurations(annotationMetadata,attributes);
这段代码,这段代码用来获取我们所有的/META-INF/spring.factories
中key
值为org.springframework.boot.autoconfigure.EnableAutoConfiguration
包全路径名的List
。
进一步分析getCandidateConfigurations
方法。
1 2 3 4 5 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; }
这里看第3行代码,可以关联到上小结提到的SpringFactoriesLoader
类,类SpringFactoriesLoader#loadFactoryNames
方法定义如下:这里面我们传入的factoryType
就是org.springframework.boot.autoconfigure.EnableAutoConfiguration
。
1 2 3 4 5 6 7 8 9 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()); }
继续深挖loadSpringFactories
方法:我们可以看见这里面就是读取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 29 30 31 32 33 34 35 36 37 38 39 40 41 42 private static Map<String, List<String>> loadSpringFactories (ClassLoader classLoader) { Map<String, List<String>> result = (Map)cache.get(classLoader); if (result != null ) { return result; } else { Map<String, List<String>> result = new HashMap (); try { Enumeration<URL> 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()) { Map.Entry<?, ?> entry = (Map.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); } } }
classLoader.getResources("META-INF/spring.factories");
这个是获取所有的spring.factories