首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

MyBatis-Spring 整合:Mapper 接口注入機制與掃描配置詳解

     編輯:Java 相關知識

MyBatis-Spring 整合核心:Mapper 接口自動代理與依賴注入完全指南

MyBatis-Spring 讓 Mapper 接口無需寫實現類,直接注入 Service 即可使用。本文深度解析 MapperFactoryBean 代理機制、MapperScannerConfigurer 掃描配置、多數據源場景下的精準注入策略,助你構建乾淨、可維護的持久層架構。

一、核心組件架構


Mapper 接口 (UserMapper)
      ↓
MapperFactoryBean (FactoryBean)
      ↓ 生成 JDK 動態代理
SqlSessionTemplate (線程安全 SqlSession 包裝)
      ↓ 委派
SqlSessionFactory (會話工廠)
      ↓
DataSource / TransactionManager

二、MapperFactoryBean:單個 Mapper 手動註冊

Mapper 接口定義


public interface UserMapper {
    User getUser(String userId);
    void insertUser(User user);
    List selectUsers(@Param("dto") UserQueryDTO dto);
}

XML 映射文件 (UserMapper.xml)



    
    
        INSERT INTO users(...) VALUES(...)
    
    

Spring XML 配置




    
    
    




    
    




    

關鍵約束

  • Mapper 接口全限定名 = XML namespace,必須完全一致
  • Mapper 必須是接口,不能是具體類(代理生成接口實現)
  • 方法簽名與 XML id/parameterType/resultType 對應

三、MapperScannerConfigurer:批量自動掃描(推薦)

基礎配置



    
    
    

核心屬性詳解

屬性類型說明默認值
basePackageString掃描基礎包路徑,遞歸掃描子包無(必填)
sqlSessionFactoryString/Object指定 SqlSessionFactory Bean 名稱/引用自動檢測單個
sqlSessionTemplateString/Object指定 SqlSessionTemplate Bean 名稱/引用自動檢測單個
annotationClassClass標記註解:僅掃描帶此註解的接口null(掃描所有)
markerInterfaceClass標記接口:僅掃描繼承此接口的接口null(掃描所有)
nameGeneratorBeanNameGeneratorBean 命名策略首字母小寫類名

進階用法:標記註解/接口過濾



@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyBatisMapper {}

// Mapper 接口加註解
@MyBatisMapper
public interface UserMapper { ... }

// 掃描器配置:僅掃描帶 @MyBatisMapper 的接口

    
    

多數據源場景:精準綁定 SqlSessionFactory




    
    




    
    




    
    




    
    

原則:每個數據源對應一組 Mapper 包、一個 SqlSessionFactory、一個 MapperScannerConfigurer。

四、Spring Boot 環境下的簡化配置

1. 註解掃描(最常用)


@SpringBootApplication
@MapperScan("com.example.mapper")  // 或多包 @MapperScan({"pkg1", "pkg2"})
public class Application { ... }

2. Mapper 接口標記


@Mapper  // 單個接口標記,無需 @MapperScan
public interface UserMapper { ... }

3. application.yml 配置


mybatis:
  mapper-locations: classpath:mapper/**/*.xml
  type-aliases-package: com.example.model
  configuration:
    map-underscore-to-camel-case: true
    cache-enabled: false
    # 多數據源需手動配置 SqlSessionFactory Bean

五、代理機制源碼級解析

MapperFactoryBean.getObject()


public class MapperFactoryBean implements FactoryBean, InitializingBean {
    private Class mapperInterface;
    private SqlSessionFactory sqlSessionFactory;
    private SqlSessionTemplate sqlSessionTemplate;

    @Override
    public T getObject() throws Exception {
        return getSqlSessionTemplate().getMapper(mapperInterface);
    }

    // SqlSessionTemplate.getMapper()
    public  T getMapper(Class type) {
        return getSqlSessionFactory().getConfiguration().getMapper(type, this);
    }

    // Configuration.getMapper() → MapperRegistry.getMapper()
    public  T getMapper(Class type, SqlSession sqlSession) {
        MapperProxyFactory mapperProxyFactory = 
            (MapperProxyFactory) knownMappers.get(type);
        if (mapperProxyFactory == null) {
            throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
        }
        return mapperProxyFactory.newInstance(sqlSession);  // JDK 動態代理
    }
}

MapperProxy 核心調用邏輯


public class MapperProxy implements InvocationHandler {
    private SqlSession sqlSession;
    private Class mapperInterface;
    private Map methodCache;

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // Object 方法直接調用
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        }
        // default 方法(JDK 8+ 接口默認方法)
        if (method.isDefault()) {
            return invokeDefaultMethod(proxy, method, args);
        }
        // 核心:委派給 MapperMethod 執行
        return cachedMapperMethod(method).execute(sqlSession, args);
    }
}

六、常見問題與避坑

問題原因解決
BindingException: Type not known to MapperRegistryMapper 接口未被掃描/註冊、包路徑錯誤檢查 @MapperScan basePackage、XML namespace、接口是否 public
多數據源 Mapper 注入錯誤MapperScannerConfigurer 自動綁定到錯誤的 SqlSessionFactory顯式指定 sqlSessionFactory 屬性、分包掃描
事務不生效SqlSessionTemplate 未使用 Spring 管理的事務管理器確保 SqlSessionFactoryBean 注入 dataSource、配置 DataSourceTransactionManager
Mapper 方法重複定義同名方法在不同 Mapper 接口、XML id 衝突namespace 隔離、方法名唯一化
泛型參數類型擦除導致映射錯誤List 等泛型在運行時擦除使用 @Param 或 ResultType 指定、或自定義 TypeHandler

七、最佳實踐總結

  1. 統一使用 @MapperScan + @Mapper:Spring Boot 專案首選,零 XML 配置
  2. Mapper 接口放獨立模塊:mapper 包,便於多數據源分離掃描
  3. XML 與接口同名同目錄:UserMapper.java ↔ UserMapper.xml,Maven 資源複製配置
  4. 命名規範:Bean 名稱 = 首字母小寫接口名(userMapper),Service 注入字段同名
  5. 禁用二級緩存:cache-enabled: false,分佈式環境用 Redis
  6. 開啟駝峰映射:map-underscore-to-camel-case: true,減少 resultMap 手寫

MyBatis-Spring 的設計精髓在於:接口即契約,代理隱藏實現,Spring 管理生命週期。掌握 MapperFactoryBean 代理鏈、MapperScannerConfigurer 掃描策略、多數據源精準綁定,能讓持久層代碼保持極致簡潔,業務邏輯聚焦於 Service 層,實現真正的關注點分離。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved