Mybatis框架之代理模式(Proxy Pattern)的實(shí)現(xiàn)
MyBatis 框架中大量使用了代理模式 (Proxy Pattern),尤其在 Mapper 接口 的實(shí)現(xiàn)上。代理模式使得 MyBatis 能夠在不直接實(shí)現(xiàn)接口的情況下動(dòng)態(tài)地提供接口的實(shí)現(xiàn),從而簡(jiǎn)化數(shù)據(jù)庫(kù)操作代碼,同時(shí)提供更強(qiáng)大的功能。下面將詳細(xì)解讀 MyBatis 中的代理模式的工作原理及其實(shí)現(xiàn)。
1. 什么是代理模式 (Proxy Pattern)?
代理模式 是一種結(jié)構(gòu)型設(shè)計(jì)模式,它為某個(gè)對(duì)象提供一個(gè)代理對(duì)象,以控制對(duì)這個(gè)對(duì)象的訪問。代理對(duì)象通常會(huì)對(duì)請(qǐng)求進(jìn)行預(yù)處理或后處理,然后將請(qǐng)求傳遞給實(shí)際的目標(biāo)對(duì)象。
代理模式的特點(diǎn):
- 控制訪問:通過代理對(duì)象來控制對(duì)目標(biāo)對(duì)象的訪問。
- 延遲加載:可以在代理中實(shí)現(xiàn)懶加載。
- 增強(qiáng)功能:可以在調(diào)用目標(biāo)對(duì)象之前或之后執(zhí)行額外的操作(例如日志、權(quán)限檢查、事務(wù)管理等)。
2. MyBatis 中代理模式的應(yīng)用
在 MyBatis 中,代理模式的主要應(yīng)用場(chǎng)景是 Mapper 接口。開發(fā)者只需要定義 Mapper 接口,而無需提供接口的實(shí)現(xiàn)類。MyBatis 會(huì)在運(yùn)行時(shí)為這些接口創(chuàng)建動(dòng)態(tài)代理對(duì)象,通過代理對(duì)象來執(zhí)行 SQL 語句。
2.1 MyBatis 如何使用代理模式
- Mapper 接口:用戶定義的接口,用于聲明數(shù)據(jù)庫(kù)操作方法(如
getUserById
、insertUser
等)。 - Mapper 動(dòng)態(tài)代理:MyBatis 通過 JDK 動(dòng)態(tài)代理 為 Mapper 接口生成代理對(duì)象。
SqlSession.getMapper()
方法:用于獲取 Mapper 接口的代理實(shí)例。當(dāng)調(diào)用代理實(shí)例的方法時(shí),會(huì)由 MyBatis 攔截并執(zhí)行相應(yīng)的 SQL 語句。
3. 代理模式的工作流程
3.1 工作原理
- 開發(fā)者定義一個(gè) Mapper 接口,聲明數(shù)據(jù)庫(kù)操作方法。
- 通過
SqlSession.getMapper(Class<T> clazz)
方法獲取接口的代理對(duì)象。 - 調(diào)用代理對(duì)象的方法時(shí),MyBatis 會(huì)通過
MapperProxy
攔截方法調(diào)用。 MapperProxy
通過MappedStatement
查找對(duì)應(yīng)的 SQL 語句,并執(zhí)行相應(yīng)的數(shù)據(jù)庫(kù)操作。- 將查詢結(jié)果封裝成接口方法的返回類型(如
List<User>
)。
3.2 Mapper 代理示意圖
UserMapper (接口) ↓ SqlSession.getMapper(UserMapper.class) ↓ MapperProxy (JDK 動(dòng)態(tài)代理) ↓ MappedStatement (映射 SQL) ↓ 執(zhí)行 SQL 并返回結(jié)果
4. 實(shí)際代碼示例
4.1 創(chuàng)建 Mapper 接口 (UserMapper.java
)
package com.example.mapper; import com.example.model.User; import java.util.List; public interface UserMapper { // 查詢所有用戶 List<User> getAllUsers(); // 根據(jù) ID 查詢用戶 User getUserById(int id); }
4.2 編寫 Mapper XML 文件 (UserMapper.xml)
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"> <mapper namespace="com.example.mapper.UserMapper"> <select id="getAllUsers" resultType="com.example.model.User"> SELECT * FROM users; </select> <select id="getUserById" parameterType="int" resultType="com.example.model.User"> SELECT * FROM users WHERE id = #{id}; </select> </mapper>
4.3 MyBatis 配置文件 (mybatis-config.xml)
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/> <dataSource type="POOLED"> <property name="driver" value="com.mysql.cj.jdbc.Driver"/> <property name="url" value="jdbc:mysql://localhost:3306/mydatabase"/> <property name="username" value="root"/> <property name="password" value="password"/> </dataSource> </environment> </environments> <mappers> <mapper resource="com/example/mapper/UserMapper.xml"/> </mappers> </configuration>
4.4 使用 SqlSession 獲取 Mapper 代理對(duì)象 (MyBatisExample.java)
import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import com.example.mapper.UserMapper; import com.example.model.User; import java.io.InputStream; import java.util.List; public class MyBatisExample { public static void main(String[] args) { String resource = "mybatis-config.xml"; try (InputStream inputStream = Resources.getResourceAsStream(resource)) { // 創(chuàng)建 SqlSessionFactory SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream); // 打開 SqlSession try (SqlSession session = sqlSessionFactory.openSession()) { // 獲取 Mapper 接口的代理對(duì)象 UserMapper userMapper = session.getMapper(UserMapper.class); // 調(diào)用代理對(duì)象的方法 List<User> users = userMapper.getAllUsers(); users.forEach(user -> System.out.println(user.getName())); // 根據(jù) ID 查詢用戶 User user = userMapper.getUserById(1); System.out.println("User ID 1: " + user.getName()); } } catch (Exception e) { e.printStackTrace(); } } }
5. MyBatis 代理模式的實(shí)現(xiàn)細(xì)節(jié)
MapperProxy
類:MyBatis 使用MapperProxy
類來實(shí)現(xiàn) JDK 動(dòng)態(tài)代理。MapperProxy
實(shí)現(xiàn)了InvocationHandler
接口,用于攔截 Mapper 接口方法的調(diào)用。MapperMethod
類:MapperProxy
會(huì)將攔截到的方法調(diào)用委托給MapperMethod
對(duì)象。MapperMethod
根據(jù)方法名查找對(duì)應(yīng)的MappedStatement
,然后執(zhí)行相應(yīng)的 SQL 語句。
MapperProxy 示例(簡(jiǎn)化版)
public class MapperProxy implements InvocationHandler { private SqlSession sqlSession; private Class<?> mapperInterface; public MapperProxy(SqlSession sqlSession, Class<?> mapperInterface) { this.sqlSession = sqlSession; this.mapperInterface = mapperInterface; } @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { String statementId = mapperInterface.getName() + "." + method.getName(); return sqlSession.selectList(statementId, args); } }
6. 代理模式的優(yōu)勢(shì)
- 解耦:開發(fā)者只需定義接口,無需編寫實(shí)現(xiàn)類,降低代碼耦合度。
- 簡(jiǎn)化代碼:減少重復(fù)的數(shù)據(jù)庫(kù)操作代碼,提高開發(fā)效率。
- 動(dòng)態(tài)性:通過動(dòng)態(tài)代理機(jī)制,在運(yùn)行時(shí)動(dòng)態(tài)生成代理對(duì)象,減少硬編碼。
- 靈活擴(kuò)展:可以輕松添加攔截器,實(shí)現(xiàn)如日志記錄、權(quán)限校驗(yàn)、事務(wù)控制等功能。
7. 代理模式的不足
- 性能開銷:動(dòng)態(tài)代理在方法調(diào)用時(shí)有一定的性能開銷,特別是在高并發(fā)場(chǎng)景下。
- 調(diào)試?yán)щy:由于沒有實(shí)際的實(shí)現(xiàn)類,調(diào)試時(shí)無法直接跳轉(zhuǎn)到方法實(shí)現(xiàn),調(diào)試復(fù)雜度增加。
- 學(xué)習(xí)成本:對(duì)于不熟悉動(dòng)態(tài)代理機(jī)制的開發(fā)者,理解 MyBatis 的內(nèi)部工作原理可能有一定的難度。
8. 總結(jié)
MyBatis 通過代理模式大幅簡(jiǎn)化了數(shù)據(jù)庫(kù)操作代碼,使得開發(fā)者可以更專注于業(yè)務(wù)邏輯而不是 SQL 操作。MyBatis 代理模式的核心是使用 JDK 動(dòng)態(tài)代理機(jī)制,在運(yùn)行時(shí)為 Mapper 接口生成代理對(duì)象,從而將接口方法映射到相應(yīng)的 SQL 語句執(zhí)行。代理模式的使用提高了 MyBatis 的靈活性和擴(kuò)展性,是其重要的設(shè)計(jì)亮點(diǎn)之一。
到此這篇關(guān)于Mybatis框架之代理模式(Proxy Pattern)的文章就介紹到這了,更多相關(guān)Mybatis 代理模式內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java中使用try-catch-finally一些值得注意的事(必看)
下面小編就為大家?guī)硪黄猨ava中使用try-catch-finally一些值得注意的事(必看)。小編覺得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2016-08-08SpringBoot集成ElasticSearch(ES)實(shí)現(xiàn)全文搜索功能
Elasticsearch是一個(gè)開源的分布式搜索和分析引擎,它被設(shè)計(jì)用于處理大規(guī)模數(shù)據(jù)集,它提供了一個(gè)分布式多用戶能力的全文搜索引擎,本文將給大家介紹SpringBoot集成ElasticSearch(ES)實(shí)現(xiàn)全文搜索功能,需要的朋友可以參考下2024-02-02springsecurity中http.permitall與web.ignoring的區(qū)別說明
這篇文章主要介紹了springsecurity中http.permitall與web.ignoring的區(qū)別說明,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08java實(shí)現(xiàn)的根據(jù)概率隨機(jī)中獎(jiǎng)測(cè)試類
這篇文章主要介紹了java實(shí)現(xiàn)的根據(jù)概率隨機(jī)中獎(jiǎng)測(cè)試類,結(jié)合完整實(shí)例形式詳細(xì)分析了java隨機(jī)數(shù)實(shí)現(xiàn)概率運(yùn)算相關(guān)操作技巧,需要的朋友可以參考下2019-09-09Java Hibernate中使用HQL語句進(jìn)行數(shù)據(jù)庫(kù)查詢的要點(diǎn)解析
HQL是Hibernate框架中提供的關(guān)系型數(shù)據(jù)庫(kù)操作腳本,當(dāng)然我們也可以使用原生的SQL語句,這里我們來看一下在Java Hibernate中使用HQL語句進(jìn)行數(shù)據(jù)庫(kù)查詢的要點(diǎn)解析:2016-06-06Java?數(shù)據(jù)結(jié)構(gòu)與算法系列精講之漢諾塔
漢諾塔是源于印度一個(gè)古老傳說的益智玩具。大梵天創(chuàng)造世界時(shí)做了三根石柱,在一根柱子上從下往上按大小順序摞著64片黃金圓盤。大梵天命令婆羅門把圓盤從下面開始按大小順序重新擺放在另一根柱子上。并且規(guī)定,在小圓盤上不能放大圓盤,三根柱子之間一次只能移動(dòng)一個(gè)圓盤2022-02-02