Mybatis執行sql流程(二)之加載Mapper

Mybatis加載Mapper

注冊方式注冊時機特點
@MapperScanBean定義階段注冊接口定義批量注冊,推薦方式
@Mapper (接口注解)@MapperScan需每個接口單獨標注
XML 配置 <mapper>MyBatis 初始化時傳統方式,不依賴 Spring 容器
SqlSessionTemplate 直接獲取調用時編程式獲取,不自動注冊為 Bean

@MapperScan注冊mapper

// 使用:在啟動類上添加注解
@MapperScan("com.zy.**.mapper")
@Import(MapperScannerRegistrar.class) // 此處使用了@Import注解,用于導入其他配置
@Repeatable(MapperScans.class)
public @interface MapperScan {...;
}
/**
1.指示要導入的一個或多個組件,通常是@Configuration類。
2.提供與Spring XML中的<import/>元素等效的功能。
3.允許導入@Configuration類,ImportSelector和ImportBeanDefinitionRegistrar實現,以及常規組件類(從4.2開始,類似于AnnotationConfigApplicationContext.register)。
4.導入的@Configuration類中聲明的@Bean定義應該使用@Autowired注入來訪問。也可以自動連接聲明bean的配置類實例。
5.如果需要導入XML或其他非@Configuration bean定義資源,請改用@ImportResource注解。
*/public @interface Import {/*** {@link Configuration @Configuration}, {@link ImportSelector},* {@link ImportBeanDefinitionRegistrar}, or regular component classes to import.*/Class<?>[] value();}
// 實現了ImportBeanDefinitionRegistrar接口,此處涉及到Spring加載BeanDefinition的邏輯
public class MapperScannerRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {private ResourceLoader resourceLoader;// 重寫ImportBeanDefinitionRegistrar 接口中的registerBeanDefinitions 方法@Overridepublic void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {// 1. 獲取@MapperScan注解屬性AnnotationAttributes mapperScanAttrs = AnnotationAttributes.fromMap(importingClassMetadata.getAnnotationAttributes(MapperScan.class.getName()));// 2. 實際注冊邏輯if (mapperScanAttrs != null) {registerBeanDefinitions(importingClassMetadata, mapperScanAttrs, registry,generateBaseBeanName(importingClassMetadata, 0));}}/**annoMeta 被注解類的元數據annoAttrs @MapperScan 注解的屬性值registry : Spring bean 定義注冊器beanName :要注冊的 Bean 名稱*/void registerBeanDefinitions(AnnotationMetadata annoMeta, AnnotationAttributes annoAttrs,BeanDefinitionRegistry registry, String beanName) {
// 創建MapperScannerConfigurer 的通用Bean定義構建器。此處還只是構造builder。BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);// 啟用屬性占位符處理(如 ${jdbc.url})builder.addPropertyValue("processPropertyPlaceHolders", true);// 注解類過濾:如果設置了annotationClass(如@Mapper),則只掃描帶該注解的接口Class<? extends Annotation> annotationClass = annoAttrs.getClass("annotationClass");if (!Annotation.class.equals(annotationClass)) {builder.addPropertyValue("annotationClass", annotationClass);}
// 標記接口過濾:如果設置了 markerInterface, 則只掃描實現該接口的MapperClass<?> markerInterface = annoAttrs.getClass("markerInterface");if (!Class.class.equals(markerInterface)) {builder.addPropertyValue("markerInterface", markerInterface);}
// Bean 名稱生成器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);}
// SQL 會話模板引用:支持指定特定的 SqlSessionTemplate BeanString sqlSessionTemplateRef = annoAttrs.getString("sqlSessionTemplateRef");if (StringUtils.hasText(sqlSessionTemplateRef)) {builder.addPropertyValue("sqlSessionTemplateBeanName", annoAttrs.getString("sqlSessionTemplateRef"));}
// SQL 會話工廠引用String sqlSessionFactoryRef = annoAttrs.getString("sqlSessionFactoryRef");if (StringUtils.hasText(sqlSessionFactoryRef)) {builder.addPropertyValue("sqlSessionFactoryBeanName", annoAttrs.getString("sqlSessionFactoryRef"));}List<String> basePackages = new ArrayList<>();// 添加 value 屬性值(包路徑)basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("value")).filter(StringUtils::hasText).collect(Collectors.toList()));
// 添加 basePackages 屬性值basePackages.addAll(Arrays.stream(annoAttrs.getStringArray("basePackages")).filter(StringUtils::hasText).collect(Collectors.toList()));
// 添加basePackageClasses的包路徑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);}
// Bean 作用域String defaultScope = annoAttrs.getString("defaultScope");if (!AbstractBeanDefinition.SCOPE_DEFAULT.equals(defaultScope)) {builder.addPropertyValue("defaultScope", defaultScope);}
// 設置掃描包路徑(逗號分割)builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(basePackages));// for spring-nativebuilder.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
// 注冊Bean 定義。上述都是在配置builder, 此處才開始注冊bean definitionregistry.registerBeanDefinition(beanName, builder.getBeanDefinition());}
}

