1.Mybatis怎么配合Spring實(shí)現(xiàn)自動(dòng)裝配

交代: SqlSession 作為MyBatis工作的主要頂層API彩掐,表示和數(shù)據(jù)庫交互的會(huì)話衙猪,完成必要數(shù)據(jù)庫增刪改查功能冰沙。里面主要有兩個(gè)參數(shù)垂券,一個(gè)是configuration另一個(gè)是executor董饰。其中Configuration就像一個(gè)大管家,承載了所有的配置文件的信息,例如Map<String, MappedStatement> mappedStatements卒暂,Map<String, ResultMap> resultMaps啄栓,``Map<String, ParameterMap> parameterMaps`等;還有一個(gè)是executor也祠,執(zhí)行crud用的昙楚。其中mappedStatements很重要,因?yàn)閤ml文件里的crud標(biāo)簽的sql語句都會(huì)封裝到這個(gè)對(duì)象中诈嘿。

所用版本為:
Group ID: org.mybatis Artifact ID: mybatis-spring Version: 2.0.2
Group ID: org.mybatis Artifact ID: mybatis Version: 3.5.2

1.文件入口

通常我們會(huì)將@MapperScan("xxx")堪旧,寫到SpringBoot的主函數(shù)入口,就是為了能夠掃描到包

@SpringBootApplication
@MapperScan("com.runchuang.liangpi.dao")
public class LiangpiApplication {
    public static void main(String[] args) {
        SpringApplication.run(LiangpiApplication.class, args);
    }
}

2.MapperScan注解

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {

我們可以看到Import這個(gè)注解奖亚,其實(shí)就是和@Bean這種注解差不多淳梦,可以將括號(hào)里的類,加載到Bean容器中昔字;這里將MapperScannerRegistrar加到了容器中爆袍,這個(gè)類主要負(fù)責(zé)掃描接口,然后偷天換日將本應(yīng)該屬于接口的BeanDefinition的class作郭,換成MapperFactoryBean陨囊,這個(gè)類就是最終就能通過getObject來獲取真正的要執(zhí)行的代理類。

3.MapperScannerRegistrar

public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar{
  @Override
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    AnnotationAttributes mapperScanAttrs = AnnotationAttributes
        .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    if (mapperScanAttrs != null) {
      registerBeanDefinitions(mapperScanAttrs, registry, generateBaseBeanName(importingClassMetadata, 0));
    }
  }
}

獲取MapperScann注解做標(biāo)注的類夹攒,然后獲取他寫的屬性名字蜘醋,例如我這里MapperScan寫了"com.runchuang.liangpi.dao"

4.registerBeanDefinitions

用一個(gè)BeanDefinitionBuilder來構(gòu)造關(guān)于MapperScannerConfigurer的BeanDefinition,獲取掃描的包名咏尝,然后設(shè)置到BeanDefinition的屬性中压语。然后動(dòng)態(tài)注冊(cè)到Bean的容器中。

 void registerBeanDefinitions(AnnotationAttributes annoAttrs, BeanDefinitionRegistry registry, String beanName) {
    BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
    ...
    builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));
    registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
  }

5.接下來我們看看MapperConfigurer

public class MapperScannerConfigurer
    implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
 @Override
  public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
    if (this.processPropertyPlaceHolders) {
      processPropertyPlaceHolders();
    }
    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);

    ....    
    
    scanner.scan(
        StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
  }

}

由于實(shí)現(xiàn)了BeanDefinitionRegistryPostPostProcesser這個(gè)后置處理器编检,所以在實(shí)例化過程中胎食,做了包掃描注冊(cè)以及jdk反向代理。

6.這時(shí)候會(huì)執(zhí)行scan方法

    public int scan(String... basePackages) {
        int beanCountAtScanStart = this.registry.getBeanDefinitionCount();

        doScan(basePackages);

        // Register annotation config processors, if necessary.
        if (this.includeAnnotationConfig) {
            AnnotationConfigUtils.registerAnnotationConfigProcessors(this.registry);
        }

        return (this.registry.getBeanDefinitionCount() - beanCountAtScanStart);
    }

這個(gè)doScan方法并不是調(diào)用自己本類的doScan方法蒙谓,而是調(diào)用父類ClassPathMapperScanner方法

public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {`
  @Override
  public Set<BeanDefinitionHolder> doScan(String... basePackages) {
    Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);
    if (beanDefinitions.isEmpty()) {
      LOGGER.warn(() -> "No MyBatis mapper was found in '" + Arrays.toString(basePackages)
          + "' package. Please check your configuration.");
    } else {
      processBeanDefinitions(beanDefinitions);
    }

    return beanDefinitions;
  }
}

