原理分析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ū)動坐標(biāo)-->
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>5.1.6</version>
<scope>runtime</scope>
</dependency>
<!--單元測試坐標(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ù)庫

3.實體類
@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>
<!--通過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工廠對象
SqlSessionFactory sqlSessionFactory = new
SqlSessionFactoryBuilder().build(resourceAsStream);
//獲得sqlSession對象
SqlSession sqlSession = sqlSessionFactory.openSession();
//執(zhí)行sql語句
userDao mapper = sqlSession.getMapper(userDao.class);
List<user> userList = mapper.findAll();
//打印結(jié)果
System.out.println(userList);
//釋放資源
sqlSession.close();
}
}
源碼分析
1.斷點

2.查看源碼
DefaultSqlSession:

Configuration:

這是Configuration 類。我們主要看getMapper()方法

MapperRegistry:

關(guān)鍵代碼:mapperProxyFactory.newInstance(sqlSession);
MapperProxyFactory:

返回的是MapperProxy 對象
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 中對SQL語句進(jìn)行分類反射
總結(jié)
- MapperProxyFactory中,使用JDK的動態(tài)代理生成Mapper接口的代理代理類
- 由動態(tài)處理器MapperProxy中調(diào)用MapperMethod中的方法處理執(zhí)行SQL
- 最后,在MapperMethod中根據(jù)執(zhí)行的方法返回值決定調(diào)用SqlSession中的對應(yīng)方法執(zhí)行SQL
本篇文章就到這里了,希望能夠給你帶來幫助,也希望您能夠多多關(guān)注腳本之家的更多內(nèi)容!
相關(guān)文章
解決Springboot中Feignclient調(diào)用時版本問題
這篇文章主要介紹了解決Springboot中Feign?client調(diào)用時版本問題,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教2022-03-03
Zookeeper如何實現(xiàn)分布式服務(wù)配置中心詳解
Zookeeper在實際使用場景很多,比如配置中心,分布式鎖,注冊中心等,下面這篇文章主要給大家介紹了關(guān)于Zookeeper如何實現(xiàn)分布式服務(wù)配置中心的相關(guān)資料,需要的朋友可以參考下2021-11-11
在CentOS系統(tǒng)上安裝Java?JDK?8簡單步驟
最近購買一臺新的云服務(wù)器,用于開發(fā)學(xué)習(xí)使用,因此需要安裝很多的組件,下面這篇文章主要給大家介紹了關(guān)于在CentOS系統(tǒng)上安裝Java?JDK8的簡單步驟,需要的朋友可以參考下2023-12-12
java9學(xué)習(xí)系列之安裝與jshell使用
2017年9月21日,千呼萬喚始出來,Java9終于發(fā)布了。作為自己天天接觸的“對象”,還是應(yīng)該多花點心思去了解她。后續(xù)再進(jìn)一步了解詳細(xì)特性。下面這篇文章主要給大家介紹了關(guān)于java9學(xué)習(xí)系列之安裝與jshell使用的相關(guān)資料,需要的朋友可以參考下。2017-09-09
Java Chassis3過載狀態(tài)下的快速失敗解決分析
本文解密了Java Chassis 3快速失敗相關(guān)的機(jī)制和背后故事,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2024-01-01

