都知道注解SpringBootApplication有两个import
1 @Import(AutoConfigurationImportSelector.class)
2 @Import(AutoConfigurationPackages.Registrar.class)
第一个的作用和原理我看网上说的挺多的,但是第二个貌似不多。今天我就分析分析@Import(AutoConfigurationPackages.Registrar.class)的作用
先看看方法内的注解
/** * Class for storing auto-configuration packages for reference later (e.g. by JPA entity * scanner).
一个Class,保存了自动装配的包路径,为了后续引用的使用(例如JPA实体类的扫描)
简单点说就是保存自动装配的路径的
现在跟随代码看看它是怎么生效的
ConfigurationClassPostProcessor.processConfigBeanDefinitions 代码片段
do { parser.parse(candidates); parser.validate(); Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses()); configClasses.removeAll(alreadyParsed); // Read the model and create bean definitions based on its content if (this.reader == null) { this.reader = new ConfigurationClassBeanDefinitionReader( registry, this.sourceExtractor, this.resourceLoader, this.environment, this.importBeanNameGenerator, parser.getImportRegistry()); } this.reader.loadBeanDefinitions(configClasses);
经过了parser解析之后,我们最终能得到configClasses这些都是带有@Configuration注解的
本篇我们不分析 AutoConfigurationImportSelector 但是他的作用还是要提一下,在 parser.parse 就会完成对AutoConfigurationImportSelector的处理,也就是把各个包下的spring.factory里的自动装配相关的class都选出来。所以上面代码中configClasses多达76个。而我们的启动类只是其中之一。
ConfigurationClass里有一个字段专门用来保存Registrar的。比如我们的启动类对应的 ConfigurationClass 就有两个 Registrar。除了自带的AutoConfigurationPackages.Registrar还有
MapperScannerRegistrar那是因为在启动类里配了Mybatis的scan路径
接下来就是分析 this.reader.loadBeanDefinitions(configClasses);
最终会执行 ConfigurationClassBeanDefinitionReader.loadBeanDefinitionsFromRegistrars
private void loadBeanDefinitionsFromRegistrars(Map<ImportBeanDefinitionRegistrar, AnnotationMetadata> registrars) { registrars.forEach((registrar, metadata) -> registrar.registerBeanDefinitions(metadata, this.registry)); }
AutoConfigurationPackages$Registrar
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports { @Override public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) { register(registry, new PackageImport(metadata).getPackageName()); }
最终调用的是 AutoConfigurationPackages.register
public static void register(BeanDefinitionRegistry registry, String... packageNames) { 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);//就是注册个beanDefinition,类是 BasePackages } }
这里的 packageNames 如果没有配的就是启动类所在的路径
二 被调用处
AutoConfigurationPackages.get 该方法的作用就是返回上面提到的路径 我的这个例子就是 com.example.demo
public static List<String> get(BeanFactory beanFactory) { try { return beanFactory.getBean(BEAN, BasePackages.class).get(); } catch (NoSuchBeanDefinitionException ex) { throw new IllegalStateException( "Unable to retrieve @EnableAutoConfiguration base packages"); } }
那么这个get又有啥作用呢
我们以 AutoConfiguredMapperScannerRegistrar 为例
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { if (!AutoConfigurationPackages.has(this.beanFactory)) { logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled."); return; } logger.debug("Searching for mappers annotated with @Mapper"); List<String> packages = AutoConfigurationPackages.get(this.beanFactory); if (logger.isDebugEnabled()) { packages.forEach(pkg -> logger.debug("Using auto-configuration base package '{}'", pkg)); }
拿到这个扫包根路径之后就可以扫包了