Mybatis是如何集成到Spring容器中的

在现在的JavaEE开发过程中,我们经常会使用到Spring+SpringMVC+Mybatis这个组合。那么Mybatis是如何集成到Spring中的呢?

本文只讲@MapperScan注解方式的整个过程。其他方式类似。

Mapper集成到Spring使用大概分为如下几个步骤:

  1. 使用Import方式引入注册类MapperScannerRegistrar
  2. MapperScannerRegistrar获取配置参数。
  3. 使用ClassPathMapperScanner进行扫描。比如扫描basePackages下面的类。然后对扫描的bean定义处理,比如替换beanClass为MapperFactoryBean.class
  4. Sping进行注册bean,调用到MapperFactoryBean的getObject()。返回一个代理对象MapperProxy
  5. 使用代理对象进行增删改查,接口方法会被代理到MapperMethod,最终用sqlSession进行增删改查。

一、使用Import方式引入注册类MapperScannerRegistrar

先看@MapperScan代码

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

这个注解中有一个@Import(MapperScannerRegistrar.class)。
这个@Import在Spring解析中会先去实例化一个MapperScannerRegistrar.class的单例bean到Spring中。

二、MapperScannerRegistrar

MapperScannerRegistrar实现了ImportBeanDefinitionRegistrar接口。ImportBeanDefinitionRegistrar会返回@Import中定义参数,然后我们使用
ClassPathMapperScanner扫描。

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

三、 使用ClassPathMapperScanner进行扫描

ClassPathMapperScanner继承了ClassPathBeanDefinitionScanner,可以扫描我们需要的bean定义。比如我们经常配置的basePackages。他就会给我扫描下面所有的类,然后对扫描的Mapper bean定义进行处理。后面Spring对扫描的beanDefintion进行初始化等操作。

下面是ClassPathMapperScanner中处理bean定义的方法

 private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    GenericBeanDefinition definition;
    //遍历bean定义
    for (BeanDefinitionHolder holder : beanDefinitions) {
       //BeanDefinitionHolder会包装一个BeanDefinition
      definition = (GenericBeanDefinition) holder.getBeanDefinition();

      if (logger.isDebugEnabled()) {
        logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() 
          + "' and '" + definition.getBeanClassName() + "' mapperInterface");
      }

      // the mapper interface is the original class of the bean
      // but, the actual class of the bean is MapperFactoryBean
	
	//此处把bean定义的构造方法中加一个参数
	//其实后面调用的MapperFactoryBean的这个构造方法,这个构造方法需要一个class。这个class就是扫描出来的Mapper的class
	//public MapperFactoryBean(Class<T> mapperInterface) {
	 //   this.mapperInterface = mapperInterface;
	 // }      
 definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59

// 此处会把   beanClass替换为MapperFactoryBean.class,后续spring进行初始化的时候就会通过MapperFactoryBean.class进行反射
  definition.setBeanClass(this.mapperFactoryBean.getClass());
//添加参数
      definition.getPropertyValues().add("addToConfig", this.addToConfig);
//下面的if没有设置一般都不会进入
      boolean explicitFactoryUsed = false;
      if (StringUtils.hasText(this.sqlSessionFactoryBeanName)) {
        definition.getPropertyValues().add("sqlSessionFactory", new RuntimeBeanReference(this.sqlSessionFactoryBeanName));
        explicitFactoryUsed = true;
      } else if (this.sqlSessionFactory != null) {
        definition.getPropertyValues().add("sqlSessionFactory", this.sqlSessionFactory);
        explicitFactoryUsed = true;
      }

      if (StringUtils.hasText(this.sqlSessionTemplateBeanName)) {
        if (explicitFactoryUsed) {
          logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
        }
        definition.getPropertyValues().add("sqlSessionTemplate", new RuntimeBeanReference(this.sqlSessionTemplateBeanName));
        explicitFactoryUsed = true;
      } else if (this.sqlSessionTemplate != null) {
        if (explicitFactoryUsed) {
          logger.warn("Cannot use both: sqlSessionTemplate and sqlSessionFactory together. sqlSessionFactory is ignored.");
        }
        definition.getPropertyValues().add("sqlSessionTemplate", this.sqlSessionTemplate);
        explicitFactoryUsed = true;
      }

      if (!explicitFactoryUsed) {
        if (logger.isDebugEnabled()) {
          logger.debug("Enabling autowire by type for MapperFactoryBean with name '" + holder.getBeanName() + "'.");
        }
    //设置@autowire的方式为class
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
      }
    }
  }

