MapperScan的工作,Spring-Mybatis怎么自动getMapper

@MapperScan

@Import(MapperScannerRegistrar.class)
@Repeatable(MapperScans.class)
public @interface MapperScan {
    ...
}

导入了MapperScannerRegistrar,这里面registerBeanDefinitions方法中new了一个ClassPathMapperScanner,调用doScan方法进行spring包扫描,会返回对应的BeanDefinition。

//ClassPathMapperScanner
@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;
  }

在ClassPathMapperScanner中doScan会调用父类的doScan方法,这里面会通过BeanDefinition注册Bean

registerBeanDefinition(definitionHolder, this.registry);

在ClassPathMapperScanner的processBeanDefinitions中会设置BeanDefinition的构造参数,BeanClass等属性。

到现在为止,Spring的BeanFactory中已经有了很多MapperFactoryBean(每一个都是不同的Mapper接口类型),然后MapperFactoryBean继承了DaoSupport,在afterPropertiesSet方法中会执行checkDaoConfig();

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

在MapperFactoryBean的checkDaoConfig方法中,会注册Mapper到Configuration的mapperRegistry中。

//MapperFactoryBean
@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);
      } catch (Exception e) {
        logger.error("Error while adding the mapper '" + this.mapperInterface + "' to configuration.", e);
        throw new IllegalArgumentException(e);
      } finally {
        ErrorContext.instance().reset();
      }
    }
  }

然后从容器中拿Mapper的Bean的时候,实际调用MapperFactoryBean的getObject方法,会拿到对应得Mapper。

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

后面就是Mybatis中的内容,会通过MapperProxyFactory new 一个MapperProxy(InnovationHandler),然后里面有MapperMethod的逻辑。

这里走Mybatis的逻辑。

//DefaultSqlSession
@Override
  public <T> T getMapper(Class<T> type) {
    return configuration.<T>getMapper(type, this);
  }
//Configuration
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    return mapperRegistry.getMapper(type, sqlSession);
  }
//MapperRegistry
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);
    }
  }

MapperFactoryBean这中间还有封装,MapperFactoryBean继承了SqlSessionDaoSupport,SqlSessionDaoSupport中装饰了SqlSessionTemplate.(SqlSessionTemplate代理了SqlSession)

SqlSessionTemplate装饰了SqlSessionFactory,通过SqlSessionFactory,Proxy newInstance了一个SqlSession实例(sqlSessionProxy),使用了SqlSessionInterceptor(InnovationHandler)这个代理。在这里面进行SQLSession的管理,然后在给SQLSession本身执行。

SqlSessionTemplate

MybatisAutoConfiguration 注入了SqlSessionTemplate Bean在IOC容器中。

SqlSessionTemplate 实际上是Spring对于SqlSession的管理,自动创建,自动关闭。调用SqlSessionTemplate 的方法实际上是调用sqlSessionProxy[SqlSessionInterceptor为InnovationHandler]的方法。

//SqlSessionTemplate
@Override
  public int delete(String statement) {
    return this.sqlSessionProxy.delete(statement);
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public int delete(String statement, Object parameter) {
    return this.sqlSessionProxy.delete(statement, parameter);
  }
//MybatisAutoConfiguration
@Bean
  @ConditionalOnMissingBean
  public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
    ExecutorType executorType = this.properties.getExecutorType();
    if (executorType != null) {
      return new SqlSessionTemplate(sqlSessionFactory, executorType);
    } else {
      return new SqlSessionTemplate(sqlSessionFactory);
    }
  }

在SqlSessionInterceptor中会自动创建sqlSession,自动关闭sqlSession。

SqlSessionInterceptor会尝试获取当前线程的sqlsession,如果没有就使用sqlsessionfactory创建。

//SqlSessionTemplate
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);
        }
      }
    }

转载于:https://www.cnblogs.com/sufferingStriver/p/10331401.html

Spring Boot中,配置多数据源可以通过以下步骤实现: 1. 在`application.properties`或`application.yml`中配置多个数据源的相关属性,例如: ```yml spring: datasource: master: url: jdbc:mysql://localhost:3306/master_db username: root password: root driver-class-name: com.mysql.jdbc.Driver slave: url: jdbc:mysql://localhost:3306/slave_db username: root password: root driver-class-name: com.mysql.jdbc.Driver ``` 2. 创建多个数据源的实例,并将它们注入到`SqlSessionFactory`中: ```java @Configuration public class MybatisConfig { @Bean @ConfigurationProperties(prefix = "spring.datasource.master") public DataSource masterDataSource() { return DataSourceBuilder.create().build(); } @Bean @ConfigurationProperties(prefix = "spring.datasource.slave") public DataSource slaveDataSource() { return DataSourceBuilder.create().build(); } @Bean public SqlSessionFactory masterSqlSessionFactory(@Qualifier("masterDataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); factoryBean.setDataSource(dataSource); return factoryBean.getObject(); } @Bean public SqlSessionFactory slaveSqlSessionFactory(@Qualifier("slaveDataSource") DataSource dataSource) throws Exception { SqlSessionFactoryBean factoryBean = new SqlSessionFactoryBean(); factoryBean.setDataSource(dataSource); return factoryBean.getObject(); } } ``` 3. 配置`@MapperScan`注解扫描多个`Mapper`接口所在的包路径,并指定对应的`SqlSessionFactory`: ```java @Configuration @MapperScan(basePackages = "com.example.mapper.master", sqlSessionFactoryRef = "masterSqlSessionFactory") public class MasterDataSourceConfig { } @Configuration @MapperScan(basePackages = "com.example.mapper.slave", sqlSessionFactoryRef = "slaveSqlSessionFactory") public class SlaveDataSourceConfig { } ``` 这样就可以在应用中使用多个数据源了。在需要访问不同数据源的`Mapper`接口方法上,可以使用`@Qualifier`注解指定对应的`SqlSessionFactory`,例如: ```java @Service public class UserService { @Autowired @Qualifier("masterSqlSessionFactory") private SqlSessionFactory masterSqlSessionFactory; @Autowired @Qualifier("slaveSqlSessionFactory") private SqlSessionFactory slaveSqlSessionFactory; public User getMasterUserById(int id) { try (SqlSession sqlSession = masterSqlSessionFactory.openSession()) { UserMapper userMapper = sqlSession.getMapper(UserMapper.class); return userMapper.getById(id); } } public User getSlaveUserById(int id) { try (SqlSession sqlSession = slaveSqlSessionFactory.openSession()) { UserMapper userMapper = sqlSession.getMapper(UserMapper.class); return userMapper.getById(id); } } } ```
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值