MapperScannerConfigurer 作為BeanDefinitionRegistryPostProcessor執行, 中重寫了 postProcessBeanDefinitionRegistry 方法,最終調用doScan(basePackages);掃描Mapper

public class ClassPathMapperScanner extends ClassPathBeanDefinitionScanner {@Overridepublic Set<BeanDefinitionHolder> doScan(String... basePackages) {// 1. 調用父類掃描方法獲取Bean定義Set<BeanDefinitionHolder> beanDefinitions = super.doScan(basePackages);if (beanDefinitions.isEmpty()) {logger.warn("No MyBatis mapper was found...");} else {// 2. 處理掃描到的Bean定義processBeanDefinitions(beanDefinitions);}return beanDefinitions;}
// 省略部分代碼private void processBeanDefinitions(Set<BeanDefinitionHolder> beanDefinitions) {for (BeanDefinitionHolder holder : beanDefinitions) {GenericBeanDefinition definition = (GenericBeanDefinition) holder.getBeanDefinition();// 3. 獲取Mapper接口名String mapperClassName = definition.getBeanClassName();// 4. BeanClass 轉換為 MapperFactoryBean// 關鍵步驟1:添加構造參數(原始Mapper接口)definition.getConstructorArgumentValues().addGenericArgumentValue(mapperClassName);try {// 關鍵步驟2:設置 MapperInterface 屬性(Spring Native 兼容)definition.getPropertyValues().add("mapperInterface", Resources.classForName(beanClassName));} catch (ClassNotFoundException ignore) {// ignore}// 關鍵步驟3:替換 Bean 類為 MapperFactoryBeandefinition.setBeanClass(this.mapperFactoryBeanClass);// 省略部分SqlSessionFactory 、sqlSessionTemplate相關內容。優先級:SqlSessionTemplate > SqlSessionFactory// 5. 設置自動裝配模式:當未顯示配置工廠/模板時,啟用按類型自動裝配if (!explicitFactoryUsed) {LOGGER.debug("Enabling autowire by type...");definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);}}}
}

轉換前Bean定義:

BeanDefinition {beanClass = com.example.UserMapper // 原始接口scope = singleton...
}

轉換后 Bean 定義

BeanDefinition {beanClass = MapperFactoryBean // 替換為工廠類constructorArgs = [com.example.UserMapper] // 原始接口作為構造參數properties = {sqlSessionFactory = ref("sqlSessionFactory") // 自動裝配addToConfig = true}...
}

實際效果:

// 用戶代碼
@Autowired
UserMapper userMapper; // 實際發生
MapperFactoryBean factoryBean = new MapperFactoryBean(UserMapper.class);
factoryBean.setSqlSessionFactory(sqlSessionFactory);
UserMapper proxy = factoryBean.getObject(); // 返回 MyBatis 代理
// MapperFactoryBean 的 getObject() 
@Overridepublic T getObject() throws Exception {return getSqlSession().getMapper(this.mapperInterface); // 此處就是獲取的代理類對象 }
// 從 @Autowired 到factoryBean.getObject(); 的過程:
@Autowired-> AutowiredAnnotationBeanPostProcessor.postProcessProperties()-> InjectionMetadata.inject()-> DefaultListableBeanFactory.doGetBean()-> AbstractBeanFactory.getObjectForBeanInstance(),發現 MapperFacoryBeanUserMapper 類型的工廠-> AbstractBeanFactory..getObjectFromFactoryBean()-> FactoryBeanRegistrySupport.doGetObjectFromFactoryBean()-> FactoryBean<?>.getObject()
@AutowiredSpring容器DefaultListableBeanFactoryFactoryBeanRegistrySupportMapperFactoryBeanMyBatis代理需要注入UserMappergetBean("userMapper")doGetBean("userMapper")getObjectForBeanInstance()doGetObjectFromFactoryBean()getObject()sqlSession.getMapper()MapperProxy返回代理返回代理返回代理注入代理對象@AutowiredSpring容器DefaultListableBeanFactoryFactoryBeanRegistrySupportMapperFactoryBeanMyBatis代理

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/96163.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/96163.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/96163.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

基于 JSP+Mysql實現MVC房屋租賃系統

基于 MVC 的房屋租賃系統的設計與實現摘 要&#xff1a;房屋租賃管理系統與網絡相結合&#xff0c;給用戶提供更加周到和人性化的服務。網站模式為 MVC 模式&#xff0c;基于 MySQL 數據庫,采用 JSP&#xff0c;Session 繪畫跟蹤、JavaScript 等技術,實現了普通用戶可以瀏覽、查…

第六天~提取Arxml中CAN采樣點信息Creat_ECU--Standard

?? ARXML探秘:解碼CAN采樣點的精準藝術 在汽車電子的交響樂中,CAN采樣點(Sample Point) 如同指揮家揮棒的關鍵時刻——它決定了何時"聆聽"總線上的信號。這個看似微小的百分比數值,卻是保障整車通信可靠性的核心密碼。本文將帶您深入ARXML中的采樣點配置世界…

Windows Git安裝配置

進入git官網Git - Downloading Package 點擊下載&#xff08;可復制鏈接到迅雷&#xff09; 雙擊運行exe安裝包 選擇安裝目錄 下一步 選擇 Git 默認編輯器&#xff0c;下一步設置初始化新項目(倉庫)的主干名字 讓Git決定&#xff08;Let Git decide&#xff09;使用默認的…

面試經驗分享-某電影廠

java會嗎&#xff1f;不會。。。。。hdfs讀文件寫文件的流程 數據寫入 1-客戶端向NameNode發起請求 2-NameNode審核權限和剩余空間&#xff0c;滿足條件即允許寫入&#xff0c;并告知客戶端寫入的DataNode地址 3-客戶端向指定的DataNode發送數據包 4-被寫入數據的DataNode同時完…

數據清理后續

前篇&#xff1a;Python 項目里的數據清理工作&#xff08;數據清洗步驟應用&#xff09; 一&#xff0c;先接上文添加兩種數據填充的方法 1、線性回歸填充 def lr_train_fill(train_data,train_label):train_data_all pd.concat([train_data, train_label], axis1)train_d…

nuc設置腳本開機自啟動

在終端執行gnome-session-properties這個是帶有圖型化頁面的設置開機自啟動的軟件沒有這個軟件的可以直接下載sudo apt update sudo apt install gnome-startup-applications一般都有&#xff0c;ubunutu自帶的右邊有添加&#xff0c;名稱和注釋隨便寫&#xff0c;只需要把命令…

JavaScript 性能優化實戰大綱

JavaScript 性能優化實戰大綱 核心優化方向 減少主線程阻塞 避免長任務&#xff08;Long Tasks&#xff09;拆分計算密集型操作使用 Web Workers 處理后臺任務優先使用 requestIdleCallback 或 requestAnimationFrame 內存管理 避免內存泄漏&#xff08;如未清理的定時器、閉包…

openssl生成自簽名證書的方法

因為開發中查詢過各種命令&#xff0c;失敗過很多次&#xff0c;所以記錄一下正確的命令&#xff1a; 生成私鑰-不要密碼 openssl genpkey -algorithm RSA -out ssl/key.pem 生成自簽名證書 openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 365 -…

電影購票+票房預測系統 - 后端項目介紹(附源碼)

電影購票預測系統 - 后端項目介紹 項目概述 本項目是一個基于Spring BootVue的前后端分離電影購票系統&#xff0c;包含完整的前臺用戶功能和后臺管理功能&#xff0c;并提供數據可視化和電影預測功能。本文檔將詳細介紹后端項目的架構、功能模塊、技術棧和使用方法。 項目源…

專利服務系統平臺|個人專利服務系統|基于java和小程序的專利服務系統設計與實現(源碼+數據庫+文檔)

專利服務系統平臺 目錄 基于java和小程序的專利服務系統設計與實現 一、前言 二、系統設計 三、系統功能設計 四、數據庫設計 五、核心代碼 六、論文參考 七、最新計算機畢設選題推薦 八、源碼獲取&#xff1a; 博主介紹&#xff1a;??大廠碼農|畢設布道師&#x…

【HTML】3D動態凱旋門

目錄 版本1.0&#xff1a;簡易版本 版本2.0&#xff1a;建筑渲染 版本3.0&#xff1a;優化建筑群 版本4.0&#xff1a;增加公路和車流 版本5.0&#xff1a;去除壓在公路上的建筑 版本6.0&#xff1a;優化車流群 版本7.0&#xff1a;添加煙花效果 版本8.0&#xff1a;添…

(論文閱讀)FedViT:邊緣視覺轉換器的聯邦持續學習

FedViT&#xff1a;邊緣視覺轉換器的聯邦持續學習 FedViT: Federated continual learning of vision transformer at edge (北京理工大學-2023年發表于《Future Generation Computer Systems》中科院二區) highlight&#xff1a; ?提出一種輕量級的客戶端聯合持續學習方法。 ?…

微算法科技(NASDAQ: MLGO)研究利用PBFT中的動態視圖變換機制,實現區塊鏈系統高效運轉

隨著區塊鏈技術的飛速發展&#xff0c;其去中心化、透明性、不可篡改等特性使得它在金融、供應鏈管理、物聯網等多個領域得到了廣泛應用。然而&#xff0c;區塊鏈系統在高并發場景下的性能瓶頸問題一直是制約其大規模應用的關鍵因素。傳統的共識算法如PoW&#xff08;工作量證明…

從數據匯總到高級分析,SQL 查詢進階實戰(下篇)—— 分組、子查詢與窗口函數全攻略

引言&#xff1a;從 “提取數據” 到 “洞察價值”&#xff0c;SQL 進階之路 在掌握了基礎查詢與多表關聯后&#xff0c;你是否曾遇到這樣的挑戰&#xff1a;如何按部門統計平均薪資&#xff1f;怎樣找出每個崗位薪資最高的員工&#xff1f;或者如何計算銷售額的月度環比增長率…

Spring 和 Lettuce 源碼分析 Redis 節點狀態檢查與失敗重連的工作原理

關鍵步驟&#xff1a;Spring Boot 啟動時創建 LettuceConnectionFactory根據配置類型&#xff08;集群/哨兵/單機&#xff09;初始化客戶端對于集群模式&#xff1a;創建 RedisClusterClient調用 setOptions(getClusterClientOptions(configuration)) 應用配置2. 節點狀態檢查機…

從ChatGPT到智能助手:Agent智能體如何顛覆AI應用

從ChatGPT到智能助手&#xff1a;Agent智能體如何顛覆AI應用 更多大模型知識分享&#xff0c;盡在>>>GitHub<<< Agent 智能體是什么 簡單來說&#xff0c;Agent 智能體是一種能夠感知環境&#xff0c;并根據自身目標自主采取行動的智能實體。它就像是一個擁…

Spring Boot應用實現圖片資源服務

在這篇文章中&#xff0c;我們將介紹如何使用Spring Boot創建一個REST API來提供服務器上的靜態圖片資源。該API包括路徑安全檢查、文件存在性驗證以及緩存控制等功能&#xff0c;并且代碼包含詳細的注釋以幫助理解。Maven依賴 首先&#xff0c;在您的pom.xml文件中添加以下依賴…

Word 中 MathType 公式編號問題與解決

注&#xff1a;本文為 “Word 中 MathType 公式編號” 相關合輯。 圖片清晰度受引文原圖所限。 略作重排&#xff0c;未整理去重。 如有內容異常&#xff0c;請看原文。 【Word】解決 MathType 已插入公式按新章節開始編號的問題 Allan326 于 2020-03-25 15:30:08 發布 問題…

19. 大數據-產品概念

文章目錄前言一、數據庫1. 簡介2. 使用場景3. 數據庫類型4. 數據類型二、數據倉庫1. 簡介2. 使用場景3. 數據倉庫架構三、數據平臺1. 簡介2. 使用場景3. 數據倉庫架構四、數據中臺1. 簡介2. 使用場景3. 數據中臺架構五、數據湖1. 簡介2. 使用場景3. 數據湖架構六、總結1. 區別2…

python學習DAY46打卡

DAY 46 通道注意力(SE注意力) 內容&#xff1a; 不同CNN層的特征圖&#xff1a;不同通道的特征圖什么是注意力&#xff1a;注意力家族&#xff0c;類似于動物園&#xff0c;都是不同的模塊&#xff0c;好不好試了才知道。通道注意力&#xff1a;模型的定義和插入的位置通道注意…