齐齐哈尔租房网:MyBatis整合Spring原理剖析

admin 7个月前 (04-15) 科技 73 1

目录
  • MyBatis整合Spring原理剖析
  • MapperScan的隐秘
  • 简朴总结

假如不连系Spring框架,我们使用MyBatis时的一个典型使用方式如下:

public class UserDaoTest {

    private SqlSessionFactory sqlSessionFactory;

    @Before
    public void setUp() throws Exception{
        ClassPathResource resource = new ClassPathResource("mybatis-config.xml");
        InputStream inputStream = resource.getInputStream();
        sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
    }

    @Test
    public void selectUserTest(){
        String id = "{0003CCCA-AEA9-4A1E-A3CC-06D884BA3906}";
        SqlSession sqlSession = sqlSessionFactory.openSession();
        CbondissuerMapper cbondissuerMapper = sqlSession.getMapper(CbondissuerMapper.class);
        Cbondissuer cbondissuer = cbondissuerMapper.selectByPrimaryKey(id);
        System.out.println(cbondissuer);
        sqlSession.close();
    }

}

我们首先需要SqlSessionFactory,然后通过SqlSessionFactory的openSession方式获得SqlSession。通过SqlSession获得我们界说的接口的动态署理类(MapperProxy)。当我们整合Spring框架时,我们使用MyBatis的方式简朴的“难以想象”:

@Autowore
private CbondissuerMapper cbondissuerMapper;

异常Spring形式的使用方式,直接注入就可以了。那这个是怎么实现的呢?Spring是怎么把上面略显庞大的模板代码省略的呢?我的第一直觉是Spring在启动的时刻做了Mybatis的初始化事情,然后一次性获取了所有Mapper接口的动态署理实现类并将其放入Spring容器中举行治理。

下面来验证下自己的料想对不对。

MyBatis整合Spring原理剖析

下面以Spring Boot中的MyBatisAutoConfigration为列子做下简朴剖析:

  //SqlSessionFactoryBean的最终作用就是剖析MyBati设置文件,并最终天生Configration工具,然后天生DefaultSqlSessionFactory,并加入Spring的容器治理。可以看出SqlSessionFactoryBean的作用和SqlSessionFactoryBeanBuilder的作用很像。
  @Bean
  @ConditionalOnMissingBean
  public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
    SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
    factory.setDataSource(dataSource);
    factory.setVfs(SpringBootVFS.class);
    if (StringUtils.hasText(this.properties.getConfigLocation())) {
      factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
    }
    Configuration configuration = this.properties.getConfiguration();
    if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
      configuration = new Configuration();
    }
    if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {
      for (ConfigurationCustomizer customizer : this.configurationCustomizers) {
        customizer.customize(configuration);
      }
    }
    factory.setConfiguration(configuration);
    if (this.properties.getConfigurationProperties() != null) {
      factory.setConfigurationProperties(this.properties.getConfigurationProperties());
    }
    if (!ObjectUtils.isEmpty(this.interceptors)) {
      factory.setPlugins(this.interceptors);
    }
    if (this.databaseIdProvider != null) {
      factory.setDatabaseIdProvider(this.databaseIdProvider);
    }
    if (StringUtils.hasLength(this.properties.getTypeAliasesPackage())) {
      factory.setTypeAliasesPackage(this.properties.getTypeAliasesPackage());
    }
    if (StringUtils.hasLength(this.properties.getTypeHandlersPackage())) {
      factory.setTypeHandlersPackage(this.properties.getTypeHandlersPackage());
    }
    if (!ObjectUtils.isEmpty(this.properties.resolveMapperLocations())) {
      factory.setMapperLocations(this.properties.resolveMapperLocations());
    }

    return factory.getObject();
  }

  //建立SqlSessionTemplate这个Bean,这个类动态署理了DefaultSqlSession,以是最后照样挪用了DefaultSqlSession,下面会重点剖析下SqlSessionTemplate
  @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);
    }
  }

以下是SqlSessionTemplate的源代码,由于源码较多,只贴出重点部门:

public class SqlSessionTemplate implements SqlSession, DisposableBean {

  private final SqlSessionFactory sqlSessionFactory;

  private final ExecutorType executorType;
  //SqlSessionTemplate动态署理了DefaultSqlSession,所用对sqlSessionProxy的挪用都市经由署理工具
  private final SqlSession sqlSessionProxy;

  private final PersistenceExceptionTranslator 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;
    this.sqlSessionProxy = (SqlSession) newProxyInstance(
        SqlSessionFactory.class.getClassLoader(),
        new Class[] { SqlSession.class },
        new SqlSessionInterceptor());
  }
    
  ....
  //对sqlSessionProxy的CRUD挪用都市先挪用到这里
  private class SqlSessionInterceptor implements InvocationHandler {
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
      //这步相当于挪用sqlSessionFactory的openSesion方式获得SqlSession
      SqlSession sqlSession = getSqlSession(
          SqlSessionTemplate.this.sqlSessionFactory,
          SqlSessionTemplate.this.executorType,
          SqlSessionTemplate.this.exceptionTranslator);
      try {
        //挪用DefaultSqlSession的CRUD方式
        Object result = method.invoke(sqlSession, args);
        if (!isSqlSessionTransactional(sqlSession, SqlSessionTemplate.this.sqlSessionFactory)) {
          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) {
          //最后强制关闭SqlSession
          closeSqlSession(sqlSession, SqlSessionTemplate.this.sqlSessionFactory);
        }
      }
    }
  }
}

