mybatis源码分析

对于Java开发来说,mybatis对于我们是经常用到的,我们知道我们只要定义相应的接口,配置好扫包的路径,就会自动生成相关的接口实现代理类,那么mybatis是如何借助Spring完成这些操作的呢,在这里我就主要分享一下我在阅读了部分Mybatis源码后的一些总结

package com.xp.config;

import com.xp.dao.XXX;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Import;
import org.springframework.context.annotation.ImportResource;

@Configuration
@ComponentScan("com.xp")
//@Import({XXX.class})
@MapperScan("com.xp")
public class AppConfig {
}

这个MapperScan大家应该很熟悉,也就是mybatis的扫包注解,这也是mybatis如何让Spring动态管理包路径下面的接口,我们点进去可以看到

明显可以看出这个MapperScan是一个复合注解,关键的一个Import注解导入了一个叫MapperScannerRegistar的类,点进去

可以看到这个MapperScannerRegistar类实现的Spring提供的 ImportBeanDefinitichonRegistrar这个接口类,相信看过Spring源码的朋友都知道这个类是Spring提供的可以动态创建bean的一个扩展点,由于MapperScannerRegistar实现了ImportBeanDefinitionRegistar这个接口,也覆盖重写了里面的registerBeanDefinitions方法,从名字也可以看出这个方法是用来注册BeanDefinition的,对于什么是BeanDefinition这里就不做过多说明,其实就是Spring用来描述它需要管理的Java对象,然后封装成BeanDefinition这个数据类型,我们来看看这个registerBeanDefinitions做了些什么操作

/**
 * {@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));
}

首先这个方法new 了一个 ClassPathMapperScanner 的扫描器,这个ClassPathMapperScanner 继承了Spring提供的

ClassPathBeanDefinitionScanner这个类

然后调用了doScan方法,我跟进去看代码

/**
 * Calls the parent search that will search and register all the candidates.
 * Then the registered objects are post processed to set them as
 * MapperFactoryBeans
 */
@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;
}

然后会调用父类的doScan方法,也就是Spring提供的ClassPathBeanDefinitionScanner的doScan方法,我们来看父类的这个doScan方法

/**
 * Perform a scan within the specified base packages,
 * returning the registered bean definitions.
 * <p>This method does <i>not</i> register an annotation config processor
 * but rather leaves this up to the caller.
 * @param basePackages the packages to check for annotated classes
 * @return set of beans registered if any for tooling registration purposes (never {@code null})
 */
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
   Assert.notEmpty(basePackages, "At least one base package must be specified");
   Set<BeanDefinitionHolder> beanDefinitions = new LinkedHashSet<>();
   for (String basePackage : basePackages) {
      Set<BeanDefinition> candidates = findCandidateComponents(basePackage);
      for (BeanDefinition candidate : candidates) {
         ScopeMetadata scopeMetadata = this.scopeMetadataResolver.resolveScopeMetadata(candidate);
         candidate.setScope(scopeMetadata.getScopeName());
         String beanName = this.beanNameGenerator.generateBeanName(candidate, this.registry);
         if (candidate instanceof AbstractBeanDefinition) {
            postProcessBeanDefinition((AbstractBeanDefinition) candidate, beanName);
         }
         if (candidate instanceof AnnotatedBeanDefinition) {
            AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);
         }
         if (checkCandidate(beanName, candidate)) {
            BeanDefinitionHolder definitionHolder = new BeanDefinitionHolder(candidate, beanName);
            definitionHolder =
                  AnnotationConfigUtils.applyScopedProxyMode(scopeMetadata, definitionHolder, this.registry);
            beanDefinitions.add(definitionHolder);
            registerBeanDefinition(definitionHolder, this.registry);
         }
      }
   }
   return beanDefinitions;
}

里面关键的一个findCandidateComponents(basePackage)会调用scanCandidateComponents方法,在这个方法里面完成对包路径下面的类的扫描,并判断该类文件是不是需要被Spring容器管理的Bean,然后放到这个candidates的set集合返回回去

/**
 * Scan the class path for candidate components.
 * @param basePackage the package to check for annotated classes
 * @return a corresponding Set of autodetected bean definitions
 */
public Set<BeanDefinition> findCandidateComponents(String basePackage) {
   if (this.componentsIndex != null && indexSupportsIncludeFilters()) {
      return addCandidateComponentsFromIndex(this.componentsIndex, basePackage);
   }
   else {
      return scanCandidateComponents(basePackage);
   }
}
private Set<BeanDefinition> scanCandidateComponents(String basePackage) {
   Set<BeanDefinition> candidates = new LinkedHashSet<>();
   try {
      String packageSearchPath = ResourcePatternResolver.CLASSPATH_ALL_URL_PREFIX +
            resolveBasePackage(basePackage) + '/' + this.resourcePattern;
      Resource[] resources = getResourcePatternResolver().getResources(packageSearchPath);
      boolean traceEnabled = logger.isTraceEnabled();
      boolean debugEnabled = logger.isDebugEnabled();
      for (Resource resource : resources) {
         if (traceEnabled) {
            logger.trace("Scanning " + resource);
         }
         if (resource.isReadable()) {
            try {
               MetadataReader metadataReader = getMetadataReaderFactory().getMetadataReader(resource);
               if (isCandidateComponent(metadataReader)) {
                  ScannedGenericBeanDefinition sbd = new ScannedGenericBeanDefinition(metadataReader);
                  sbd.setResource(resource);
                  sbd.setSource(resource);
                  if (isCandidateComponent(sbd)) {
                     if (debugEnabled) {
                        logger.debug("Identified candidate component class: " + resource);
                     }
                     candidates.add(sbd);
                  }
                  else {
                     if (debugEnabled) {
                        logger.debug("Ignored because not a concrete top-level class: " + resource);
                     }
                  }
               }
               else {
                  if (traceEnabled) {
                     logger.trace("Ignored because not matching any filter: " + resource);
                  }
               }
            }
            catch (Throwable ex) {
               throw new BeanDefinitionStoreException(
                     "Failed to read candidate component class: " + resource, ex);
            }
         }
         else {
            if (traceEnabled) {
               logger.trace("Ignored because not readable: " + resource);
            }
         }
      }
   }
   catch (IOException ex) {
      throw new BeanDefinitionStoreException("I/O failure during classpath scanning", ex);
   }
   return candidates;
}

拿到这些需要被Spring管理的Bean集合,接着会走下面的代码

从上图可以看到,程序会继续往下面执行,大概说明一下 

AnnotationConfigUtils.processCommonDefinitionAnnotations((AnnotatedBeanDefinition) candidate);

方法会解析这些bean上面的lazy一些常用的注解属性,接着会执行一个关键的方法

registerBeanDefinition我们跟进去看看
/**
 * Register the specified bean with the given registry.
 * <p>Can be overridden in subclasses, e.g. to adapt the registration
 * process or to register further bean definitions for each scanned bean.
 * @param definitionHolder the bean definition plus bean name for the bean
 * @param registry the BeanDefinitionRegistry to register the bean with
 */
protected void registerBeanDefinition(BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry) {
   BeanDefinitionReaderUtils.registerBeanDefinition(definitionHolder, registry);
}
/**
 * Register the given bean definition with the given bean factory.
 * @param definitionHolder the bean definition including name and aliases
 * @param registry the bean factory to register with
 * @throws BeanDefinitionStoreException if registration failed
 */
public static void registerBeanDefinition(
      BeanDefinitionHolder definitionHolder, BeanDefinitionRegistry registry)
      throws BeanDefinitionStoreException {

   // Register bean definition under primary name.
   String beanName = definitionHolder.getBeanName();
   registry.registerBeanDefinition(beanName, definitionHolder.getBeanDefinition());

   // Register aliases for bean name, if any.
   String[] aliases = definitionHolder.getAliases();
   if (aliases != null) {
      for (String alias : aliases) {
         registry.registerAlias(beanName, alias);
      }
   }
}

首先会调用BeanDefinitionReaderUtils的registerBeanDefinition方法,然后执行registry的registerBeanDefinition方法把扫描的Bean方法Spring容器的一个beanDefinitionMap的map容器里面,这些数据会在之后Spring生命周期过程中完成初始化,这就是mybatis如何借助Spring完成相关类的管理,当然这里面扫包的到的Bean的类型是一个特殊的工,厂Bean,也就是FactoryBean这个类里面有一个getObject方法,完成一个动态代理,有兴趣的朋友可以去看看mybatis的源码,当然整个这个registerBeanDefinitions的方法是在spring容器refesh里面的 invokeBeanFactoryPostProcessors方法里面的一个工厂后置处理器ConfigurationClassPostProcessor的类完成解析和触发的,有兴趣的朋友可以去研究一下,这里就不做过多描述,以上就是我的一点点理解

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值