spring(boot) mybatis整合

1.Mybatis解析配置部分

下图是解析配置类的UML图

 

SqlSessionFactoryBuilder

 1 public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
 2     try {
 3       XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
 4       return build(parser.parse());
 5     } catch (Exception e) {
 6       throw ExceptionFactory.wrapException("Error building SqlSession.", e);
 7     } finally {
 8       ErrorContext.instance().reset();
 9       try {
10         inputStream.close();
11       } catch (IOException e) {
12         // Intentionally ignore. Prefer previous error.
13       }
14     }
15   }
16     
17   public SqlSessionFactory build(Configuration config) {
18     return new DefaultSqlSessionFactory(config);
19   }

XMLConfigBuilder负责解析配置文件,XMLConfigBuilder会调用XPathParser 产生Configuration对象,XPathParser 利用XPath技术解析XMLConfiguration里面存储的都是config配置文件各种信息,我们也可以编程自己实例化Configuration来创建SqlSessionFactory

还有类MapperAnnotationBuilder是用来解析注解配置Mapper

 

XMLMapperBuilder是用来解析Xml配置Mapper

2.Spring整合Mybatis

SqlSessionFactoryBean

 

MapperFactoryBean

 

 

SqlSessionFactoryBean

<bean id="sqlSessionFactory" class="org.mybatis.spring.SqlSessionFactoryBean">
  <property name="dataSource" ref="dataSource" />
</bean>

Spring整合Mybatis最重要的是上面两个FactoryBeanFactoryBeanSpring一种特殊的BeanSpring会调用getObject方法产生Bean

 

现在简单看下SqlSessionFactoryBean 源码,看源码最后会调用到方法buildSqlSessionFactory,在下面源码我们看到了熟悉的几个类XMLConfigBuilderConfigurationSqlSessionFactoryBuilderSqlSessionFactoryXMLMapperBuilder(解析XmlMapper,注解的MapperMapperFactoryBean

 

 1 protected SqlSessionFactory buildSqlSessionFactory() throws IOException {
 2 
 3     Configuration configuration;
 4 
 5     XMLConfigBuilder xmlConfigBuilder = null;
 6     if (this.configuration != null) {
 7       configuration = this.configuration;
 8       if (configuration.getVariables() == null) {
 9         configuration.setVariables(this.configurationProperties);
10       } else if (this.configurationProperties != null) {
11         configuration.getVariables().putAll(this.configurationProperties);
12       }
13 //如果配置了configLocation,则直接调用XMLConfigBuilder得到Configuration
14     } else if (this.configLocation != null) {
15       xmlConfigBuilder = new XMLConfigBuilder(this.configLocation.getInputStream(), null, this.configurationProperties);
16       configuration = xmlConfigBuilder.getConfiguration();
17 } else {
18 //如果没有配置configLocation,先直接new Configuration 
19       if (LOGGER.isDebugEnabled()) {
20         LOGGER.debug("Property 'configuration' or 'configLocation' not specified, using default MyBatis Configuration");
21       }
22       configuration = new Configuration();
23       if (this.configurationProperties != null) {
24         configuration.setVariables(this.configurationProperties);
25       }
26     }
27 //配置前面得到的Configuration,Configuration的set设置都是FactoryBean的配置属性
28    //……. objectFactory
29 
30    //……objectWrapperFactory
31 
32    //………. vfs
33 
34    //………typeAliasesPackage
35 
36     //……….. typeAliases
37 
38    //……………plugins
39 
40     //…………typeHandlersPackage
41 
42   //…………typeHandlers
43 
44   //…………. databaseIdProvider
45 
46  //……………….cache
47 
48 //配置了configLocation的时候解析
49     if (xmlConfigBuilder != null) {
50       try {
51         xmlConfigBuilder.parse();
52 
53         if (LOGGER.isDebugEnabled()) {
54           LOGGER.debug("Parsed configuration file: '" + this.configLocation + "'");
55         }
56       } catch (Exception ex) {
57         throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
58       } finally {
59         ErrorContext.instance().reset();
60       }
61     }
62 //没有配置transactionFactory   默认指定SpringManagedTransactionFactory
63     if (this.transactionFactory == null) {
64       this.transactionFactory = new SpringManagedTransactionFactory();
65     }
66 
67 configuration.setEnvironment(new Environment(this.environment, this.transactionFactory, this.dataSource));
68 
69     if (!isEmpty(this.mapperLocations)) {
70       for (Resource mapperLocation : this.mapperLocations) {
71         if (mapperLocation == null) {
72           continue;
73         }
74 //解析XmlMapper,如果配置了mapperLocations
75         try {
76           XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
77               configuration, mapperLocation.toString(), configuration.getSqlFragments());
78           xmlMapperBuilder.parse();
79         } catch (Exception e) {
80           throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
81         } finally {
82           ErrorContext.instance().reset();
83         }
84 
85         if (LOGGER.isDebugEnabled()) {
86           LOGGER.debug("Parsed mapper file: '" + mapperLocation + "'");
87         }
88       }
89     } else {
90       if (LOGGER.isDebugEnabled()) {
91         LOGGER.debug("Property 'mapperLocations' was not specified or no matching resources found");
92       }
93     }
94 
95     return this.sqlSessionFactoryBuilder.build(configuration);
96   }

 

