通常我们都会将 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 {@Overridepublic void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));}@Overridepublic 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 所在的包的路径
