原理分析Java?Mybatis中的Mapper
準(zhǔn)備
1.pom文件
<dependencies> <!--mybatis坐標(biāo)--> <dependency> <groupId>org.mybatis</groupId> <artifactId>mybatis</artifactId> <version>3.4.5</version> </dependency> <!--mysql驅(qū)動(dòng)坐標(biāo)--> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> <version>5.1.6</version> <scope>runtime</scope> </dependency> <!--單元測(cè)試坐標(biāo)--> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> <scope>test</scope> </dependency> <!--日志坐標(biāo)--> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.12</version> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.2</version> <scope>provided</scope> </dependency> </dependencies>
2.user類-數(shù)據(jù)庫(kù)
3.實(shí)體類
@Getter @Setter @ToString @NoArgsConstructor public class user { private int id; private String username; private String password; }
4.dao 層
public interface userDao { List<user> findAll(); }
5.Mapper 文件
<?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="dao.userDao"> <select id="findAll" resultType="mode.user"> select * from user </select> </mapper>
核心配置文件
<?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> <!--通過(guò)properties標(biāo)簽加載外部properties文件--> <properties resource="jdbc.properties"></properties> <!--自定義別名--> <typeAliases> <typeAlias type="mode.user" alias="user"></typeAlias> </typeAliases> <!--數(shù)據(jù)源環(huán)境--> <environments default="developement"> <environment id="developement"> <transactionManager type="JDBC"></transactionManager> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment> </environments> <!--加載映射文件--> <mappers> <mapper resource="userMapper.xml"></mapper> </mappers> </configuration>
核心代碼
import dao.userDao; import mode.user; import org.apache.ibatis.io.Resources; import org.apache.ibatis.session.SqlSession; import org.apache.ibatis.session.SqlSessionFactory; import org.apache.ibatis.session.SqlSessionFactoryBuilder; import java.io.IOException; import java.io.InputStream; import java.util.List; public class TestMapper { public static void main(String[] args) throws IOException { //加載核心配置文件 InputStream resourceAsStream = Resources.getResourceAsStream("SqlMapConfig.xml"); //獲得sqlSession工廠對(duì)象 SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream); //獲得sqlSession對(duì)象 SqlSession sqlSession = sqlSessionFactory.openSession(); //執(zhí)行sql語(yǔ)句 userDao mapper = sqlSession.getMapper(userDao.class); List<user> userList = mapper.findAll(); //打印結(jié)果 System.out.println(userList); //釋放資源 sqlSession.close(); } }
源碼分析
1.斷點(diǎn)
2.查看源碼
DefaultSqlSession:
Configuration:
這是Configuration 類。我們主要看getMapper()方法
MapperRegistry:
關(guān)鍵代碼:mapperProxyFactory.newInstance(sqlSession);
MapperProxyFactory:
返回的是MapperProxy 對(duì)象
MapperProxy:
源碼:
// Source code recreated from a .class file by IntelliJ IDEA // (powered by FernFlower decompiler) // package org.apache.ibatis.binding; import java.io.Serializable; import java.lang.invoke.MethodHandles.Lookup; import java.lang.reflect.Constructor; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.util.Map; import org.apache.ibatis.lang.UsesJava7; import org.apache.ibatis.reflection.ExceptionUtil; import org.apache.ibatis.session.SqlSession; public class MapperProxy<T> implements InvocationHandler, Serializable { private static final long serialVersionUID = -6424540398559729838L; private final SqlSession sqlSession; private final Class<T> mapperInterface; private final Map<Method, MapperMethod> methodCache; public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperMethod> methodCache) { this.sqlSession = sqlSession; this.mapperInterface = mapperInterface; this.methodCache = methodCache; } public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { try { if (Object.class.equals(method.getDeclaringClass())) { return method.invoke(this, args); } if (this.isDefaultMethod(method)) { return this.invokeDefaultMethod(proxy, method, args); } } catch (Throwable var5) { throw ExceptionUtil.unwrapThrowable(var5); } MapperMethod mapperMethod = this.cachedMapperMethod(method); return mapperMethod.execute(this.sqlSession, args); } private MapperMethod cachedMapperMethod(Method method) { MapperMethod mapperMethod = (MapperMethod)this.methodCache.get(method); if (mapperMethod == null) { mapperMethod = new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration()); this.methodCache.put(method, mapperMethod); } return mapperMethod; } @UsesJava7 private Object invokeDefaultMethod(Object proxy, Method method, Object[] args) throws Throwable { Constructor<Lookup> constructor = Lookup.class.getDeclaredConstructor(Class.class, Integer.TYPE); if (!constructor.isAccessible()) { constructor.setAccessible(true); } Class<?> declaringClass = method.getDeclaringClass(); return ((Lookup)constructor.newInstance(declaringClass, 15)).unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args); } private boolean isDefaultMethod(Method method) { return (method.getModifiers() & 1033) == 1 && method.getDeclaringClass().isInterface(); } }
在invoke方法中可以看到,如果我們調(diào)用的是Object中的方法,不做任何處理,直接調(diào)用,否則執(zhí)行:
mapperMethod.execute(this.sqlSession, args);
MapperMethod:
在MapperMethod 中對(duì)SQL語(yǔ)句進(jìn)行分類反射
總結(jié)
- MapperProxyFactory中,使用JDK的動(dòng)態(tài)代理生成Mapper接口的代理代理類
- 由動(dòng)態(tài)處理器MapperProxy中調(diào)用MapperMethod中的方法處理執(zhí)行SQL
- 最后,在MapperMethod中根據(jù)執(zhí)行的方法返回值決定調(diào)用SqlSession中的對(duì)應(yīng)方法執(zhí)行SQL
本篇文章就到這里了,希望能夠給你帶來(lái)幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
java 使用線程做的一個(gè)簡(jiǎn)單的ATM存取款實(shí)例代碼
線程 Thread 類,和 Runable 接口 比較兩者的特點(diǎn)和應(yīng)用領(lǐng)域.可以,直接繼承線程Thread類。該方法編寫(xiě)簡(jiǎn)單,可以直接操作線程,適用于單重繼承情況,因而不能在繼承其他類,下面我們來(lái)看一個(gè)實(shí)例2013-08-08解決Springboot中Feignclient調(diào)用時(shí)版本問(wèn)題
這篇文章主要介紹了解決Springboot中Feign?client調(diào)用時(shí)版本問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-03-03Zookeeper如何實(shí)現(xiàn)分布式服務(wù)配置中心詳解
Zookeeper在實(shí)際使用場(chǎng)景很多,比如配置中心,分布式鎖,注冊(cè)中心等,下面這篇文章主要給大家介紹了關(guān)于Zookeeper如何實(shí)現(xiàn)分布式服務(wù)配置中心的相關(guān)資料,需要的朋友可以參考下2021-11-11在CentOS系統(tǒng)上安裝Java?JDK?8簡(jiǎn)單步驟
最近購(gòu)買(mǎi)一臺(tái)新的云服務(wù)器,用于開(kāi)發(fā)學(xué)習(xí)使用,因此需要安裝很多的組件,下面這篇文章主要給大家介紹了關(guān)于在CentOS系統(tǒng)上安裝Java?JDK8的簡(jiǎn)單步驟,需要的朋友可以參考下2023-12-12java9學(xué)習(xí)系列之安裝與jshell使用
2017年9月21日,千呼萬(wàn)喚始出來(lái),Java9終于發(fā)布了。作為自己天天接觸的“對(duì)象”,還是應(yīng)該多花點(diǎn)心思去了解她。后續(xù)再進(jìn)一步了解詳細(xì)特性。下面這篇文章主要給大家介紹了關(guān)于java9學(xué)習(xí)系列之安裝與jshell使用的相關(guān)資料,需要的朋友可以參考下。2017-09-09Java Chassis3過(guò)載狀態(tài)下的快速失敗解決分析
本文解密了Java Chassis 3快速失敗相關(guān)的機(jī)制和背后故事,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01解決SpringBoot引用別的模塊無(wú)法注入的問(wèn)題
這篇文章主要介紹了解決SpringBoot引用別的模塊無(wú)法注入的問(wèn)題,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2022-02-02