天天看点

MapperScan注解分析

Spring 3.x+版本支持使用@Configuration和@Bean注解来提供基于 Java 的配置。

如果使用基于java的配置,可以使用@MapperScan注解来扫描映射Mapper接口类。

@MapperScan(value = "org.springframework.orm.mapper", sqlSessionFactoryRef = "sqlSessionFactoryBean")

// 通过Bean注解注入自定义构建的SqlSessionFactoryBean对象
@Bean
    public SqlSessionFactoryBean sqlSessionFactoryBean() throws Exception {
        SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
        sqlSessionFactoryBean.setDataSource(dataSource());
        sqlSessionFactoryBean.setMapperLocations(new PathMatchingResourcePatternResolver()
            .getResources("T1Mapper.xml"));
        sqlSessionFactoryBean.setTypeAliasesPackage("org.springframework.orm.entity");
        return sqlSessionFactoryBean;
    }
           

value:指定mapper接口类包路径

sqlSessionFactoryRef:指定SqlSessionFactoryBean对象名

MapperScan进一步解析

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
// 通过spring import注解方式加载
@Import(MapperScannerRegistrar.class)
public @interface MapperScan {
           

@Import 是被用来整合所有在@Configuration注解中定义的bean配置

这里MapperScannerRegistrar实现了ImportBeanDefinitionRegistrar接口注入bean,再通过import注解导入加载。

@Override
  public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    // 获取注解
    AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    // 扩展spring中ClassPathBeanDefinitionScanner类将类扫面成BeanDefinition
    ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
           
// 扫描包路径集
    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));
    }
    // 注册相关filter
    scanner.registerFilters();
    // 扫描
    scanner.doScan(StringUtils.toStringArray(basePackages));
           

scanner.doScan中将实现mapper扫描映射

// 通过spring提供的方法将mapper接口类扫描转换成BeanDefinitionHolder集合
    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 {
      // 再根据BeanDefinitionHolder做相关处理
      processBeanDefinitions(beanDefinitions);
    }

    return beanDefinitions;
           

processBeanDefinitions方法中会设置mapper注入对应实现的bean等配置

private MapperFactoryBean<?> mapperFactoryBean = new MapperFactoryBean<Object>();

// 配置构造函数中参数值
definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
      // 设置BeanClass为MapperFactoryBean类
      definition.setBeanClass(this.mapperFactoryBean.getClass());
      // 设置属性值
      definition.getPropertyValues().add("addToConfig", this.addToConfig);
           

definition.setBeanClass(this.mapperFactoryBean.getClass())此处设置mapper接口对应的实现类为MapperFactoryBean类。

MapperFactoryBean类实现了FactoryBean功能,最后实际执行的bean对象是通过getObject方法获取。

public T getObject() throws Exception {
    // 此处使用了动态代理
    return getSqlSession().getMapper(this.mapperInterface);
  }
           
protected T newInstance(MapperProxy<T> mapperProxy) {
    // 通过MapperProxy生成mapper接口执行的代理对象
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[] { mapperInterface }, mapperProxy);
  }

  public T newInstance(SqlSession sqlSession) {
    // MapperProxy就是实际的目标对象,也就是代理对象
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
  }
           

到此处为止,可以看到mybatis加载的mapper接口对应的实现目标类都是MapperProxy对象。

SqlSessionTemplate类中执行具体操作是再次使用了一次代理

this.sqlSessionFactory = sqlSessionFactory;
    this.executorType = executorType;
    this.exceptionTranslator = exceptionTranslator;
    this.sqlSessionProxy = (SqlSession) newProxyInstance(
        SqlSessionFactory.class.getClassLoader(),
        new Class[] { SqlSession.class },
        new SqlSessionInterceptor());
           
sqlSessionProxy代理对象是在SqlSessionTemplate对象构造方法时指定的
           
......
  public void select(String statement, ResultHandler handler) {
    this.sqlSessionProxy.select(statement, handler);
  }

  public void select(String statement, Object parameter, ResultHandler handler) {
    this.sqlSessionProxy.select(statement, parameter, handler);
  }
  ......
           

SqlSessionInterceptor代理类

private class SqlSessionInterceptor implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      SqlSession sqlSession = getSqlSession(
          SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType,
          SqlSessionTemplate.this.exceptionTranslator);
      try {
        Object result = method.invoke(sqlSession, args);
        if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
          // force commit even on non-dirty sessions because some databases require
          // a commit/rollback before calling close()
          sqlSession.commit(true);
        }
        return result;
      } catch (Throwable t) {
        Throwable unwrapped = unwrapThrowable(t);
        if (SqlSessionTemplate.this.exceptionTranslator != null && unwrapped instanceof PersistenceException) {
          // release the connection to avoid a deadlock if the translator is no loaded. See issue #22
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
          sqlSession = null;
          Throwable translated = SqlSessionTemplate.this.exceptionTranslator.translateExceptionIfPossible((PersistenceException) unwrapped);
          if (translated != null) {
            unwrapped = translated;
          }
        }
        throw unwrapped;
      } finally {
        if (sqlSession != null) {
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
        }
      }
    }
  }
           

继续阅读