基于springboot 2.1.3版本
首先看看springboot启动类代码:
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
整个流程的入口就是@SpringBootApplication注解:
@Target(ElementType.TYPE)//用在类上
@Retention(RetentionPolicy.RUNTIME)//保留到运行时
@Documented //生成文档
@Inherited //如果有子类继承该类,则会继承类的所有注解
@SpringBootConfiguration //申明是配置,里面就是@Configuration注解
@EnableAutoConfiguration //开启自动注入
@ComponentScan(excludeFilters = { //包扫描
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
下面我们主要看看@ComponentScan和@EnableAutoConfiguration注解的源码
先看@ComponentScan,这个注解的作用就是指定当前应用所要扫描的包
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)//表示可以重复用这个注解,在一个类上可以加两个@ComponentScan注解,但是属性值肯定需要不同
public @interface ComponentScan {
下面就是重点
,@EnableAutoConfiguration注解
看源码:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
里面的重点注解就两个
@AutoConfigurationPackage:用于导入并装配用户自定义类,即自动扫描包中的类
@Import(AutoConfigurationImportSelector.class):用于导入并装配框架本身的类
下面我们看@AutoConfigurationPackage:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
//扫描包中加了注解(@Controller,@Service等)的类,加入到IOC容器中
register(registry, new PackageImport(metadata).getPackageName());
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImport(metadata));
}
}
下面我们看@Import注解,这个注解用于导入指定的类,导入的类一般为配置类,常见的导入方式有三种:
1. 直接引入配置类
@Import 中指定的类一般为 Configuration 结尾,且该类上会注解@Configuration,表示当前类为 配置类。例如用于开启定时任务的@EnableScheduling 注解的@Import。
2. 根据条件选择配置类
@Import 中指定的类一般以 ConfigurationSelector 结尾,且该类实现了 ImportSelector接口,表示当前类会根据条件选择不同的配置类导入。例如,用于开启缓存功能的@EnableCaching 的@Import。
3. 动态注册 Bean
@Import 中指定的类一般以 Registrar 结尾,且该类实现了 ImportBeanDefinitionRegistrar
接口,用于表示在代码运行时若使用了到该配置类,则系统会自动将其导入。例如,用于开启 AspectJ 自动代理功能的@EnableAspectJAutoProxy 注解的@Import。
接着看最上面:
@SpringBootApplication 的 @EnableAutoConfiguration 的 @Import(AutoConfigurationImportSelector.class),很明显就是采用的第二种,根据条件选择配置类
下面看AutoConfigurationImportSelector类的selectImports方法源码:
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
//下面看这个方法getAutoConfigurationEntry
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(
autoConfigurationMetadata, annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
protected AutoConfigurationEntry getAutoConfigurationEntry(
AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//下面看这个方法getCandidateConfigurations
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);
}
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata,
AnnotationAttributes attributes) {
//下面看loadFactoryNames
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;
}
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
String factoryClassName = factoryClass.getName();
//下面看loadSpringFactories
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 ?
//重点就在这
// public static final String FACTORIES_RESOURCE_LOCATION = "META-INF/spring.factories";
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);
}
}
看到最后就知道了,最终加载的是"META-INF/spring.factories"里面制定的类