四、 Sping进行注册MapperFactoryBean

在Spring中有一个比较特殊的bean较FactoryBean。这个bean为使用者提供了一种灵活初始化bean的方式,Spring在构造FactoryBean类型的bean的时候会调用FactoryBean.getObject()进行初始化bean。
下面是MapperFactoryBean初始化bean的方法

  @Override
  public T getObject() throws Exception {
  //会返回一个MapperProxy的类,Spring就会把MapperProxy注册到容器中,当使用Mapper的类型获取bean的时候其实spring返回的是一个MapperProxy代理对象。
    return getSqlSession().getMapper(this.mapperInterface);
  }

返回MapperProxy的方法是通过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);
    }
  }

进入MapperProxyFactory类

public class MapperProxyFactory<T> {
//mapper接口的class
  private final Class<T> mapperInterface;
  //MapperMethod缓存
  private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<Method, MapperMethod>();
  
  public MapperProxyFactory(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
  }

  public Class<T> getMapperInterface() {
    return mapperInterface;
  }

  public Map<Method, MapperMethod> getMethodCache() {
    return methodCache;
  }

  @SuppressWarnings("unchecked")
  protected T newInstance(MapperProxy<T> mapperProxy) {
  //使用JDK动态代理为mapperInterface创建一个mapperProxy的动态代理,后续调用mapperInterface方法的时候会进入mapperProxy的invoke方法。invoke会代理mapperInterface的方法调用。
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

  public T newInstance(SqlSession sqlSession) {
  //new一个MapperProxy代理对象
    final MapperProxy<T> mapperProxy = new MapperProxy<T>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }

}

五. 使用代理对象进行增删改查

MapperProxy其实就是一个InvocationHandler,MapperProxy代理所有的Mapper方法调用。

public class MapperProxy<T> implements InvocationHandler, Serializable {

  private static final long serialVersionUID = -6424540398559729838L;
  private final SqlSession sqlSession;
  private final Class<T> mapperInterface;
  private final Map<Method, MapperMethod> methodCache;

  public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) {
    this.sqlSession = sqlSession;
    this.mapperInterface = mapperInterface;
    this.methodCache = methodCache;
  }

  @Override
  public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
    //代理的对象是否为实体类
      if (Object.class.equals(method.getDeclaringClass())) {
        return method.invoke(this, args);
      } else if (isDefaultMethod(method)) {//是否为接口的default方法
        return invokeDefaultMethod(proxy, method, args);
      }
    } catch (Throwable t) {
      throw ExceptionUtil.unwrapThrowable(t);
    }
    //接口Mapper一般都是进入这里,这边会用一个MapperMethod进行调用,里面其实就是一个sqlSeesion的调用。
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    return mapperMethod.execute(sqlSession, args);
  }

到这里mybatis中Mapper如何集成到Spring的大概就完了。
那么sqlSession如何集成到Spring的呢?

其实很简单,sqlSession其实需要我们自己手动配置,mybatis-spring中为我们提供了一个SqlSessionFactoryBean,这个也是一个FactoryBean。SqlSessionFactoryBean会返回一个SqlSessionFactory。SqlSessionFactory注册到Spring中。在使用sqlSession的地方直接通过SqlSessionFactory返回即可。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

年迈程序

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值