主要内容:
- @AutoConfigurationPackage是干什么的
- 通过什么来获取主启动类包全路径名
SpringBoot的自动注册用@EnableAutoConfiguration来完成
除开元注解,我们来看看这个注解:
@AutoConfigurationPackage
@Import(AutoConfigurationImportSelector.class)
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
/**
* Exclude specific auto-configuration classes such that they will never be applied.
* @return the classes to exclude
*/
Class<?>[] exclude() default {};
/**
* Exclude specific auto-configuration class names such that they will never be
* applied.
* @return the class names to exclude
* @since 1.3.0
*/
String[] excludeName() default {};
}
@AutoConfigurationPackage
Indicates that the package containing the annotated class should be registered with
表示包含该注解的类所在的包应该在 AutoConfigurationPackages 中注册。
/**
* {@link ImportBeanDefinitionRegistrar} to store the base package from the importing
* configuration.
* 用于保存导入的配置类所在的根包。很明显,它就是实现把主配置所在根包保存起来以便后期扫描用的
*/
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
Registrar
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
register(registry, new PackageImport(metadata).getPackageName());
}
}
这个Registrar实现了ImportBeanDefinitionRegistrar,我们看看这个借口中对这个方法的定义:
//导入类的元注解信息
@param importingClassMetadata annotation metadata of the importing class
@param registry current bean definition registry
拿到这个导入类的元注解信息importingClassMetadata后:
register(registry, new PackageImport(metadata).getPackageName());
我们可以看到,是new了一个PackageImport,在其中metadata.getClassName()获取了一个类名,我们点进这个getClassName看看:
public interface ClassMetadata {
/**
* Return the name of the underlying class.
*/
String getClassName();
}
我们发现是个接口,我们在这里打个断点测试一下:
于是我们去这个实现类看看,并且断点测试也自动来到这个实现类的getClassName方法了:
@Override
public String getClassName() {
return this.introspectedClass.getName();
}
他最终是返回了个这个名字,就拿到了包名:
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
//判断当前IOC容器中是否包含 AutoConfigurationPackages
//private static final String BEAN = AutoConfigurationPackages.class.getName();
if (registry.containsBeanDefinition(BEAN)) {
BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
ConstructorArgumentValues constructorArguments = beanDefinition
.getConstructorArgumentValues();
constructorArguments.addIndexedArgumentValue(0,
addBasePackages(constructorArguments, packageNames));
}
else {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
beanDefinition.setBeanClass(BasePackages.class);
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0,
packageNames);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
registry.registerBeanDefinition(BEAN, beanDefinition);
}
}
在单步调试的结果下:
这个packageNames就是我们的基包
拿到这个包后做了什么呢
也解释了为什么 SpringBoot 的启动器一定要在所有类的最外层。
总结:
- @AutoConfigurationPackage中封装的注解能帮我们获取到启动类所在包名,并且存储起来供以后扫描使用
- 实际上是通过AnnotationMetadata来获取所在包名如下