<?xml version="1.0" encoding="UTF-8"?>
<!--

    Copyright ${license.git.copyrightYears} the original author or authors.

    Licensed under the Apache License, Version 2.0 (the "License");
    you may not use this file except in compliance with the License.
    You may obtain a copy of the License at

       http://www.apache.org/licenses/LICENSE-2.0

    Unless required by applicable law or agreed to in writing, software
    distributed under the License is distributed on an "AS IS" BASIS,
    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    See the License for the specific language governing permissions and
    limitations under the License.

-->
<document xmlns="http://maven.apache.org/XDOC/2.0"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/XDOC/2.0 http://maven.apache.org/xsd/xdoc-2.0.xsd">

  <properties>
    <title>MyBatis-Spring | 注入映射器</title>
    <author email="hpresnall@gmail.com">Hunter Presnall</author>
    <author email="eduardo.macarron@gmail.com">Eduardo Macarron</author>
    <author email="nanlei1987@gmail.com">Nan Lei</author>
  </properties>

  <body>
    <section name="注入映射器">
      <p>
        与其在数据访问对象（DAO）中手工编写使用 <code>SqlSessionDaoSupport</code> 或 <code>SqlSessionTemplate</code> 的代码，还不如让 Mybatis-Spring 为你创建一个线程安全的映射器，这样你就可以直接注入到其它的 bean 中了：
      </p>

      <source><![CDATA[<bean id="fooService" class="org.mybatis.spring.sample.service.FooServiceImpl">
  <constructor-arg ref="userMapper" />
</bean>]]></source>

      <p>
        注入完毕后，映射器就可以在你的应用逻辑代码中使用了：
      </p>
      <source><![CDATA[public class FooServiceImpl implements FooService {

  private final UserMapper userMapper;

  public FooServiceImpl(UserMapper userMapper) {
    this.userMapper = userMapper;
  }

  public User doSomeBusinessStuff(String userId) {
    return this.userMapper.getUser(userId);
  }
}]]></source>
      <p>
        注意代码中并没有任何的对 <code>SqlSession</code> 或 MyBatis 的引用。你也不需要担心创建、打开、关闭 session，MyBatis-Spring 将为你打理好一切。
      </p>

      <subsection name="注册映射器" id="register">
        <p>
          注册映射器的方法根据你的配置方法，即经典的 XML 配置或新的 3.0 以上版本的 Java 配置（也就是常说的 @Configuration），而有所不同。
        </p>

        <h4>XML 配置</h4>

        <p>
          在你的 XML 中加入 <code>MapperFactoryBean</code> 以便将映射器注册到 Spring 中。就像下面一样：
        </p>
        <source><![CDATA[<bean id="userMapper" class="org.mybatis.spring.mapper.MapperFactoryBean">
  <property name="mapperInterface" value="org.mybatis.spring.sample.mapper.UserMapper" />
  <property name="sqlSessionFactory" ref="sqlSessionFactory" />
</bean>]]></source>

        <p>
          如果映射器接口 UserMapper 在相同的类路径下有对应的 MyBatis XML 映射器配置文件，将会被 <code>MapperFactoryBean</code> 自动解析。不需要在 MyBatis 配置文件中显式配置映射器，除非映射器配置文件与接口类不在同一个类路径下。参考 <code>SqlSessionFactoryBean</code> 的 <code><a href="factorybean.html">configLocation</a></code> 属性以获取更多信息。
        </p>

        <p>
          注意 <code>MapperFactoryBean</code> 需要配置一个 <code>SqlSessionFactory</code> 或 <code>SqlSessionTemplate</code>。它们可以分别通过 <code>sqlSessionFactory</code> 和 <code>sqlSessionTemplate</code> 属性来进行设置。如果两者都被设置，<code>SqlSessionFactory</code> 将被忽略。由于 <code>SqlSessionTemplate</code> 已经设置了一个 session 工厂，<code>MapperFactoryBean</code> 将使用那个工厂。
        </p>

        <h4>Java 配置</h4>

        <source><![CDATA[
@Bean
public MapperFactoryBean<UserMapper> userMapper() throws Exception {
  MapperFactoryBean<UserMapper> factoryBean = new MapperFactoryBean<>(UserMapper.class);
  factoryBean.setSqlSessionFactory(sqlSessionFactory());
  return factoryBean;
}]]></source>

      </subsection>

      <subsection name="发现映射器" id="scan">
        <p>
          不需要一个个地注册你的所有映射器。你可以让 MyBatis-Spring 对类路径进行扫描来发现它们。
        </p>

        <p>
          有几种办法来发现映射器：
        </p>
        <ul>
          <li>使用 <code>&lt;mybatis:scan/&gt;</code> 元素</li>
          <li>使用 <code>@MapperScan</code> 注解</li>
          <li>在经典 Spring XML 配置文件中注册一个 <code>MapperScannerConfigurer</code></li>
        </ul>

        <p><code>&lt;mybatis:scan/&gt;</code> 和 <code>@MapperScan</code> 都在 MyBatis-Spring 1.2.0 中被引入。<code>@MapperScan</code> 需要你使用 Spring 3.1+。</p>

        <p>
          Since 2.0.2, mapper scanning feature support a option (<code>lazy-initialization</code>)
          that control lazy initialization enabled/disabled of mapper bean.
          The motivation for adding this option is supporting a lazy initialization control feature supported by Spring Boot 2.2.
          The default of this option is <code>false</code> (= not use lazy initialization).
          If developer want to use lazy initialization for mapper bean, it should be set to the <code>true</code> expressly.
        </p>
        <p>
          <span class="label important">IMPORTANT</span> If use the lazy initialization feature,
          the developer need to understand following limitations. If any of following conditions are matches,
          usually the lazy initialization feature cannot use on your application.
        </p>
        <ul>
          <li>When refers to the statement of <b>other mapper</b> using <code><![CDATA[<association>]]></code>(<code>@One</code>) and <code><![CDATA[<collection>]]></code>(<code>@Many</code>)</li>
          <li>When includes to the fragment of <b>other mapper</b> using <code><![CDATA[<include>]]></code></li>
          <li>When refers to the cache of <b>other mapper</b> using <code><![CDATA[<cache-ref>]]></code>(<code>@CacheNamespaceRef</code>)</li>
          <li>When refers to the result mapping of <b>other mapper</b> using <code><![CDATA[<select resultMap="...">]]></code>(<code>@ResultMap</code>)</li>
        </ul>

        <p>
          <span class="label important">NOTE</span> However, It become possible to use it by simultaneously initializing dependent beans using <code>@DependsOn</code>(Spring's feature) as follow:
        </p>

        <source><![CDATA[@DependsOn("vendorMapper")
public interface GoodsMapper {
  // ...
}]]></source>

        <h4>&lt;mybatis:scan/&gt;</h4>

        <p>
          <code>&lt;mybatis:scan/&gt;</code> 元素会发现映射器，它发现映射器的方法与 Spring 内建的 <code>&lt;context:component-scan/&gt;</code> 发现 bean 的方法非常类似。
        </p>

        <p>下面是一个 XML 配置样例：</p>

        <source><![CDATA[<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xmlns:mybatis="http://mybatis.org/schema/mybatis-spring"
  xsi:schemaLocation="
  http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
  http://mybatis.org/schema/mybatis-spring http://mybatis.org/schema/mybatis-spring.xsd">

  <mybatis:scan base-package="org.mybatis.spring.sample.mapper" />

  <!-- ... -->

</beans>]]></source>

        <p>
          <code>base-package</code> 属性允许你设置映射器接口文件的基础包。通过使用逗号或分号分隔，你可以设置多个包。并且会在你所指定的包中递归搜索映射器。
        </p>

        <p>
          注意，不需要为 <code>&lt;mybatis:scan/&gt;</code> 指定 <code>SqlSessionFactory</code> 或 <code>SqlSessionTemplate</code>，这是因为它将使用能够被自动注入的 <code>MapperFactoryBean</code>。但如果你正在使用多个数据源（<code>DataSource</code>），自动注入可能不适合你。在这种情况下，你可以使用 <code>factory-ref</code> 或 <code>template-ref</code> 属性指定你想使用的 bean 名称。
        </p>

        <p>
          <code>&lt;mybatis:scan/&gt;</code> 支持基于标记接口或注解的过滤操作。在 <code>annotation</code> 属性中，可以指定映射器应该具有的特定注解。而在 <code>marker-interface</code> 属性中，可以指定映射器应该继承的父接口。当这两个属性都被设置的时候，被发现的映射器会满足这两个条件。默认情况下，这两个属性为空，因此在基础包中的所有接口都会被作为映射器被发现。
        </p>

        <p>
          被发现的映射器会按照 Spring 对自动发现组件的默认命名策略进行命名（参考 <a href="https://docs.spring.io/spring/docs/current/spring-framework-reference/core.html#beans-scanning-name-generator">the Spring reference document(Core Technologies -Naming autodetected components-)</a>）。也就是说，如果没有使用注解显式指定名称，将会使用映射器的首字母小写非全限定类名作为名称。但如果发现映射器具有 @Component 或 JSR-330 标准中 @Named 注解，会使用注解中的名称作为名称。提醒一下，你可以设置 <code>annotation</code> 属性为你自定义的注解，然后在你的注解上设置 <code>org.springframework.stereotype.Component</code> 或 <code>javax.inject.Named</code>（需要使用 Java SE 6 以上）注解，这样你的注解既可以作为标记，也可以作为一个名字提供器来使用了。
        </p>

        <p>
          <span class="label important">提示</span> <code>&lt;context:component-scan/&gt;</code> 无法发现并注册映射器。映射器的本质是接口，为了将它们注册到 Spring 中，发现器必须知道如何为找到的每个接口创建一个 <code>MapperFactoryBean</code>。
        </p>

        <h4>@MapperScan</h4>

        <p>
          当你正在使用 Spring 的基于 Java 的配置时（也就是 @Configuration），相比于使用 <code>&lt;mybatis:scan/&gt;</code>，你会更喜欢用 <code>@MapperScan</code>。
        </p>

        <p><code>@MapperScan</code> 注解的使用方法如下：</p>

        <source><![CDATA[@Configuration
@MapperScan("org.mybatis.spring.sample.mapper")
public class AppConfig {
  // ...
}
]]></source>

        <p>
          这个注解具有与之前见过的 <code>&lt;mybatis:scan/&gt;</code> 元素一样的工作方式。它也可以通过 <code>markerInterface</code> 和 <code>annotationClass</code> 属性设置标记接口或注解类。通过配置 <code>sqlSessionFactory</code> 和 <code>sqlSessionTemplate</code> 属性，你还能指定一个 <code>SqlSessionFactory</code> 或 <code>SqlSessionTemplate</code>。
        </p>

        <h4>MapperScannerConfigurer</h4>

        <p>
          <code>MapperScannerConfigurer</code> 是一个 <code>BeanDefinitionRegistryPostProcessor</code>，这样就可以作为一个 bean，包含在经典的 XML 应用上下文中。为了配置 <code>MapperScannerConfigurer</code>，使用下面的 Spring 配置：
        </p>
        <source><![CDATA[<bean class="org.mybatis.spring.mapper.MapperScannerConfigurer">
  <property name="basePackage" value="org.mybatis.spring.sample.mapper" />
</bean>]]></source>

        <p>
          如果你需要指定 <code>sqlSessionFactory</code> 或 <code>sqlSessionTemplate</code>，那你应该要指定的是 <strong>bean 名</strong>而不是 bean 的引用，因此要使用 <code>value</code> 属性而不是通常的 <code>ref</code> 属性：
        </p>
        <source><![CDATA[<property name="sqlSessionFactoryBeanName" value="sqlSessionFactory" />]]></source>

        <p>
          <span class="label important">提示</span> 在 MyBatis-Spring 1.0.2 之前，<code>sqlSessionFactoryBean</code> 和
          <code>sqlSessionTemplateBean</code> 属性是唯一可用的属性。但由于 <code>MapperScannerConfigurer</code> 在启动过程中比 <code>PropertyPlaceholderConfigurer</code> 运行得更早，经常会产生错误。基于这个原因，上述的属性已被废弃，现在建议使用 <code>sqlSessionFactoryBeanName</code> 和 <code>sqlSessionTemplateBeanName</code> 属性。
        </p>
      </subsection>
    </section>
  </body>
</document>
