准备工作
1. 定义UserDAO接口,提供一个SelectByName抽象方法
package DAO;
import Entity.User;
import org.apache.ibatis.annotations.*;
@Mapper
public interface UserDAO {
@Select("select * from manage where name = #{name}")
List<User> selectByName(String name);
}
2. 在mybatis全局配置文件中mappers属性中引用该接口,通过配置文件的二进制数据流创建SqlSession对象【Mybatis源码分析 4】Sqlsession进行CRUD操作的实现原理
package Service;
import DAO.UserDAO;
import Entity.User;
import org.apache.ibatis.binding.MapperRegistry;
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 org.junit.Test;
import java.io.IOException;
import java.io.InputStream;
import java.util.Collection;
import java.util.List;
public class UserServiceImpl implements UserService {
@Test
public void test() throws IOException {
InputStream resourceAsStream = Resources.getResourceAsStream("mybatis-conf.xml");
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(resourceAsStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
// CRUD操作
sqlSession.commit();
sqlSession.close();
}
}
我们知道SqlSession对象是SqlSession接口的默认实现类DefaultSqlSession的实例,DefaultSqlSession中实现了SqlSession接口的select、insert、update和delete等方法,通过executor、statementHandler、parameterandler和resultsethandler等调用jdbc操作数据库和处理结果【Mybatis源码分析 4】Sqlsession进行CRUD操作的实现原理。
调用SqlSession对象的select、insert、update和delete等方法需要传入一个statementid字符串参数,指定.xml中定义的statementid标识的dao方法,该方法中声明的sql语句在加载配置文件时通过org.apache.ibatis.parsing.GenericTokenParser::parse解析【Mybatis源码分析 7】mapper.xml获取参数的两种方式源码分析:${}和#{},两种传参方式的差别
在扫描映射器的时候,源码中看到,如果是resource或url引用的.xml映射文件,最终会进行parse然后再注册到Configuration对象的mappers注册器中,而package或class引用的接口方法则是通过addMappers和addMapper方法注册到Configuration对象的mappers注册器中。
那么mapper接口方法是怎么执行sql语句的呢
答:通过代理模式(org.apache.ibatis.binding.MapperProxy)
获取接口代理对象
UserDAO mapper = sqlSession.getMapper(UserDAO.class);
调用sqlSession的getMapper方法,从configuration对象的mapper注册器(mapperRegistry)中获取指定的mapper接口类型的代理对象
// DefaultSqlSession
public <T> T getMapper(Class<T> type) {
return this.configuration.getMapper(type, this);
}
// Configuration
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
return this.mapperRegistry.getMapper(type, sqlSession);
}
创建指定mapper接口类型的mapper代理对象工厂,通过mapper代理对象工厂创建mapper代理对象
// MapperRegistry
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
// 创建指定mapper接口类型的mapper代理对象工厂
MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory)this.knownMappers.get(type);
if (mapperProxyFactory == null) {
throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
} else {
try {
// 通过mapper代理对象工厂创建mapper代理对象
return mapperProxyFactory.newInstance(sqlSession);
} catch (Exception var5) {
throw new BindingException("Error getting mapper instance. Cause: " + var5, var5);
}
}
}
// MapperProxyFactory
public T newInstance(SqlSession sqlSession) {
MapperProxy<T> mapperProxy = new MapperProxy(sqlSession, this.mapperInterface, this.methodCache);
return this.newInstance(mapperProxy);
}
// MapperProxY先会执行static静态代码块,然后再执行构造方法
static {
Method privateLookupIn;
try {
//通过反射机制获取接口中的方法
privateLookupIn = MethodHandles.class.getMethod("privateLookupIn", Class.class, Lookup.class);
} catch (NoSuchMethodException var5) {
privateLookupIn = null;
}
privateLookupInMethod = privateLookupIn;
Constructor<Lookup> lookup = null;
if (privateLookupInMethod == null) {
try {
lookup = Lookup.class.getDeclaredConstructor(Class.class, Integer.TYPE);
lookup.setAccessible(true);
} catch (NoSuchMethodException var3) {
throw new IllegalStateException("There is neither 'privateLookupIn(Class, Lookup)' nor 'Lookup(Class, int)' method in java.lang.invoke.MethodHandles.", var3);
} catch (Exception var4) {
lookup = null;
}
}
lookupConstructor = lookup;
}
//构造方法
public MapperProxy(SqlSession sqlSession, Class<T> mapperInterface, Map<Method, MapperProxy.MapperMethodInvoker> methodCache) {
this.sqlSession = sqlSession;
this.mapperInterface = mapperInterface;
this.methodCache = methodCache;
}
// MapperProxyFactory对mapperProxy加工,最终返回MapperProxy对象
protected T newInstance(MapperProxy<T> mapperProxy) {
return Proxy.newProxyInstance(this.mapperInterface.getClassLoader(), new Class[]{this.mapperInterface}, mapperProxy);
}
接口代理对象mapper执行接口方法selectByName,并传入参数"p7"
List<User> list = mapper.selectByName("p7");
mapper会去执行实现InvocationHandler的invoke方法,将mapper代理对象(Object proxy)、方法(Method method)、参数数组(Object[] args)传递进去
// mapper动态代理对象执行invoke方法
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
try {
//return Object.class.equals(method.getDeclaringClass()) ? method.invoke(this, args) : this.cachedInvoker(method).invoke(proxy, method, args, this.sqlSession);
//太长了,分解一下
if(Object.class.equals(method.getDeclaringClass()))
// 如果是Object基类的方法,直接调用invoke执行sql语句,并将参数args传进去
return method.invoke(this, args);
else
// 获取MapperMethodInvoker对象,调用invoke方法
return this.cachedInvoker(method).invoke(proxy, method, args, this.sqlSession);
} catch (Throwable var5) {
throw ExceptionUtil.unwrapThrowable(var5);
}
}
获取MapperMethodInvoker对象
private MapperProxy.MapperMethodInvoker cachedInvoker(Method method) throws Throwable {
try {
// computeIfAbsent: 现在缓存集合中查看是否存在这个method键的MapperMethodInvoker对象,如果有的话直接返回该对象,没有的话进行创建。
return (MapperProxy.MapperMethodInvoker)this.methodCache.computeIfAbsent(method, (m) -> {
// 判断方法访问权限是否为default
if (m.isDefault()) {
try {
return privateLookupInMethod == null ? new MapperProxy.DefaultMethodInvoker(this.getMethodHandleJava8(method)) : new MapperProxy.DefaultMethodInvoker(this.getMethodHandleJava9(method));
} catch (InstantiationException | InvocationTargetException | NoSuchMethodException | IllegalAccessException var4) {
throw new RuntimeException(var4);
}
} else {
// 因为接口方法默认是public abstract修饰的,所以正常会执行到这,返回一个PlainMethodInvoker对象
return new MapperProxy.PlainMethodInvoker(new MapperMethod(this.mapperInterface, method, this.sqlSession.getConfiguration()));
}
});
} catch (RuntimeException var4) {
Throwable cause = var4.getCause();
throw (Throwable)(cause == null ? var4 : cause);
}
}
MapperMethodInvoker对象执行invoke()
private static class PlainMethodInvoker implements MapperProxy.MapperMethodInvoker {
private final MapperMethod mapperMethod;
public PlainMethodInvoker(MapperMethod mapperMethod) {
this.mapperMethod = mapperMethod;
}
public Object invoke(Object proxy, Method method, Object[] args, SqlSession sqlSession) throws Throwable {
// 最终调用mapperMethod的execute方法
return this.mapperMethod.execute(sqlSession, args);
}
}
mybatis实现mapper接口方法动态代理的关键类MapperMethod,主要的功能是执行SQL的相关操作,在初始化时会实例化两个组件Sql命令(SqlCommand)和方法签名(MethodSignature)
package org.apache.ibatis.binding;
import ...
public class MapperMethod {
private final MapperMethod.SqlCommand command; //SqlCommand sql语句
private final MapperMethod.MethodSignature method; //MethodSignature 方法签名
//构造方法
public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
this.command = new MapperMethod.SqlCommand(config, mapperInterface, method);
this.method = new MapperMethod.MethodSignature(config, mapperInterface, method);
}
// 执行方法
public Object execute(SqlSession sqlSession, Object[] args) {...}
// MethodSignature内部类 封装了方法的参数信息 返回类型信息等
public static class MethodSignature {...}
// SqlCommand内部类 封装了SQL标签的类型 insert update delete select
public static class SqlCommand {...}
// 其他方法
}
MapperMethod执行execute方法
首先switch判断方法
this.command.getType()会返回"SELECT"
// 核心方法: 通过MethodProxy动态代理执行MapperMethond的execute方法
public Object execute(SqlSession sqlSession, Object[] args) {
Object result;
Object param;
switch(this.command.getType()) {
case INSERT:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.insert(this.command.getName(), param));
break;
case UPDATE:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.update(this.command.getName(), param));
break;
case DELETE:
param = this.method.convertArgsToSqlCommandParam(args);
result = this.rowCountResult(sqlSession.delete(this.command.getName(), param));
break;
case SELECT:
// 对返回类型(resultType进行判断)
if (this.method.returnsVoid() && this.method.hasResultHandler()) {
this.executeWithResultHandler(sqlSession, args);
result = null;
} else if (this.method.returnsMany()) {
// 查询多条记录
result = this.executeForMany(sqlSession, args);
} else if (this.method.returnsMap()) {
result = this.executeForMap(sqlSession, args);
} else if (this.method.returnsCursor()) {
result = this.executeForCursor(sqlSession, args);
} else {
param = this.method.convertArgsToSqlCommandParam(args);
result = sqlSession.selectOne(this.command.getName(), param);
if (this.method.returnsOptional() && (result == null || !this.method.getReturnType().equals(result.getClass()))) {
result = Optional.ofNullable(result);
}
}
break;
case FLUSH:
result = sqlSession.flushStatements();
break;
default:
throw new BindingException("Unknown execution method for: " + this.command.getName());
}
if (result == null && this.method.getReturnType().isPrimitive() && !this.method.returnsVoid()) {
throw new BindingException("Mapper method '" + this.command.getName() + " attempted to return null from a method with a primitive return type (" + this.method.getReturnType() + ").");
} else {
return result;
}
}
查询多条记录
private <E> Object executeForMany(SqlSession sqlSession, Object[] args) {
//将参数转换为sql语句中的参数(比如使用了@Param命名参数注解给参数起别名啥的)
Object param = this.method.convertArgsToSqlCommandParam(args);
List result;
// 判断接口方法中是否添加分页插件
if (this.method.hasRowBounds()) {
// 提取分页信息
RowBounds rowBounds = this.method.extractRowBounds(args);
// 最终回到sqlSession调用selectList方法查询
result = sqlSession.selectList(this.command.getName(), param, rowBounds);
} else {
// 。。。
result = sqlSession.selectList(this.command.getName(), param);
}
if (!this.method.getReturnType().isAssignableFrom(result.getClass())) {
return this.method.getReturnType().isArray() ? this.convertToArray(result) : this.convertToDeclaredCollection(sqlSession.getConfiguration(), result);
} else {
return result;
}
}
所以说,映射接口中的方法是通过MapperProxy代理对象,实现InvocationHandler接口的invoke方法,执行MapperMethond的execute方法,解析接口方法中的参数和注解描述的sql语句,最终执行SqlSession的select、insert、update和delete等方法