1. 程式人生 > >Mybatis原始碼---重寫一個最簡單的Mybatis架構實現(三)

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來防止被掃描。 

    之所以要重點說明

MapperScannerConfigurer類的屬性含義,是因為在Mybatis被自動配置的時候,其實就是這些內容被自動設值了。Mybatis自動配置的關鍵是@MapperScan,可以看看它的原始碼:

@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;

}