第一個(gè)是掃描包斥季,將接口的注冊(cè)成BeanDefinitionHolder,但是這個(gè)BeanDefinition肯定不能實(shí)例化啦累驮,因?yàn)槭墙涌诼锖ㄇ悖缓髮⑦@個(gè)Set集合的BeanDefinitions處理然后注冊(cè),這里就涉及了偷天換日

7.偷天換日

private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    GenericBeanDefinition definition;
    for (BeanDefinitionHolder holder : beanDefinitions) {
      definition = (GenericBeanDefinition) holder.getBeanDefinition();
      String beanClassName = definition.getBeanClassName();
      // the mapper interface is the original class of the bean
      // but, the actual class of the bean is MapperFactoryBean
      definition.getConstructorArgumentValues().addGenericArgumentValue(beanClassName); // issue #59
      definition.setBeanClass(this.mapperFactoryBeanClass);
      ....省略...
      if (!explicitFactoryUsed) {
              definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
      }
      definition.setLazyInit(lazyInitialization);
    }
  }

這里將傳進(jìn)來的beanDefinitions的class全部換掉換成MapperFactoryBean谤专,自此躁锡,這個(gè)Bean的實(shí)例化不再是接口,而是MapperFactoryBean置侍,這個(gè)Bean還是一個(gè)FactoryBean映之,會(huì)在獲取真正的bean的時(shí)候拦焚,通過getObject去獲取。

8.MapperFactoryBean

  @Override
  public T getObject() throws Exception {
    return getSqlSession().getMapper(this.mapperInterface);
  }

在這里調(diào)用的是MapperRegistry的getMapper方法

  public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    if (mapperProxyFactory == null) {
      throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    try {
      return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
      throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
  }

注意一個(gè)問題這個(gè)mapperProxyFactory.newInstance(sqlSession);就是調(diào)用的動(dòng)態(tài)代理了杠输。

9.動(dòng)態(tài)代理的調(diào)用赎败。

  protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

  public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

10.代理類MapperProxy實(shí)現(xiàn)了InvocationHandler

public class MapperProxy<T> implements InvocationHandler, Serializable {


  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }
}

11.執(zhí)行方法

public class MapperMethod {

  private final SqlCommand command;
  private final MethodSignature method;

  public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
    this.command = new SqlCommand(config, mapperInterface, method);
    this.method = new MethodSignature(config, mapperInterface, method);
  }

  public Object execute(SqlSession sqlSession, Object[] args) {
    Object result;
    switch (command.getType()) {
      case INSERT: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.insert(command.getName(), param));
        break;
      }
      case UPDATE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.update(command.getName(), param));
        break;
      }
      case DELETE: {
        Object param = method.convertArgsToSqlCommandParam(args);
        result = rowCountResult(sqlSession.delete(command.getName(), param));
        break;
      }

    ……
 

12.到底在哪里裝配的Configuration呢?

其實(shí)是在MapperFactoryBean他的父類里蠢甲,DaoSupport僵刮,他實(shí)現(xiàn)了InitializingBean,所以在初始化之后鹦牛,實(shí)現(xiàn)了afterPropertiesSet方法

    @Override
    public final void afterPropertiesSet() throws IllegalArgumentException, BeanInitializationException {
        // Let abstract subclasses check their configuration.
        checkDaoConfig();

        // Let concrete implementations initialize themselves.
        try {
            initDao();
        }
        catch (Exception ex) {
            throw new BeanInitializationException("Initialization of DAO failed", ex);
        }
    }

主要在checkDaoConfig();配置了Configuration搞糕;
具體實(shí)現(xiàn)在MapperFactoryBean里

