【Mybatis源码分析 8】mybatis的mapper接口中dao方法并没有具体实现类,是怎么通过mapper动态代理运行的

准备工作

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等方法

 

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值