@SpringBootApplication
发现是一个复合注解 @SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan 由三个注解组合而来
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
...
}
-
@SpringBootConfiguration 等效于 @Configuration 注解(@Configuration 如果不清楚去看看前面 spring 的文章)
@Target({ElementType.TYPE}) @Retention(RetentionPolicy.RUNTIME) @Documented @Configuration public @interface SpringBootConfiguration { @AliasFor( annotation = Configuration.class ) boolean proxyBeanMethods() default true; }
-
@ComponentScan 是用来配置哪些包能被扫描,注意这里只是规定哪些被扫描,并不会把这些包下面的类没有注册到容器(@EnableAutoConfiguration 会读取 @ComponentScan 配置的信息,然后根据配置进行注册)
简言之就是 @ComponentScan 只管配置,@EnableAutoConfiguration 会根据配置来注册
-
@EnableAutoConfiguration 这个是核心,完成自动配置的注解,专门来说说这个
@EnableAutoConfiguration
可以看出是由两个注解的复合注解 @AutoConfigurationPackage、@Import(AutoConfigurationImportSelector.class)
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
}
@AutoConfigurationPackage
-
这个注解的元注解可以看出,使用 @Import 导入了一个 AutoConfigurationPackages.Registrar 类型的 bean
@Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @Import(AutoConfigurationPackages.Registrar.class) public @interface AutoConfigurationPackage { }
-
@Import 前面说的很清楚了,有三种用法
@Import(A.class) // 导入普通类 @Import(MyImportSelector.class) // 导入实现了 ImportSelector 的类(可以批量注册 bean,方法返回的数组就是要导入的 bean) @Import(MyImportBeanDefinitionRegister.class) // 导入实现了 ImportBeanDefinitionRegistrar 的类(也可以批量,通过注册 BeanDefinition 的方式)
@Import(AutoConfigurationPackages.Registrar.class) 就是使用的第三种方式,看看源码(这是个内部类)
// org.springframework.boot.autoconfigure.AutoConfigurationPackages.Registrar static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports { @Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { // metadata 表示注解元信息,获取使用了这个注解的那个类上的注解信息(我们是从 SpringBootApplication 跟进来的,也就是启动类) register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0])); } @Override public Set<Object> determineImports(AnnotationMetadata metadata) { return Collections.singleton(new PackageImports(metadata)); } }
register 方法做了什么(spring 的 方方面面详细讲了 @Import 怎么注册 BeanDefinition 的,这里再大概看下)
// org.springframework.boot.autoconfigure.AutoConfigurationPackages#register public static void register(BeanDefinitionRegistry registry, String... packageNames) { // 如果容器中的 beanFactory.beanDefinitionMap 已经有这个 bean 了(初识起动不会有) if (registry.containsBeanDefinition(BEAN)) { BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN); ConstructorArgumentValues constructorArguments = beanDefinition.getConstructorArgumentValues(); constructorArguments.addIndexedArgumentValue(0, addBasePackages(constructorArguments, packageNames)); } else { // BeanDefinition 对象 GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClass(BasePackages.class); beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, packageNames); beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); // 注册 BeanDefinition registry.registerBeanDefinition(BEAN, beanDefinition); } }
new PackageImports(metadata) 做了什么,是在读取 @ComponentScan 配置的信息
// org.springframework.boot.autoconfigure.AutoConfigurationPackages.PackageImports#PackageImports PackageImports(AnnotationMetadata metadata) { AnnotationAttributes attributes = AnnotationAttributes .fromMap(metadata.getAnnotationAttributes(AutoConfigurationPackage.class.getName(), false)); List<String> packageNames = new ArrayList<>(); // 先找 basePackages 属性 for (String basePackage : attributes.getStringArray("basePackages")) { packageNames.add(basePackage); } // 再找 basePackageClasses 属性 for (Class<?> basePackageClass : attributes.getClassArray("basePackageClasses")) { packageNames.add(basePackageClass.getPackage().getName()); } // 如果都没配置,那就使用启动类所在的包 if (packageNames.isEmpty()) { // metadata.getClassName() 启动类的包 packageNames.add(ClassUtils.getPackageName(metadata.getClassName())); } this.packageNames = Collections.unmodifiableList(packageNames); }
这里有一点要注意,如果什么都不配置,只会导入启动类这一个包,并不是往上说的子包及以下的包
比如我定义一个包里面放一个 Controller(UserController)为什么这个也会被注册到容器,刚不是说子包不会被扫描到吗?
因为这只是 spring 的容器,springMVC 初始化的时候,自己还有个容器,会把这些再进行注册!
@Import(AutoConfigurationImportSelector.class)
刚才的 @Import 是导入了一个实现了 ImportBeanDefinitionRegistrar 的类,这个 @Import 导入了一个实现了 ImportSelector 的类AutoConfigurationImportSelector 类继承和实现关系:AutoConfigurationImportSelector > DeferredImportSelector > ImportSelector
// org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#selectImports
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
// 主要看这个方法返回的什么(返回的数组都要被导入容器)
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getAutoConfigurationEntry
protected AutoConfigurationEntry getAutoConfigurationEntry(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 = getConfigurationClassFilter().filter(configurations);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
org.springframework.boot.autoconfigure.AutoConfigurationImportSelector#getCandidateConfigurations
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;
}
org.springframework.core.io.support.SpringFactoriesLoader#loadFactoryNames
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) {
String factoryTypeName = factoryType.getName();
// 调用了本类的 loadSpringFactories 方法,就是下面的方法
return (List)loadSpringFactories(classLoader).getOrDefault(factoryTypeName, Collections.emptyList());
}
// org.springframework.core.io.support.SpringFactoriesLoader#loadSpringFactories
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 {
// 找所有 jar 的 META-INF/spring.factories 文件,里面配置的
Enumeration<URL> urls = classLoader != null ? classLoader.getResources("META-INF/spring.factories") : ClassLoader.getSystemResources("META-INF/spring.factories");
....
} catch (IOException var13) {
IOException ex = var13;
throw new IllegalArgumentException("Unable to load factories from location [META-INF/spring.factories]", ex);
}
}
}
找到的所有的类都要注册到容器吗,答案肯定不是的,不然太多了,这里就需要按需加载了
用 spring-boot-autoconfigure 这个 jar 为例,这个很典型,springboot 自己的,里面类容也是最多的,有的 jar 里面跟没没有这个文件
随便拎一个 org.springframework.boot.autoconfigure.data.cassandra.CassandraDataAutoConfiguration 类,进去看看
标签:springboot,springframework,自动,原理,Import,metadata,packageNames,class,configuratio From: https://www.cnblogs.com/cryus/p/18286785