ImportBeanDefinitionRegistrar
spring官方就是用這種方式,實現@Component
、@Service
等注解的動態注入機制。定義一個ImportBeanDefinitionRegistrar的實現類,然后在有@Configuration
注解的配置類上使用@Import
導入
需要實現類似@Componet
的功能,添加了@Mapper
注解的類會被自動加入到spring容器中。
創建一個@Mapper
注解,再新建一個CountryMapper
類,使用該Mapper注解
@Inherited @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) public @interface Mapper { } @Mapper public class CountryMapper { }
創建MapperAutoConfiguredMyBatisRegistrar
實現ImportBeanDefinitionRegistrar
,同時可以繼承一些Aware接口,獲得spring的一些數據
BeanFactoryAware
ResourceLoaderAware
EnvironmentAware
使用@Import將MapperAutoConfiguredMyBatisRegistrar導入容器
public class MapperAutoConfiguredMyBatisRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware, BeanFactoryAware { private ResourceLoader resourceLoader; private BeanFactory beanFactory; @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { } @Override public void setResourceLoader(ResourceLoader resourceLoader) { this.resourceLoader = resourceLoader; } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory = beanFactory; } }
實現registerBeanDefinitions
方法。但是有一個問題,我們並不知道需要register哪些bean。這里我們還需要借助一個類ClassPathBeanDefinitionScanner
,也就是掃描器,通過掃描器獲取我們需要注冊的bean。先簡單看一下spring源碼中的的定義
需要繼承
ClassPathBeanDefinitionScanner
,掃描使用
@Mapper
的注解的類,
ClassPathBeanDefinitionScanner
又繼承
ClassPathScanningCandidateComponentProvider
類,
ClassPathScanningCandidateComponentProvider
中有兩個TypeFilter集合,includeFilters、excludeFilters。滿足任意includeFilters會被加載,同樣的滿足任意excludeFilters不會被加載
@Slf4j public class MyClassPathBeanDefinitionScanner extends ClassPathBeanDefinitionScanner{ public MyClassPathBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) { super(registry, useDefaultFilters); } protected void registerFilters() { addIncludeFilter(new AnnotationTypeFilter(Mapper.class)); } @Override protected Set<BeanDefinitionHolder> doScan(String... basePackages) { return super.doScan(basePackages); } }
registerFilters()方法,然后在我們的ImportBeanDefinitionRegistrar
實現類中調用
@Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { MyClassPathBeanDefinitionScanner scanner = new MyClassPathBeanDefinitionScanner(registry, false); scanner.setResourceLoader(resourceLoader); scanner.registerFilters(); scanner.doScan("com.smart.school.domain"); }
測試
@RunWith(SpringRunner.class) @SpringBootTest public class SrcDemoApplicationTests { @Resource private CountryMapper countryMapper; @Test public void contextLoads() { System.out.println(countryMapper.getClass()); } }