Mybatis原始碼---重寫一個最簡單的Mybatis架構實現(三)
前兩篇文章裡,我們實現了一個簡單的Mybatis。只要願意,如果完善了後續的資料庫操作,我們完全可以用它來替換本來的Mybatis。在本篇文章裡,我們要做的是完成我們自定義Mybatis與Spring或SpringBoot整合時的自動配置。首先,我們在來熟悉一下在XML中配置MapperScannerConfigurer時的使用:
<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer"> <property name="sqlSessionFactory" ref="sqlSessionFactory"/> <property name="basePackage" value="com.mapper"/> </bean>
在前面的文章裡,我們詳細分析過為Mapper介面生成代理的配置方法,這裡不做過多描述。MapperScannerConfigurer類會為我們批量完成Mapper介面代理類的生成。看XML中的配置可以發現,需要為它注入一些屬性。我們再來看看MapperScannerConfigurer類的原始碼中屬性定義的部分:
public class MapperScannerConfigurer implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware { private String basePackage; private boolean addToConfig = true; private SqlSessionFactory sqlSessionFactory; private SqlSessionTemplate sqlSessionTemplate; private String sqlSessionFactoryBeanName; private String sqlSessionTemplateBeanName; private Class<? extends Annotation> annotationClass; private Class<?> markerInterface; private ApplicationContext applicationContext; private String beanName; private boolean processPropertyPlaceHolders; private BeanNameGenerator nameGenerator; }
從名稱我們也可以大致猜出來它們的含義。需要重點說一下幾個特別的屬性:
1. annotationClass是用來指定掃描哪個註解的。這樣,MapperScannerConfigurer類只會為聲明瞭該註解的類生成代理類。這樣,就可以防止MapperScannerConfigurer把我們不需要生成代理類的類也生成代理類了。
2.markerInterface是用來宣告抽象Mapper介面的。比如,我們聲明瞭一個所有Mapper介面都要繼承的介面AbstractMapper,它本身並不需要被掃描,那麼,就可以看設定為markerInterface來防止被掃描。
之所以要重點說明
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
public @interface MapperScan {
String[] value() default {};
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
Class<? extends Annotation> annotationClass() default Annotation.class;
Class<?> markerInterface() default Class.class;
String sqlSessionTemplateRef() default "";
String sqlSessionFactoryRef() default "";
Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;
}
在@MapperScan註解中,我們發現它和MapperScannerConfigurer類的屬性看起來很像。其實,這就是所謂預設配置了,Spring用註解中設定值的方式替代了XML中的屬性注入。那麼問題來了,@MapperScan的值是什麼時候起作用的呢?我們發現@MapperScan註解的宣告中有一行程式碼:@Import(MapperScannerRegistrar.class)。而@Import註解是用來引入@Configuration註解宣告的內容的。這一切怎麼解釋呢?
我們看看@SpringBootApplication註解的原始碼:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(excludeFilters = {
@Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class),
@Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) })
public @interface SpringBootApplication {
}
我們看到@SpringBootApplication註解宣告時用到了@SpringBootConfiguration註解,那好,我們看看@SpringBootConfiguration註解的原始碼:
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
}
好了,我們明白了一件事,被@SpringBootApplication宣告的類其實也被@Configuration註解聲明瞭,被@SpringBootApplication註解宣告的類其實就是主啟動類了。前面的文章中提到過在主啟動類其實執行了AbstractApplicationContext的run()方法(Spring容器啟動的主要流程),程式碼如下:
public void refresh() throws BeansException, IllegalStateException {
//重新整理前的預處理
prepareRefresh();
// 獲取BeanFactory
ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();
// BeanFactory的預準備工作(BeanFactory進行一些設定)
prepareBeanFactory(beanFactory);
// BeanFactory準備工作完成後進行的後置處理工作,子類通過重寫這個方法來在BeanFactory建立並預準備完成以後 做進一步的設定
postProcessBeanFactory(beanFactory);
// 執行BeanFactoryPostProcessor的後置處理方法
invokeBeanFactoryPostProcessors(beanFactory);
// 註冊BeanPostProcessors,此時並不執行這些bean的後置處理器
registerBeanPostProcessors(beanFactory);
// 初始化MessageSource
initMessageSource();
// 初始化事件派發器
initApplicationEventMulticaster();
// 是一個空方法,留給容器的子類實現,子類重寫這個方法,在容器重新整理的時候可以自定義一些邏輯
onRefresh();
// 註冊事件驅動
registerListeners();
//初始化所有剩下的單例項bean
finishBeanFactoryInitialization(beanFactory);
// 完成BeanFactory的初始化建立工作,IOC容器就建立完成
finishRefresh();
}
在執行其中的obtainFreshBeanFactory()方法時,關鍵程式碼如下:
DefaultListableBeanFactory beanFactory = createBeanFactory();
beanFactory.setSerializationId(getId());
customizeBeanFactory(beanFactory);
loadBeanDefinitions(beanFactory);
synchronized (this.beanFactoryMonitor) {
this.beanFactory = beanFactory;
}
好了,重點來了。我們看看MapperScannerRegistrar 類的原始碼:
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
private ResourceLoader resourceLoader;
/**
* {@inheritDoc}
*/
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
// this check is needed in Spring 3.1
if (resourceLoader != null) {
scanner.setResourceLoader(resourceLoader);
}
Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
if (!Annotation.class.equals(annotationClass)) {
scanner.setAnnotationClass(annotationClass);
}
Class<?> markerInterface = annoAttrs.getClass("markerInterface");
if (!Class.class.equals(markerInterface)) {
scanner.setMarkerInterface(markerInterface);
}
Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
if (!BeanNameGenerator.class.equals(generatorClass)) {
scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
}
Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
scanner.setMapperFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
}
scanner.setSqlSessionTemplateBeanName(annoAttrs.getString("sqlSessionTemplateRef"));
scanner.setSqlSessionFactoryBeanName(annoAttrs.getString("sqlSessionFactoryRef"));
List<String> basePackages = new ArrayList<String>();
for (String pkg : annoAttrs.getStringArray("value")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
for (String pkg : annoAttrs.getStringArray("basePackages")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
scanner.registerFilters();
scanner.doScan(StringUtils.toStringArray(basePackages));
}
/**
* {@inheritDoc}
*/
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
}
可以發現,大篇幅的程式碼都只描述了一個方法----registerBeanDefinitions()。本來還可以繼續往下挖,但是呢,已經不是很有必要了吧?很明顯可以猜出來,在SpringBoot主啟動類啟動時,會在載入BeanDefinition的階段執行MapperScannerRegistrar類的registerBeanDefinitions()方法。這個方法比較簡單,只要抓住ClassPathMapperScanner scan這條線索很容易就看懂了,和MapperScannerConfigurer類做的事情是一毛一樣的。所以,我們只要定義一個類似@MapperScan的註解,然後把MapperScannerRegistrar類引入就可以了。
最後是福利時間,下面兩段程式碼就是改造完成的MapperScannerRegistrar類和@MapperScan註解原始碼了,親測可用。
@Component
public class FisherScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
private ResourceLoader resourceLoader;
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(FisherScan.class.getName()));
ClassPathFisherScanner scanner = new ClassPathFisherScanner(registry);
// this check is needed in Spring 3.1
if (resourceLoader != null) {
scanner.setResourceLoader(resourceLoader);
}
Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
if (!Annotation.class.equals(annotationClass)) {
scanner.setAnnotationClass(annotationClass);
}
Class<?> markerInterface = annoAttrs.getClass("markerInterface");
if (!Class.class.equals(markerInterface)) {
scanner.setMarkerInterface(markerInterface);
}
Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
if (!BeanNameGenerator.class.equals(generatorClass)) {
scanner.setBeanNameGenerator(BeanUtils.instantiateClass(generatorClass));
}
Class<? extends FisherFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
if (!FisherFactoryBean.class.equals(mapperFactoryBeanClass)) {
scanner.setFisherFactoryBean(BeanUtils.instantiateClass(mapperFactoryBeanClass));
}
List<String> basePackages = new ArrayList<String>();
for (String pkg : annoAttrs.getStringArray("value")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
for (String pkg : annoAttrs.getStringArray("basePackages")) {
if (StringUtils.hasText(pkg)) {
basePackages.add(pkg);
}
}
for (Class<?> clazz : annoAttrs.getClassArray("basePackageClasses")) {
basePackages.add(ClassUtils.getPackageName(clazz));
}
scanner.registerFilters();
scanner.doScan(StringUtils.toStringArray(basePackages));
}
/**
* {@inheritDoc}
*/
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
}
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(FisherScannerRegistrar.class)
public @interface FisherScan {
String[] value() default {};
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;
Class<? extends Annotation> annotationClass() default Annotation.class;
Class<?> markerInterface() default Class.class;
Class<? extends FisherFactoryBean> factoryBean() default FisherFactoryBean.class;
}