关于注解@AutoConfigurationPackage
作用
最近在看SpringBoot源码时发现了这个注解 看了一下 网上的教程和视频几乎都是说该注解作用是扫描我们启动类包和子包下组件并加入容器 然而断点跟踪一下发现 在这个注解导入的组件生效之前 我们启动类包下的内容已经被放入Bean定义信息工厂里了 那这个注解的作用到底是什么那 我们来跟踪一下代码
- 首先说一下 我们包和子包标注注解的组件 之所以被加载入容器 是因为
@ComponentScan(...)
注解 它在不填写value的情况下 就会扫描所在包和子包 原理不过多赘述 有兴趣的同学可以看看源码 - 现在来看看
@AutoConfigurationPackage
到底干了啥
// 引入AutoConfigurationPackages.Registrar
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage{}...
// 向容器中注入组件
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImports(metadata).getPackageNames().toArray(new String[0]));
}
}...
// 调用方法 注入组件
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
// private static final String BEAN = AutoConfigurationPackages.class.getName();
// 这最终向容器注入组件 名称是全类名 值是PackageImports 里面包含一个集合 集合中存储packageNames 也就是我们注解所在包
if (registry.containsBeanDefinition(BEAN)) {
BasePackagesBeanDefinition beanDefinition = (BasePackagesBeanDefinition) registry.getBeanDefinition(BEAN);
beanDefinition.addBasePackages(packageNames);
}
else {
registry.registerBeanDefinition(BEAN, new BasePackagesBeanDefinition(packageNames));
}
}
- 说白了 就是把我们注解所在包路径加入了容器一个专门记录packageNames的集合里 其他组件也可以加入自己想放入的包路径到这个集合
- 看到这可能大家更疑惑了?SO?就这?其实这注解真就做了这么多事 我们看看注解的说明
Class for storing auto-configuration packages for reference later (e.g. by JPA entity scanner).
Since:
1.0.0
Author:
Phillip Webb, Dave Syer, Oliver Gierke
-
看最后一句 e.g. by JPA entity scanner ?JPA实体扫描?那我们看看JPA是怎么玩的
-
还是一样 挑关键的看看
// 先要引入 HibernateJpaAutoConfiguration
@AutoConfigureAfter({ HibernateJpaAutoConfiguration.class...})
// JAP自动配置的引入
public class JpaRepositoriesAutoConfiguration {}...
// 引入HibernateJpaConfiguration
@Import(HibernateJpaConfiguration.class)
public class HibernateJpaAutoConfiguration {}
// 继承JpaBaseConfiguration
class HibernateJpaConfiguration extends JpaBaseConfiguration {}
// 关键方法 entityManagerFactory中调用getPackagesToScan()
public abstract class JpaBaseConfiguration implements BeanFactoryAware {
@Bean
@Primary
@ConditionalOnMissingBean({ LocalContainerEntityManagerFactoryBean.class, EntityManagerFactory.class })
public LocalContainerEntityManagerFactoryBean entityManagerFactory(EntityManagerFactoryBuilder factoryBuilder) {
Map<String, Object> vendorProperties = getVendorProperties();
customizeVendorProperties(vendorProperties);
// getPackagesToScan()!!!
return factoryBuilder.dataSource(this.dataSource).packages(getPackagesToScan()).properties(vendorProperties)
.mappingResources(getMappingResources()).jta(isJta()).build();
}
// 这里先去拿EntityScanPackages 如果拿不到 就去拿我们的AutoConfigurationPackages
protected String[] getPackagesToScan() {
List<String> packages = EntityScanPackages.get(this.beanFactory).getPackageNames();
if (packages.isEmpty() && AutoConfigurationPackages.has(this.beanFactory)) {
packages = AutoConfigurationPackages.get(this.beanFactory);
}
return StringUtils.toStringArray(packages);
}
}
- 这里就比较明确了 在注入JPA核心类时 如果无特殊配置 就会去拿我们的packages做扫描
- 所以总结一下 该注解的作用是向容器内注入一个组件 组件的作用是保存一些包路径
- JPA里 利用该路径扫描自己的实体类
- 如果以后我们要开发一些组件 可以利用该机制 保存一些关心的包路径 并在合适的时机让组件读取 以便实现功能
- END;