通常我们都会将 Application 放在项目的根路径下,此时 Spring 就会自动扫描该路径下的包与子包下所有注解,那么这个又是如何实现的呢?
@SpringBootApplication
@SpringBootApplication 注解下有一个注解是 @EnableAutoConfiguration
@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 {
// ...
}
@EnableAutoConfiguration 注解下,有一个 @AutoConfigurationPackage 注解,从这个注解的名字可以看出来,是用来配置自动扫描包的
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
这里使用了一个 @Import 导入了一个 AutoConfigurationPackages.Registrar.class 类,那么这个类又是做什么的呢?
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImports(metadata));
}
}
我们可以看到 ,这个 Registrar 实现了 ImportBeanDefinitionRegistrar 接口,而 ImportBeanDefinitionRegistrar 类主要作用就是来动态注册 bean
我们可以看到,他首先通过 new PackageImports(metadata) 拿到当前类的 metadata
PackageImports(AnnotationMetadata metadata) {
AnnotationAttributes attributes = AnnotationAttributes
.fromMap(metadata.getAnnotationAttributes(AutoConfigurationPackage.class.getName(), false));
List<String> packageNames = new ArrayList<>();
for (String basePackage : attributes.getStringArray("basePackages")) {
packageNames.add(basePackage);
}
for (Class<?> basePackageClass : attributes.getClassArray("basePackageClasses")) {
packageNames.add(basePackageClass.getPackage().getName());
}
if (packageNames.isEmpty()) {
packageNames.add(ClassUtils.getPackageName(metadata.getClassName()));
}
this.packageNames = Collections.unmodifiableList(packageNames);
}
当我们没有提供任何 basePackages 或 basePackageClasses 时,就会使用当前类的包名,作为扫描路径
if (packageNames.isEmpty()) {
packageNames.add(ClassUtils.getPackageName(metadata.getClassName()));
}
public static String getPackageName(String fqClassName) {
Assert.notNull(fqClassName, "Class name must not be null");
int lastDotIndex = fqClassName.lastIndexOf(".");
return (lastDotIndex != -1 ? fqClassName.substring(0, lastDotIndex) : "");
}
这样就完成了自动扫描包的目的:其实就是截取当前 Application 所在的包的路径