到此为止,整个Spring中获取SqlSessionFactory、获得SqlSession、执行CRUD、以及关闭SqlSession的流程都举行剖析了。另有一个有疑问的地方就是Mapper接口的实现类是在什么时刻动态天生的。

MapperScan的隐秘

我们知道MapperScan是用来扫描Mapper接口的,以是很自然的想到去MapperScan这个类内里一探事实。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
//隐秘在MapperScannerRegistrar内里
@Import(MapperScannerRegistrar.class)
public @interface MapperScan {

  String[] value() default {};
  //设置扫描的包
  String[] basePackages() default {};

  Class<?>[] basePackageClasses() default {};

  Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

  Class<? extends Annotation> annotationClass() default Annotation.class;

  Class<?> markerInterface() default Class.class;

  String sqlSessionTemplateRef() default "";
    
  String sqlSessionFactoryRef() default "";
  //指定自界说的MapperFactoryBean
  Class<? extends MapperFactoryBean> factoryBean() default MapperFactoryBean.class;

}

下面就看下MapperScannerRegistrar内里做了什么:

public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {

    AnnotationAttributes annoAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));
    //建立了一个ClassPathMapperScanner,并凭据@MapperScan内里的设置设置属性
    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,并凭据@MapperScan内里的设置设置属性,最先扫描。下面再看ClassPathMapperScanner。

private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {
    GenericBeanDefinition definition;
    //对扫描到的BeanDefinition做进一步处置
    for (BeanDefinitionHolder holder : beanDefinitions) {
      definition = (GenericBeanDefinition) holder.getBeanDefinition();

      if (logger.isDebugEnabled()) {
        logger.debug("Creating MapperFactoryBean with name '" + holder.getBeanName() 
          + "' and '" + definition.getBeanClassName() + "' mapperInterface");
      }
 definition.getConstructorArgumentValues().addGenericArgumentValue(definition.getBeanClassName()); // issue #59
      //这边是重点,将扫描到的Mapper接口的BeanClass设置为MapperFactoryBrean
      //MapperFactoryBrean是工厂Bean,用于天生MapperProxy;
      //Spring在自动注入Mapper的时刻会自动挪用这个工厂Bean的getObject方式,天生MapperProxy并放入Spring容器。
      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() + "'.");
        }
        definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
      }
    }
  }

到此Spring自动天生Mapper接口实现类的历程也剖析完了。

简朴总结

通过以上剖析,印证了一最先的料想:Spring在启动的时刻做了Mybatis的初始化事情,然后一次性获取了所有Mapper接口的动态署理实现类并将其放入Spring容器中举行治理。大致流程如下:

  • 设置SqlSessionFactoryBean,这个Bean的最终作用是建立DefaultSqlSessionFactory,并将DefaultSqlSessionFactory加入Spring容器治理;
  • 建立SqlSessionTemplate,这个工具署理了MyBatis中的DefaultSqlSession,通过SqlSessionTemplate挪用任何CRUD方式都市履历openSession、挪用DefaultSqlSession的CRUD方式和关闭Session的历程;

Mapper接口天生的流程大致如下:

  • @MapperScan注解扫描到所有的Mapper接口天生响应的BeanDefinition;
  • ClassPathMapperScanner的处置方式对这些BeanDefinition做进一步设置,其中最终要的一步就是将这些BeanDefinition的Beanclass属性设置为MapperFactoryBean(这是一个工厂Bean,专门用于天生Mapper的动态署理实现MapperProxy,一个Dao接口会对应一个MapperFactoryBean);
  • Spring在检测到需要自动注入Mapper时通过层层挪用,最终会挪用到MapperFactoryBean这个工厂Bean的getObject方式天生对应的MapperProxy,并将这个工具纳入Spring治理。

以上大致就是MyBatis整合进Spring的原理。我们发现其实质和传统的Mybatis使用是一样的,只不过是通过Spring做了一些自定设置等。

剖析的有点乱,先这样吧~

,

诚信阳光在线官网下载

诚信阳光在线官网(原诚信在线官网)现已开放阳光在线手机版、阳光在线电脑客户端下载。阳光在线娱乐游戏公平、公开、公正,用实力赢取信誉。

皇冠APP声明:该文看法仅代表作者自己,与本平台无关。转载请注明:齐齐哈尔租房网:MyBatis整合Spring原理剖析

网友评论

  • (*)

最新评论

  • 伸博最新官网 2020-04-15 01:47:32 回复

    Sunbet 申博Sunbet 申博www.sunbet88.us是Sunbet指定的Sunbet官网,Sunbet提供Sunbet(Sunbet)、Sunbet、申博代理合作等业务。手机没电,用电脑看

    1

文章归档

站点信息

  • 文章总数:526
  • 页面总数:0
  • 分类总数:8
  • 标签总数:961
  • 评论总数:152
  • 浏览总数:3753