作用
ImportBeanDefinitionRegistrar,ImportSelector 这两个接口用于@Import(Config.class)注解种倒入的配置类中,在Config.class 实现这两个接口 在注解被spring加载到是时候 回进行回调 spring调用这两个接口的方法 ,而我们只需要在该接口上实现我们的加载BeanDefinition的逻辑即可
通过一个开源ORM框架讲解 比如我们经常使用mybatis-spring-boot-starter 我们需要扫描到所有的Mapper接口时是不是需要使用注解@MapperScan("包路径"),通过该注解就能扫描到所有的Mapper
@MapperScan 代码结构
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {
/**
* Alias for the {@link #basePackages()} attribute. Allows for more concise annotation declarations e.g.:
* {@code @MapperScan("org.my.pkg")} instead of {@code @MapperScan(basePackages = "org.my.pkg"})}.
*
* @return base package names
*/
String[] value() default {};
/**
* Base packages to scan for MyBatis interfaces. Note that only interfaces with at least one method will be
* registered; concrete classes will be ignored.
*
* @return base package names for scanning mapper interface
*/
String[] basePackages() default {};
/**
* Type-safe alternative to {@link #basePackages()} for specifying the packages to scan for annotated components. The
* package of each class specified will be scanned.
* <p>
* Consider creating a special no-op marker class or interface in each package that serves no purpose other than being
* referenced by this attribute.
*
* @return classes that indicate base package for scanning mapper interface
*/
Class<?>[] basePackageClasses() default {};
/**
* The {@link BeanNameGenerator} class to be used for naming detected components within the Spring container.
*
* @return the class of {@link BeanNameGenerator}
*/
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
/**
* This property specifies the annotation that the scanner will search for.
* <p>
* The scanner will register all interfaces in the base package that also have the specified annotation.
* <p>
* Note this can be combined with markerInterface.
*
* @return the annotation that the scanner will search for
*/
Class<? extends Annotation> annotationClass() default Annotation.class;
/**
* This property specifies the parent that the scanner will search for.
* <p>
* The scanner will register all interfaces in the base package that also have the specified interface class as a
* parent.
* <p>
* Note this can be combined with annotationClass.
*
* @return the parent that the scanner will search for
*/
Class<?> markerInterface() default Class.class;
/**
* Specifies which {@code SqlSessionTemplate} to use in the case that there is more than one in the spring context.
* Usually this is only needed when you have more than one datasource.
*
* @return the bean name of {@code SqlSessionTemplate}
*/
String sqlSessionTemplateRef() default "";
/**
* Specifies which {@code SqlSessionFactory} to use in the case that there is more than one in the spring context.
* Usually this is only needed when you have more than one datasource.
*
* @return the bean name of {@code SqlSessionFactory}
*/
String sqlSessionFactoryRef() default "";
/**
* Specifies a custom MapperFactoryBean to return a mybatis proxy as spring bean.
*
* @return the class of {@code MapperFactoryBean}
*/
Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;
/**
* Whether enable lazy initialization of mapper bean.
*
* <p>
* Default is {@code false}.
* </p>
*
* @return set {@code true} to enable lazy initialization
* @since 2.0.2
*/
String lazyInitialization() default "";
/**
* Specifies the default scope of scanned mappers.
*
* <p>
* Default is {@code ""} (equiv to singleton).
* </p>
*
* @return the default scope
*/
String defaultScope() default AbstractBeanDefinition.SCOPE_DEFAULT;
}
从该注解中可以看到有使用@Import注解倒入一个MapperScannerRegistrar配置类 点开MapperScannerRegistrar可以看到MapperScannerRegistrar 实现了ImportBeanDefinitionRegistrar接口
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware
查看MapperScannerRegistrar的源码中看到 spring 在加载该类时调用ImportBeanDefinitionRegistrar的接口方法 而在实现类中调用了registerBeanDefinitions方法
void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,
BeanDefinitionRegistry registry, String beanName) {
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
builder.addPropertyValue("processPropertyPlaceHolders", true);
Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
if (!Annotation.class.equals(annotationClass)) {
builder.addPropertyValue("annotationClass", annotationClass);
}
Class<?> markerInterface = annoAttrs.getClass("markerInterface");
if (!Class.class.equals(markerInterface)) {
builder.addPropertyValue("markerInterface", markerInterface);
}
Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
if (!BeanNameGenerator.class.equals(generatorClass)) {
builder.addPropertyValue("nameGenerator", BeanUtils.instantiateClass(generatorClass));
}
Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass);
}
String sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef");
if (StringUtils.hasText(sqlSessionTemplateRef)) {
builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef"));
}
String sqlSessionFactoryRef = annoAttrs.getString("sqlSessionFactoryRef");
if (StringUtils.hasText(sqlSessionFactoryRef)) {
builder.addPropertyValue("sqlSessionFactoryBeanName", annoAttrs.getString("sqlSessionFactoryRef"));
}
List<String> basePackages = new ArrayList<>();
basePackages.addAll(
Arrays.stream(annoAttrs.getStringArray("value")).filter(StringUtils::hasText).collect(Collectors.toList()));
basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText)
.collect(Collectors.toList()));
basePackages.addAll(Arrays.stream(annoAttrs.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName)
.collect(Collectors.toList()));
if (basePackages.isEmpty()) {
basePackages.add(getDefaultBasePackage(annoMeta));
}
String lazyInitialization = annoAttrs.getString("lazyInitialization");
if (StringUtils.hasText(lazyInitialization)) {
builder.addPropertyValue("lazyInitialization", lazyInitialization);
}
String defaultScope = annoAttrs.getString("defaultScope");
if (!AbstractBeanDefinition.SCOPE_DEFAULT.equals(defaultScope)) {
builder.addPropertyValue("defaultScope", defaultScope);
}
builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));
registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
}
在registerBeanDefinitions 方法中可以看到该方法实现了一个MapperScannerConfigurer的BeanDefinition并且为这个BeanDefinition添加了很多属性,点开这个MapperScannerConfigurer类可以看到 该类实现了许多spring生命周期的相关接口BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware
核心关注点还是在BeanDefinitionRegistryPostProcessor接口上
public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
if (this.processPropertyPlaceHolders) {
processPropertyPlaceHolders();
}
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
scanner.setAddToConfig(this.addToConfig);
scanner.setAnnotationClass(this.annotationClass);
scanner.setMarkerInterface(this.markerInterface);
scanner.setSqlSessionFactory(this.sqlSessionFactory);
scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
scanner.setResourceLoader(this.applicationContext);
scanner.setBeanNameGenerator(this.nameGenerator);
scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
if (StringUtils.hasText(lazyInitialization)) {
scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
}
if (StringUtils.hasText(defaultScope)) {
scanner.setDefaultScope(defaultScope);
}
scanner.registerFilters();
scanner.scan(
StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
}
postProcessBeanDefinitionRegistry 方法构建了一个ClassPathMapperScanner
Mapper 类扫描器 这个类是mybatis-spring依赖中的内容 执行scan时