public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {

  private Class<T> mapperInterface;

  private boolean addToConfig = true;

  public MapperFactoryBean() {
    // intentionally empty
  }

  public MapperFactoryBean(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  protected void checkDaoConfig() {
    super.checkDaoConfig();

    notNull(this.mapperInterface, "Property 'mapperInterface' is required");

    Configuration configuration = getSqlSession().getConfiguration();
    if (this.addToConfig && !configuration.hasMapper(this.mapperInterface)) {
      try {
        configuration.addMapper(this.mapperInterface);
      } 
    }
  }

addMapper具體實(shí)現(xiàn),并且將接口類型穿進(jìn)來了曼追,以便jdk動(dòng)態(tài)代理窍仰。

  public <T> void addMapper(Class<T> type) {
    if (type.isInterface()) {
      if (hasMapper(type)) {
        throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
      }
      boolean loadCompleted = false;
      try {
        knownMappers.put(type, new MapperProxyFactory<>(type));        
        MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
        parser.parse();
        loadCompleted = true;
      } finally {
        if (!loadCompleted) {
          knownMappers.remove(type);
        }
      }
    }
  }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市礼殊,隨后出現(xiàn)的幾起案子驹吮,更是在濱河造成了極大的恐慌,老刑警劉巖膏燕,帶你破解...
    沈念sama閱讀 219,539評(píng)論 6 508
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件钥屈,死亡現(xiàn)場(chǎng)離奇詭異悟民,居然都是意外死亡坝辫,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,594評(píng)論 3 396
  • 文/潘曉璐 我一進(jìn)店門射亏,熙熙樓的掌柜王于貴愁眉苦臉地迎上來近忙,“玉大人,你說我怎么就攤上這事智润〖吧幔” “怎么了?”我有些...
    開封第一講書人閱讀 165,871評(píng)論 0 356
  • 文/不壞的土叔 我叫張陵窟绷,是天一觀的道長(zhǎng)锯玛。 經(jīng)常有香客問我,道長(zhǎng)兼蜈,這世上最難降的妖魔是什么攘残? 我笑而不...
    開封第一講書人閱讀 58,963評(píng)論 1 295
  • 正文 為了忘掉前任,我火速辦了婚禮为狸,結(jié)果婚禮上歼郭,老公的妹妹穿的比我還像新娘。我一直安慰自己辐棒,他們只是感情好病曾,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,984評(píng)論 6 393
  • 文/花漫 我一把揭開白布牍蜂。 她就那樣靜靜地躺著,像睡著了一般泰涂。 火紅的嫁衣襯著肌膚如雪鲫竞。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,763評(píng)論 1 307
  • 那天逼蒙,我揣著相機(jī)與錄音贡茅,去河邊找鬼。 笑死其做,一個(gè)胖子當(dāng)著我的面吹牛顶考,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播妖泄,決...
    沈念sama閱讀 40,468評(píng)論 3 420
  • 文/蒼蘭香墨 我猛地睜開眼驹沿,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了蹈胡?” 一聲冷哼從身側(cè)響起渊季,我...
    開封第一講書人閱讀 39,357評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎罚渐,沒想到半個(gè)月后却汉,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,850評(píng)論 1 317
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡荷并,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 38,002評(píng)論 3 338
  • 正文 我和宋清朗相戀三年合砂,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片源织。...
    茶點(diǎn)故事閱讀 40,144評(píng)論 1 351
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡翩伪,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出谈息,到底是詐尸還是另有隱情缘屹,我是刑警寧澤,帶...
    沈念sama閱讀 35,823評(píng)論 5 346
  • 正文 年R本政府宣布侠仇,位于F島的核電站轻姿,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏逻炊。R本人自食惡果不足惜互亮,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,483評(píng)論 3 331
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望嗅骄。 院中可真熱鬧胳挎,春花似錦、人聲如沸溺森。這莊子的主人今日做“春日...
    開封第一講書人閱讀 32,026評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至医窿,卻和暖如春磅甩,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背姥卢。 一陣腳步聲響...
    開封第一講書人閱讀 33,150評(píng)論 1 272
  • 我被黑心中介騙來泰國(guó)打工卷要, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人独榴。 一個(gè)月前我還...
    沈念sama閱讀 48,415評(píng)論 3 373
  • 正文 我出身青樓僧叉,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親棺榔。 傳聞我的和親對(duì)象是個(gè)殘疾皇子瓶堕,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 45,092評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容