MapperFactoryBean

1 <bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
2   <property name="mapperInterface" value="org.mybatis.spring.sample.mapper.UserMapper" />
3   <property name="sqlSessionFactory" ref="sqlSessionFactory" />
4 </bean>

这个是处理注解的Mapper,简单的看下他的getObject方法,先看下这个类的签名,在看它的getObject方法

public class MapperFactoryBean<T> extends SqlSessionDaoSupport

public T getObject() throws Exception {

    return getSqlSession().getMapper(this.mapperInterface);

  }

 

这个是注解Mapper的正常用法。

MapperScannerConfigurer

<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
  <property name="basePackage" value="org.mybatis.spring.sample.mapper" />
</bean>

这个这个类会更具指定的basePackage来自动装配MapperFactoryBean,但如果指定了多个DataSource,则必须指定 sqlSessionFactoryBeanName属性

<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />

SqlSessionTemplate

SqlSessionDaoSupport

 

上面两个类是Spring对于Mybatis DAO支持的具体实现类

3SpringBoot整合Mybatis

mybatis-spring-boot-starter来进行pom更新来支持mybatis starter开发,分析Spring Boot知道,AutoConfigurationstarter的核心,boot加载的时候会读取classpath下的spring.factories,下面是MybatisAutoConfiguration配置

# Auto Configure

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\

org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

 

可以看到类org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

现在看下这个类的签名

@org.springframework.context.annotation.Configuration

@ConditionalOnClass({ SqlSessionFactory.class, SqlSessionFactoryBean.class })

@ConditionalOnBean(DataSource.class)

@EnableConfigurationProperties(MybatisProperties.class)

 

@AutoConfigureAfter(DataSourceAutoConfiguration.class)

publicclass MybatisAutoConfiguration {……..}

Configuration注解:说明这是spring的配置项,容器初始化的时候要进行解析处理

ConditionalOnClass注解:有类SqlSessionFactory(Mybatis)SqlSessionFactoryBean(Mybatis-Spring)的时候才进行解析处理

ConditionalOnBean:容器中有DataSource Bean的时候才进行解析处理

AutoConfigureAfter注解: DataSourceAutoConfiguration后解析

EnableConfigurationProperties注解和MybatisProperties:配置自己的Mybatis 属性,在Application.Properties中。

 

MybatisAutoConfiguration源码,几点

1.  SqlSessionFactory

@Bean
  @ConditionalOnMissingBean
  public SqlSessionFactory sqlSessionFactory(DataSource dataSource){…………….}
实例化SqlSessionFactory bean

2.  sqlSessionTemplate

@Bean
  @ConditionalOnMissingBean
  public SqlSessionTemplate sqlSessionTemplate(SqlSessionFactory sqlSessionFactory){………………}

3.  MapperScannerRegistrarNotFoundConfiguration

 1 @org.springframework.context.annotation.Configuration
 2   @Import({ AutoConfiguredMapperScannerRegistrar.class })
 3   @ConditionalOnMissingBean(MapperFactoryBean.class)
 4   public static class MapperScannerRegistrarNotFoundConfiguration {
 5 
 6     @PostConstruct
 7     public void afterPropertiesSet() {
 8       logger.debug("No {} found.", MapperFactoryBean.class.getName());
 9     }
10   }

如果没有使用MapperScann这个注解,会自动去搜索Mapper,看下源码,Import注解里面的类才是关键AutoConfiguredMapperScannerRegistrar,看他的registerBeanDefinitions方法源码,发现它会去类路径下去搜索Mapper注解的类生成MapperFactoryBean

 1 public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
 2 
 3       logger.debug("Searching for mappers annotated with @Mapper");
 4 
 5       ClassPathMapperScanner scanner = new ClassPathMapperScanner(registry);
 6 
 7       try {
 8         if (this.resourceLoader != null) {
 9           scanner.setResourceLoader(this.resourceLoader);
10         }
11 
12         List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
13         if (logger.isDebugEnabled()) {
14           for (String pkg : packages) {
15             logger.debug("Using auto-configuration base package '{}'", pkg);
16           }
17         }
18 
19         scanner.setAnnotationClass(Mapper.class);
20         scanner.registerFilters();
21         scanner.doScan(StringUtils.toStringArray(packages));
22       } catch (IllegalStateException ex) {
23         logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.", ex);
24       }
25 }

最后看下MapperScann注解

@Retention(RetentionPolicy.RUNTIME)

@Target(ElementType.TYPE)

@Documented

@Import(MapperScannerRegistrar.class)

public @interface MapperScan {}

 

看类MapperScannerRegistrarregisterBeanDefinitions方法,最后也是借用ClassPathMapperScanner类去扫描Mapper注解

 

public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {

    try {

      XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);

      return build(parser.parse());

    } catch (Exception e) {

      throw ExceptionFactory.wrapException("Error building SqlSession.", e);

    } finally {

      ErrorContext.instance().reset();

      try {

        inputStream.close();

      } catch (IOException e) {

        // Intentionally ignore. Prefer previous error.

      }

    }

  }

   

  public SqlSessionFactory build(Configuration config) {

    returnnew DefaultSqlSessionFactory(config);

  }

posted @ 2018-04-05 17:32  tom是cat  阅读(6113)  评论(0编辑  收藏  举报