最近闲来无事想写一篇关于Spring自动装配原理实现的文章,所以自己就简单复习了一下相关源码,然后突然发现一个之前没有注意到的注解:@AutoConfigurationPackage,感觉很有意思,特此记录一下。
它所在的位置是在@SpringBootApplication ->@EnableAutoConfiguration上,具体如下:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import(AutoConfigurationPackages.Registrar.class)
public @interface AutoConfigurationPackage {
}
其实说白点就是被这个注解标记的类所在的包路径在Spring中可以通过被AutoConfigurationPackages.get方法拿到,我们可以做一些第三方组件扫描并交给Spring管理。
在注解中使用了@Import导入了AutoConfigurationPackages.Registrar类,该类实现了ImportBeanDefinitionRegistrar接口,重写了registerBeanDefinitions方法,该方法的作用是注册BasePackages类为beanDefinition,而BasePackages是将@AutoConfigurationPackage注解所在类的所在包路径保存下来,可以通过AutoConfigurationPackages.get()方法获取所有的包名。
这里一个小知识点:ImportBeanDefinitionRegistrar接口是也是spring的扩展点之一,它可以支持我们自己写的代码封装成BeanDefinition对象;实现此接口的类会回调postProcessBeanDefinitionRegistry方法,注册到spring容器中。把bean注入到spring容器不止有 @Service @Component等注解方式;还可以实现此接口,只要@Import导入即可。
所以被@AutoConfigurationPackage修饰的类所在的包我们都可以通过AutoConfigurationPackages.get()获取到。
public static List<String> get(BeanFactory beanFactory) {
try {
//获取BasePackagesbean,然后调用get方法
return beanFactory.getBean(BEAN, BasePackages.class).get();
}
catch (NoSuchBeanDefinitionException ex) {
throw new IllegalStateException(
"Unable to retrieve @EnableAutoConfiguration base packages");
}
}
//BasePackages
static final class BasePackages {
private final List<String> packages;
private boolean loggedBasePackageInfo;
//在注册beanDefinition的时候会将参数传进来
BasePackages(String... names) {
List<String> packages = new ArrayList<>();
for (String name : names) {
if (StringUtils.hasText(name)) {
packages.add(name);
}
}
this.packages = packages;
}
//获取所有的被@AutoConfigurationPackage注解所在类的所在包路径
public List<String> get() {
if (!this.loggedBasePackageInfo) {
if (this.packages.isEmpty()) {
if (logger.isWarnEnabled()) {
logger.warn("@EnableAutoConfiguration was declared on a class "
+ "in the default package. Automatic @Repository and "
+ "@Entity scanning is not enabled.");
}
}
else {
if (logger.isDebugEnabled()) {
String packageNames = StringUtils
.collectionToCommaDelimitedString(this.packages);
}
}
this.loggedBasePackageInfo = true;
}
return this.packages;
}
}
而BasePackages作为beanDefinition被注册是在registerBeanDefinitions方法被调用的时候:
SpringApplication.run()
=> refreshContext()
=> EmbeddedWebApplicationContext.refresh()
=> AbstractApplicationContext.invokeBeanFactoryPostProcessors()
=> PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors()
=> ConfigurationClassPostProcessor.processConfigBeanDefinitions()
=> ConfigurationClassBeanDefinitionReader.loadBeanDefinitions()
=> loadBeanDefinitionsFromRegistrars()
=> AutoConfigurationPackages$Registrar.registerBeanDefinitions()
=> AutoConfigurationPackages.register()
说到这里不知道大家有没有发现,这里的@AutoConfigurationPackage和另外一个注解的作用很相似,没错就是@ComponentScan。
@ComponentScan这个注解作用想必大家都很清楚,他是扫描指定包下的所有的Spring提供的例如@Controller、@Service、@Component、@Repository 以及由此衍生出来的一些其他的注解所修饰的类,将其交给IOC容器管理。
这里有一个问题,@ComponentScan只能扫描Spring相关的注解,对于第三方提供的注解,它并不能扫描到。例如我们常用的Mybatis框架里的@Mapper注解,那么Mybatis又如何将@Mapper修饰的类交给Spring的IOC容器管理呢,这里便用到了@AutoConfigurationPackage注解(这里只是演示一种方式,实际我们可以使用@MapperScan进行扫描)。jnty
为了验证这一点,我找到了Mybatis的自动装配类,找到了相关方法JNTY
//该类通过@Import注解被导入
public static class AutoConfiguredMapperScannerRegistrar
implements BeanFactoryAware, ImportBeanDefinitionRegistrar, ResourceLoaderAware {
private BeanFactory beanFactory;
private ResourceLoader resourceLoader;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
//创建扫描器
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
try {
if (this.resourceLoader != null) {
scanner.setResourceLoader(this.resourceLoader);
}
//这里我们可以看到获取所有被@AutoConfigurationPackage修饰的类所在的包名
List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
//设置扫描注解
scanner.setAnnotationClass(Mapper.class);
scanner.registerFilters();
//开始扫描指定路径
scanner.doScan(StringUtils.toStringArray(packages));
} catch (IllegalStateException ex) {
logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.", ex);
}
}
}
//使用自动装配,导入AutoConfiguredMapperScannerRegistrar类
@org.springframework.context.annotation.Configuration
@Import({ AutoConfiguredMapperScannerRegistrar.class })
@ConditionalOnMissingBean(MapperFactoryBean.class)
public static class MapperScannerRegistrarNotFoundConfiguration {
@PostConstruct
public void afterPropertiesSet() {
logger.debug("No {} found.", MapperFactoryBean.class.getName());
}
}
默认情况下,SpringBoot 项目的启动注解中,实际上已经包含了 @AutoConfigurationPackage,注解具体位置在 @SpringBootApplication
总结一下:
电话:400-123-4567
传 真:+86-123-4567
手 机:13800000000
邮 箱:admin@qq.com
地 址:JNTY体育广东省广州市天河区88号