zoukankan      html  css  js  c++  java
  • 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);
            }
          }
        }
    
  • 相关阅读:
    Travis 编译使用 JDK 的版本
    《程序员的职业素养》读书笔记
    先做人,在做事
    CAP理论
    ZGC实践
    虚拟化技术的分类及介绍
    C# AD域验证登录
    dotnet core 3.1 站点发布成windows服务
    windows 服务无法调用office word COM接口
    Ext 动态加载js文件
  • 原文地址:https://www.cnblogs.com/sufferingStriver/p/10331401.html
Copyright © 2011-2022 走看看