要理解自动装配原理,首先要理解BeanDefinition的作用,在BeanDefinition里定义了一些属性,其实就是将Bean的定义信息存储到这个BeanDefinition相应的属性中,后面对Bean的操作就直接对BeanDefinition进行,例如拿到这个BeanDefinition后,可以根据里面的类名、构造函数、构造函数参数,使用反射进行对象创建,BeanDefinition是一个接口,是一个抽象的定义,实际使用的是其实现类,如 ChildBeanDefinition、RootBeanDefinition、GenericBeanDefinition等。
我们都知道@SpringBootApplication注解里面封装了@ComponentScan注解,这个注解默认情况下扫描main函数所在的包路径,把带有@Component, @Configuration, @Service, @Responsitory的类都加载到Spring 容器中,加载过程是怎么样的呢,在@SpringBootApplication注解上面有@EnableAutoConfiguration注解,在这个注解里面又一个很重要的注解,那就是@Import(AutoConfigurationImportSelector.Class), 这个注解的作用是加载META-INF/spring.factories文件里的内容,spring.factories文件里存储的内容是<key,value> 的键值对,key就是注解或者说配置类(XXXAutoConfiguration,比如@Import这个接口)的全路径,value就是需要自动配置的配置类(上面加上@Configuration的类),拿spring官方的spring-boot-autoconfigure下的spring.properties文件来看一下(部分配置):
# Auto Configure org.springframework.boot.autoconfigure.EnableAutoConfiguration=\ # key,下面的都是value 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,\
在AutoConfigurationImportSelector类里面有一个getCandidateConfigurations方法,源码如下:
/**
* Return the auto-configuration class names that should be considered. By default
* this method will load candidates using {@link SpringFactoriesLoader} with
* {@link #getSpringFactoriesLoaderFactoryClass()}.
* @param metadata the source metadata
* @param attributes the {@link #getAttributes(AnnotationMetadata) annotation
* attributes}
* @return a list of candidate configurations
*/
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;
}
我们进入loadFactoryNames方法查看:
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
return (List)loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = (MultiValueMap)cache.get(classLoader);
if (result != null) {
return result;
} else {
try {
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
LinkedMultiValueMap result = new LinkedMultiValueMap();
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文件,加载进来,解析文件的内容,根据键值对对应关系,循环遍历spring.factories,把每个键值对解析为一个.property文件,然后再放到一个beanDefinitionMap里面,这个map的key是beanName,value是beanDefinition对象,有了这个beanDefinitionMap,spring会遍历这个map,对每个bean进行实例化(懒加载的除外),这样就得到了autoConfiguration里面每个bean的实例,可以对其进行属性的配置