• 07Spring整合Mybatis


    Spring之整合Mybatis

    整合核心思路

    由很多框架都需要和Spring进行整合,而整合的核心思想就是把其他框架所产生的对象放到Spring容器中,让其成为Bean。

    比如Mybatis,Mybatis框架可以单独使用,而单独使用Mybatis框架就需要用到Mybatis所提供的一些类构造出对应的对象,然后使用该对象,就能使用到Mybatis框架给我们提供的功能,和Mybatis整合Spring就是为了将这些对象放入Spring容器中成为Bean,只要成为了Bean,在我们的Spring项目中就能很方便的使用这些对象了,也就能很方便的使用Mybatis框架所提供的功能了。

    Mybatis-Spring 1.3.2版本

    1. 通过@MapperScan导入了MapperScannerRegistrar类
    2. MapperScannerRegistrar类实现了ImportBeanDefinitionRegistrar接口,所以Spring在启动时会调用MapperScannerRegistrar类中的registerBeanDefinitions方法
    3. 在registerBeanDefinitions方法中定义了一个ClassPathMapperScanner对象,用来扫描mapper
    4. 设置ClassPathMapperScanner对象可以扫描到接口,因为在Spring中是不会扫描接口的
    5. 同时因为ClassPathMapperScanner中重写了isCandidateComponent方法,导致isCandidateComponent只会认为接口是备选者Component
    6. 通过利用Spring的扫描后,会把接口扫描出来并且得到对应的BeanDefinition
    7. 接下来把扫描得到的BeanDefinition进行修改,把BeanClass修改为MapperFactoryBean,把AutowireMode修改为byType
    8. 扫描完成后,Spring就会基于BeanDefinition去创建Bean了,相当于每个Mapper对应一个FactoryBean
    9. 在MapperFactoryBean中的getObject方法中,调用了getSqlSession()去得到一个sqlSession对象,然后根据对应的Mapper接口生成一个Mapper接口代理对象,这个代理对象就成为Spring容器中的Bean
    10. sqlSession对象是Mybatis中的,一个sqlSession对象需要SqlSessionFactory来产生
    11. MapperFactoryBean的AutowireMode为byType,所以Spring会自动调用set方法,有两个set方法,一个setSqlSessionFactory,一个setSqlSessionTemplate,而这两个方法执行的前提是根据方法参数类型能找到对应的bean,所以Spring容器中要存在SqlSessionFactory类型的bean或者SqlSessionTemplate类型的bean。
    12. 如果你定义的是一个SqlSessionFactory类型的bean,那么最终也会被包装为一个SqlSessionTemplate对象,并且赋值给sqlSession属性
    13. 而在SqlSessionTemplate类中就存在一个getMapper方法,这个方法中就产生一个Mapper接口代理对象
    14. 到时候,当执行该代理对象的某个方法时,就会进入到Mybatis框架的底层执行流程,详细的请看下图

    Spring整合Mybatis之后SQL执行流程: https://www.processon.com/view/link/6152cc385653bb6791db436c

    源码流程

    @MapperScan导入了MapperScannerRegistrar类

    @Import(MapperScannerRegistrar.class)
    public @interface MapperScan {
    

    MapperScannerRegistrar类实现了ImportBeanDefinitionRegistrar接口,在Spring启动时会调用MapperScannerRegistrar类中的registerBeanDefinitions方法

    public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
    
      private ResourceLoader resourceLoader;
    
      /**
       * {@inheritDoc}
       */
      @Override
      public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    
        AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
        //定义了一个ClassPathMapperScanner对象,用来扫描mapper
        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);
        }
    
    	//设置ClassPathMapperScanner对象可以扫描到接口,因为在Spring中是不会扫描接口的
        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));
        }
        //添加TypeFilter,总是返回true,用于绕过spring的doscan的scanCandidateComponents方法的isCandidateComponent判断
        scanner.registerFilters();
        scanner.doScan(StringUtils.toStringArray(basePackages));
      }
    
      /**
       * {@inheritDoc}
       */
      @Override
      public void setResourceLoader(ResourceLoader resourceLoader) {
        this.resourceLoader = resourceLoader;
      }
    
    }
    

    ClassPathMapperScanner中重写了isCandidateComponent方法,导致isCandidateComponent只会认为接口是备选者Component

    @Override
    protected boolean isCandidateComponent(AnnotatedBeanDefinition beanDefinition) {
      return beanDefinition.getMetadata().isInterface() && beanDefinition.getMetadata().isIndependent();
    }
    

    通过利用Spring的扫描后,会把接口扫描出来并且得到对应的BeanDefinition

    @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;
    }
    
    private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
      GenericBeanDefinition definition;
      for (BeanDefinitionHolder holder : beanDefinitions) {
        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
        //MapperFactoryBean的构造器,参数就是对应的mapper接口
        definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
        //把BeanClass修改为MapperFactoryBean
        definition.setBeanClass(this.mapperFactoryBean.getClass());
    
        definition.getPropertyValues().add("addToConfig", this.addToConfig);
    
        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() + "'.");
          }
          //把AutowireMode修改为byType,会根据set来自动注入
          definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
        }
      }
    }
    

    扫描完成后,Spring就会基于BeanDefinition去创建Bean了,相当于每个Mapper对应一个MapperFactoryBean

    public class MapperFactoryBean<T> extends SqlSessionDaoSupport implements FactoryBean<T> {
    
    //被代理对象的类型
      private Class<T> mapperInterface;
    
      private boolean addToConfig = true;
    
      public MapperFactoryBean() {
        //intentionally empty 
      }
      
      public MapperFactoryBean(Class<T> mapperInterface) {
        this.mapperInterface = mapperInterface;
      }
    
      /**
       * {@inheritDoc}
       */
      @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();
          }
        }
      }
    
      /**
       * {@inheritDoc}
       */
      @Override
      public T getObject() throws Exception {
      //这里就会生成jdk动态代理对象
      //调用了getSqlSession()去得到一个sqlSession对象,然后根据对应的Mapper接口生成一个Mapper接口代理对象,这个代理对象就成为Spring容器中的Bean
        return getSqlSession().getMapper(this.mapperInterface);
      }
    
      /**
       * {@inheritDoc}
       */
      @Override
      public Class<T> getObjectType() {
      //类型就是接口的类型
        return this.mapperInterface;
      }
    
      /**
       * {@inheritDoc}
       */
      @Override
      public boolean isSingleton() {
        return true;
      }
    
    }
    

    sqlSession对象是Mybatis中的,一个sqlSession对象需要SqlSessionFactory来产生,使用sqlSessionFactory添加了一个SqlSessionTemplate再转换成sqlSession

    public abstract class SqlSessionDaoSupport extends DaoSupport {
    
      private SqlSession sqlSession;
    
      private boolean externalSqlSession;
    
      public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
        if (!this.externalSqlSession) {
        //使用sqlSessionFactory添加了一个SqlSessionTemplate再转换成sqlSession
          this.sqlSession = new SqlSessionTemplate(sqlSessionFactory);
        }
      }
    
      public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
        this.sqlSession = sqlSessionTemplate;
        this.externalSqlSession = true;
      }
    
      /**
       * Users should use this method to get a SqlSession to call its statement methods
       * This is SqlSession is managed by spring. Users should not commit/rollback/close it
       * because it will be automatically done.
       *
       * @return Spring managed thread safe SqlSession
       */
      public SqlSession getSqlSession() {
      //这里就是返回的SqlSession对象
        return this.sqlSession;
      }
    
      /**
       * {@inheritDoc}
       */
      @Override
      protected void checkDaoConfig() {
        notNull(this.sqlSession, "Property 'sqlSessionFactory' or 'sqlSessionTemplate' are required");
      }
    
    }
    

    而在SqlSessionTemplate类中就存在一个getMapper方法,这个方法中就产生一个Mapper接口代理对象

    public class SqlSessionTemplate implements SqlSession, DisposableBean {
    
      private final SqlSessionFactory sqlSessionFactory;
    
      private final ExecutorType executorType;
    
    //注意,这里的SqlSession其实是sqlSessionProxy
      private final SqlSession sqlSessionProxy;
    
      private final PersistenceExceptionTranslator exceptionTranslator;
    
      /**
       * Constructs a Spring managed SqlSession with the {@code SqlSessionFactory}
       * provided as an argument.
       *
       * @param sqlSessionFactory
       */
      public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory) {
        this(sqlSessionFactory, sqlSessionFactory.getConfiguration().getDefaultExecutorType());
      }
    
      /**
       * Constructs a Spring managed SqlSession with the {@code SqlSessionFactory}
       * provided as an argument and the given {@code ExecutorType}
       * {@code ExecutorType} cannot be changed once the {@code SqlSessionTemplate}
       * is constructed.
       *
       * @param sqlSessionFactory
       * @param executorType
       */
      public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType) {
        this(sqlSessionFactory, executorType,
            new MyBatisExceptionTranslator(
                sqlSessionFactory.getConfiguration().getEnvironment().getDataSource(), true));
      }
    
      /**
       * Constructs a Spring managed {@code SqlSession} with the given
       * {@code SqlSessionFactory} and {@code ExecutorType}.
       * A custom {@code SQLExceptionTranslator} can be provided as an
       * argument so any {@code PersistenceException} thrown by MyBatis
       * can be custom translated to a {@code RuntimeException}
       * The {@code SQLExceptionTranslator} can also be null and thus no
       * exception translation will be done and MyBatis exceptions will be
       * thrown
       *
       * @param sqlSessionFactory
       * @param executorType
       * @param exceptionTranslator
       */
      public SqlSessionTemplate(SqlSessionFactory sqlSessionFactory, ExecutorType executorType,
          PersistenceExceptionTranslator exceptionTranslator) {
    
        notNull(sqlSessionFactory, "Property 'sqlSessionFactory' is required");
        notNull(executorType, "Property 'executorType' is required");
    
        this.sqlSessionFactory = sqlSessionFactory;
        this.executorType = executorType;
        this.exceptionTranslator = exceptionTranslator;
        //注意,这里的SqlSession其实是sqlSessionProxy代理对象
        this.sqlSessionProxy = (SqlSession) newProxyInstance(
            SqlSessionFactory.class.getClassLoader(),
            new Class[] { SqlSession.class },
            new SqlSessionInterceptor());
      }
      
      @Override
      public <T> T getMapper(Class<T> type) {
        return getConfiguration().getMapper(type, this);
      }
      
    }
    

    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 {
        //真正执行sql的方法
          Object result = method.invoke(sqlSession, args);
          //这里使用ThreadLocal
          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);
          }
        }
      }
    }
    

    在getSqlSession中看一下

    public static SqlSession getSqlSession(SqlSessionFactory sessionFactory, ExecutorType executorType, PersistenceExceptionTranslator exceptionTranslator) {
    
      notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
      notNull(executorType, NO_EXECUTOR_TYPE_SPECIFIED);
    
      SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
    
      SqlSession session = sessionHolder(executorType, holder);
      if (session != null) {
        return session;
      }
    
      if (LOGGER.isDebugEnabled()) {
        LOGGER.debug("Creating a new SqlSession");
      }
    	//这里sessionFactory就是DefaultSqlSessionFactory,并返回
      session = sessionFactory.openSession(executorType);
    
      registerSessionHolder(sessionFactory, executorType, exceptionTranslator, session);
    
      return session;
    }
    

    继续openSession

    @Override
    public SqlSession openSession(ExecutorType execType, Connection connection) {
      return openSessionFromConnection(execType, connection);
    }
    
    @Override
    public Configuration getConfiguration() {
      return configuration;
    }
    
    private SqlSession openSessionFromDataSource(ExecutorType execType, TransactionIsolationLevel level, boolean autoCommit) {
      Transaction tx = null;
      try {
        final Environment environment = configuration.getEnvironment();
        final TransactionFactory transactionFactory = getTransactionFactoryFromEnvironment(environment);
        tx = transactionFactory.newTransaction(environment.getDataSource(), level, autoCommit);
        final Executor executor = configuration.newExecutor(tx, execType);
        //返回了DefaultSqlSession
        return new DefaultSqlSession(configuration, executor, autoCommit);
      } catch (Exception e) {
        closeTransaction(tx); // may have fetched a connection so lets call close()
        throw ExceptionFactory.wrapException("Error opening session.  Cause: " + e, e);
      } finally {
        ErrorContext.instance().reset();
      }
    }
    

    因为SqlSession就是是SqlSessionTemplate,因此会调用SqlSessionTemplate的selectOne方法来查询

     /**
       * {@inheritDoc}
       */
      @Override
      public <T> T selectOne(String statement) {
        return this.sqlSessionProxy.<T> selectOne(statement);
      }
    

    DefaultSqlSession是线程不安全的,SqlSessionTemplate使用ThreadLocal保证线程安全

    SqlSessionInterceptor.invoke方法的isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)

    public static boolean isSqlSessionTransactional(SqlSession session, SqlSessionFactory sessionFactory) {
      notNull(session, NO_SQL_SESSION_SPECIFIED);
      notNull(sessionFactory, NO_SQL_SESSION_FACTORY_SPECIFIED);
    
      SqlSessionHolder holder = (SqlSessionHolder) TransactionSynchronizationManager.getResource(sessionFactory);
    
      return (holder != null) && (holder.getSqlSession() == session);
    }
    

    跟一下getResource

    @Nullable
    public static Object getResource(Object key) {
       Object actualKey = TransactionSynchronizationUtils.unwrapResourceIfNecessary(key);
       Object value = doGetResource(actualKey);
       if (value != null && logger.isTraceEnabled()) {
          logger.trace("Retrieved value [" + value + "] for key [" + actualKey + "] bound to thread [" +
                Thread.currentThread().getName() + "]");
       }
       return value;
    }
    

    跟一下doGetResource

    @Nullable
    private static Object doGetResource(Object actualKey) {
       Map<Object, Object> map = resources.get();
       if (map == null) {
          return null;
       }
       Object value = map.get(actualKey);
       // Transparently remove ResourceHolder that was marked as void...
       if (value instanceof ResourceHolder && ((ResourceHolder) value).isVoid()) {
          map.remove(actualKey);
          // Remove entire ThreadLocal if empty...
          if (map.isEmpty()) {
             resources.remove();
          }
          value = null;
       }
       return value;
    }
    

    看一下resources

    public abstract class TransactionSynchronizationManager {
    
       private static final Log logger = LogFactory.getLog(TransactionSynchronizationManager.class);
    
       private static final ThreadLocal<Map<Object, Object>> resources =
             new NamedThreadLocal<>("Transactional resources");
             
    }
    

    Mybatis-Spring 2.0.6版本

    1. 通过@MapperScan导入了MapperScannerRegistrar类
    2. MapperScannerRegistrar类实现了ImportBeanDefinitionRegistrar接口,所以Spring在启动时会调用MapperScannerRegistrar类中的registerBeanDefinitions方法
    3. 在registerBeanDefinitions方法中注册一个MapperScannerConfigurer类型的BeanDefinition
    4. 而MapperScannerConfigurer实现了BeanDefinitionRegistryPostProcessor接口,所以Spring在启动过程中时会调用它的postProcessBeanDefinitionRegistry()方法
    5. 在postProcessBeanDefinitionRegistry方法中会生成一个ClassPathMapperScanner对象,然后进行扫描
    6. 后续的逻辑和1.3.2版本一样。

    带来的好处是,可以不使用@MapperScan注解,而可以直接定义一个Bean,比如:

    @Bean
    public MapperScannerConfigurer mapperScannerConfigurer() {
    	MapperScannerConfigurer mapperScannerConfigurer = new MapperScannerConfigurer();
    	mapperScannerConfigurer.setBasePackage("com.luban");
    	return mapperScannerConfigurer;
    }
    

    源码流程

    MapperScannerRegistrar类实现了ImportBeanDefinitionRegistrar接口,所以Spring在启动时会调用MapperScannerRegistrar类中的registerBeanDefinitions方法

    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
      AnnotationAttributes mapperScanAttrs = AnnotationAttributes
          .fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
      if (mapperScanAttrs != null) {
        registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,
            generateBaseBeanName(importingClassMetadata, 0));
      }
    }
    
    void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,
        BeanDefinitionRegistry registry, String beanName) {
    
    //注册一个MapperScannerConfigurer类型的BeanDefinition
      BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
      builder.addPropertyValue("processPropertyPlaceHolders", true);
    
      Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");
      if (!Annotation.class.equals(annotationClass)) {
        builder.addPropertyValue("annotationClass", annotationClass);
      }
    
      Class<?> markerInterface = annoAttrs.getClass("markerInterface");
      if (!Class.class.equals(markerInterface)) {
        builder.addPropertyValue("markerInterface", markerInterface);
      }
    
      Class<? extends BeanNameGenerator> generatorClass = annoAttrs.getClass("nameGenerator");
      if (!BeanNameGenerator.class.equals(generatorClass)) {
        builder.addPropertyValue("nameGenerator", BeanUtils.instantiateClass(generatorClass));
      }
    
      Class<? extends MapperFactoryBean> mapperFactoryBeanClass = annoAttrs.getClass("factoryBean");
      if (!MapperFactoryBean.class.equals(mapperFactoryBeanClass)) {
        builder.addPropertyValue("mapperFactoryBeanClass", mapperFactoryBeanClass);
      }
    
      String sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef");
      if (StringUtils.hasText(sqlSessionTemplateRef)) {
        builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef"));
      }
    
      String sqlSessionFactoryRef = annoAttrs.getString("sqlSessionFactoryRef");
      if (StringUtils.hasText(sqlSessionFactoryRef)) {
        builder.addPropertyValue("sqlSessionFactoryBeanName", annoAttrs.getString("sqlSessionFactoryRef"));
      }
    
      List<String> basePackages = new ArrayList<>();
      basePackages.addAll(
          Arrays.stream(annoAttrs.getStringArray("value")).filter(StringUtils::hasText).collect(Collectors.toList()));
    
      basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText)
          .collect(Collectors.toList()));
    
      basePackages.addAll(Arrays.stream(annoAttrs.getClassArray("basePackageClasses")).map(ClassUtils::getPackageName)
          .collect(Collectors.toList()));
    
      if (basePackages.isEmpty()) {
        basePackages.add(getDefaultBasePackage(annoMeta));
      }
    
      String lazyInitialization = annoAttrs.getString("lazyInitialization");
      if (StringUtils.hasText(lazyInitialization)) {
        builder.addPropertyValue("lazyInitialization", lazyInitialization);
      }
    
      String defaultScope = annoAttrs.getString("defaultScope");
      if (!AbstractBeanDefinition.SCOPE_DEFAULT.equals(defaultScope)) {
        builder.addPropertyValue("defaultScope", defaultScope);
      }
    
      builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));
    
      registry.registerBeanDefinition(beanName, builder.getBeanDefinition());
    
    }
    

    MapperScannerConfigurer功能就很丰富了,看下源码

    public class MapperScannerConfigurer
        implements BeanDefinitionRegistryPostProcessor, InitializingBean, ApplicationContextAware, BeanNameAware {
    
      private String basePackage;
    
      private boolean addToConfig = true;
    
      private String lazyInitialization;
    
      private SqlSessionFactory sqlSessionFactory;
    
      private SqlSessionTemplate sqlSessionTemplate;
    
      private String sqlSessionFactoryBeanName;
    
      private String sqlSessionTemplateBeanName;
    
      private Class<? extends Annotation> annotationClass;
    
      private Class<?> markerInterface;
    
      private Class<? extends MapperFactoryBean> mapperFactoryBeanClass;
    
      private ApplicationContext applicationContext;
    
      private String beanName;
    
      private boolean processPropertyPlaceHolders;
    
      private BeanNameGenerator nameGenerator;
    
      private String defaultScope;
    
      
      public void setMarkerInterface(Class<?> superClass) {
        this.markerInterface = superClass;
      }
    
    
      @Deprecated
      public void setSqlSessionTemplate(SqlSessionTemplate sqlSessionTemplate) {
        this.sqlSessionTemplate = sqlSessionTemplate;
      }
    
    
      public void setSqlSessionTemplateBeanName(String sqlSessionTemplateName) {
        this.sqlSessionTemplateBeanName = sqlSessionTemplateName;
      }
    
    
      @Deprecated
      public void setSqlSessionFactory(SqlSessionFactory sqlSessionFactory) {
        this.sqlSessionFactory = sqlSessionFactory;
      }
    
    
      public void setSqlSessionFactoryBeanName(String sqlSessionFactoryName) {
        this.sqlSessionFactoryBeanName = sqlSessionFactoryName;
      }
    
    
      public void setProcessPropertyPlaceHolders(boolean processPropertyPlaceHolders) {
        this.processPropertyPlaceHolders = processPropertyPlaceHolders;
      }
    
    
      public void setMapperFactoryBeanClass(Class<? extends MapperFactoryBean> mapperFactoryBeanClass) {
        this.mapperFactoryBeanClass = mapperFactoryBeanClass;
      }
    
    
      @Override
      public void setApplicationContext(ApplicationContext applicationContext) {
        this.applicationContext = applicationContext;
      }
    
    
      @Override
      public void setBeanName(String name) {
        this.beanName = name;
      }
    
    
      public BeanNameGenerator getNameGenerator() {
        return nameGenerator;
      }
    
    
      public void setNameGenerator(BeanNameGenerator nameGenerator) {
        this.nameGenerator = nameGenerator;
      }
    
    
      public void setDefaultScope(String defaultScope) {
        this.defaultScope = defaultScope;
      }
    
    
      @Override
      public void afterPropertiesSet() throws Exception {
        notNull(this.basePackage, "Property 'basePackage' is required");
      }
    
    
      @Override
      public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) {
        // left intentionally blank
      }
    
    	//关键代码
      @Override
      public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) {
        if (this.processPropertyPlaceHolders) {
          processPropertyPlaceHolders();
        }
    	//添加ClassPathMapperScanner,进行扫描
        ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
        scanner.setAddToConfig(this.addToConfig);
        scanner.setAnnotationClass(this.annotationClass);
        scanner.setMarkerInterface(this.markerInterface);
        scanner.setSqlSessionFactory(this.sqlSessionFactory);
        scanner.setSqlSessionTemplate(this.sqlSessionTemplate);
        scanner.setSqlSessionFactoryBeanName(this.sqlSessionFactoryBeanName);
        scanner.setSqlSessionTemplateBeanName(this.sqlSessionTemplateBeanName);
        scanner.setResourceLoader(this.applicationContext);
        scanner.setBeanNameGenerator(this.nameGenerator);
        scanner.setMapperFactoryBeanClass(this.mapperFactoryBeanClass);
        if (StringUtils.hasText(lazyInitialization)) {
          scanner.setLazyInitialization(Boolean.valueOf(lazyInitialization));
        }
        if (StringUtils.hasText(defaultScope)) {
          scanner.setDefaultScope(defaultScope);
        }
        scanner.registerFilters();
        scanner.scan(
            StringUtils.tokenizeToStringArray(this.basePackage, ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS));
      }
    
     
      private void processPropertyPlaceHolders() {
        Map<String, PropertyResourceConfigurer> prcs = applicationContext.getBeansOfType(PropertyResourceConfigurer.class,
            false, false);
    
        if (!prcs.isEmpty() && applicationContext instanceof ConfigurableApplicationContext) {
          BeanDefinition mapperScannerBean = ((ConfigurableApplicationContext) applicationContext).getBeanFactory()
              .getBeanDefinition(beanName);
    
          // PropertyResourceConfigurer does not expose any methods to explicitly perform
          // property placeholder substitution. Instead, create a BeanFactory that just
          // contains this mapper scanner and post process the factory.
          DefaultListableBeanFactory factory = new DefaultListableBeanFactory();
          factory.registerBeanDefinition(beanName, mapperScannerBean);
    
          for (PropertyResourceConfigurer prc : prcs.values()) {
            prc.postProcessBeanFactory(factory);
          }
    
          PropertyValues values = mapperScannerBean.getPropertyValues();
    
          this.basePackage = getPropertyValue("basePackage", values);
          this.sqlSessionFactoryBeanName = getPropertyValue("sqlSessionFactoryBeanName", values);
          this.sqlSessionTemplateBeanName = getPropertyValue("sqlSessionTemplateBeanName", values);
          this.lazyInitialization = getPropertyValue("lazyInitialization", values);
          this.defaultScope = getPropertyValue("defaultScope", values);
        }
        this.basePackage = Optional.ofNullable(this.basePackage).map(getEnvironment()::resolvePlaceholders).orElse(null);
        this.sqlSessionFactoryBeanName = Optional.ofNullable(this.sqlSessionFactoryBeanName)
            .map(getEnvironment()::resolvePlaceholders).orElse(null);
        this.sqlSessionTemplateBeanName = Optional.ofNullable(this.sqlSessionTemplateBeanName)
            .map(getEnvironment()::resolvePlaceholders).orElse(null);
        this.lazyInitialization = Optional.ofNullable(this.lazyInitialization).map(getEnvironment()::resolvePlaceholders)
            .orElse(null);
        this.defaultScope = Optional.ofNullable(this.defaultScope).map(getEnvironment()::resolvePlaceholders).orElse(null);
      }
    
      private Environment getEnvironment() {
        return this.applicationContext.getEnvironment();
      }
    
      private String getPropertyValue(String propertyName, PropertyValues values) {
        PropertyValue property = values.getPropertyValue(propertyName);
    
        if (property == null) {
          return null;
        }
    
        Object value = property.getValue();
    
        if (value == null) {
          return null;
        } else if (value instanceof String) {
          return value.toString();
        } else if (value instanceof TypedStringValue) {
          return ((TypedStringValue) value).getValue();
        } else {
          return null;
        }
      }
    
    }
    

    整合后一级缓存失效问题

    先看下图: Spring整合Mybatis之后SQL执行流程: https://www.processon.com/view/link/6152cc385653bb6791db436c

    Mybatis中的一级缓存是基于SqlSession来实现的,所以在执行同一个sql时,如果使用的是同一个SqlSession对象,那么就能利用到一级缓存,提高sql的执行效率。

    但是在Spring整合Mybatis后,如果没有执行某个方法时,该方法上没有加@Transactional注解,也就是没有开启Spring事务,那么后面在执行具体sql时,每执行一个sql时都会新生成一个SqlSession对象来执行该sql,这就是我们说的一级缓存失效(也就是没有使用同一个SqlSession对象),而如果开启了Spring事务,那么该Spring事务中的多个sql,在执行时会使用同一个SqlSession对象,从而一级缓存生效,具体的底层执行流程在上图。

    个人理解:实际上Spring整合Mybatis后一级缓存失效并不是问题,是正常的实现,因为,一个方法如果没有开启Spring事务,那么在执行sql时候,那就是每个sql单独一个事务来执行,也就是单独一个SqlSession对象来执行该sql,如果开启了Spring事务,那就是多个sql属于同一个事务,那自然就应该用一个SqlSession来执行这多个sql。所以,在没有开启Spring事务的时候,SqlSession的一级缓存并不是失效了,而是存在的生命周期太短了(执行完一个sql后就被销毁了,下一个sql执行时又是一个新的SqlSession了)。

  • 相关阅读:
    OneDay!——冲刺日志6(05-03)
    OneDay!——冲刺日志5(04-30)
    OneDay!——冲刺日志4(04-29)
    OneDay!——冲刺日志3(04-28)
    OneDay!——冲刺日志2(04-27)
    OneDay!——冲刺日志集合1
    OneDay!——冲刺日志1(04-26)
    团队作业第五次——站立式会议+alpha冲刺
    Hail_Hydra2—冲刺日志(7)
    Hail_Hydra2—冲刺日志(6)
  • 原文地址:https://www.cnblogs.com/lusaisai/p/15983050.html
Copyright © 2020-2023  润新知