Mybatis--Binding模块

1、概述

 Mybatis的Binding模块对应的是io包,如下图所示: 

             

Binding模块所涉及的类如下图所示:  

2. MapperRegistry 

2.1 构造方法



//MyBatis Configuration 对象
private final Configuration config;

private final Map<Class<?>, MapperProxyFactory<?>> knownMappers = new HashMap<>();

public MapperRegistry(Configuration config) {
    this.config = config;
}

2.2 addMappers

addMappers(String packageName, ...) 方法,扫描指定包,并将符合的类,添加到 knownMappers 中。代码如下:

public void addMappers(String packageName) {
    addMappers(packageName, Object.class);
}
    
public void addMappers(String packageName, Class<?> superType) {
    // 1、 扫描指定包下的指定类
    ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>();
    resolverUtil.find(new ResolverUtil.IsA(superType), packageName);
    Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
    // 2、遍历,添加到 knownMappers 中
    for (Class<?> mapperClass : mapperSet) {
        addMapper(mapperClass);
    }
}

先使用 ResolverUtil 扫描指定包下的指定类,在 上篇的 IO 模块中有,在通过addMapper方法添加到knowMappers中,代码如下:


public <T> void addMapper(Class<T> type) {
    // 1、判断是否为接口。
    if (type.isInterface()) {
        // 2、 已经添加过,则抛出 BindingException 异常
        if (hasMapper(type)) {
            throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
        }
        boolean loadCompleted = false;
        try {
            // 3、 添加到 knownMappers 中
            knownMappers.put(type, new MapperProxyFactory<>(type));
            // 4、解析 Mapper 的注解配置
            MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
            parser.parse();
            // 5、 标记加载完成
            loadCompleted = true;
        } finally {
            // 6、若加载未完成,从 knownMappers 中移除
            if (!loadCompleted) {
                knownMappers.remove(type);
            }
        }
    }
}

2.3 getMapper

getMapper(Class<T> type, SqlSession sqlSession) 方法,获得 Mapper Proxy 对象。代码如下:

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
    // 1、获得 MapperProxyFactory 对象
    final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
    // 不存在,则抛出 BindingException 异常
    if (mapperProxyFactory == null) {
        throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
    }
    // 2、创建 Mapper Proxy 对象
    try {
        return mapperProxyFactory.newInstance(sqlSession);
    } catch (Exception e) {
        throw new BindingException("Error getting mapper instance. Cause: " + e, e);
    }
}

<1> 处,从 knownMappers 中,获得 MapperProxyFactory 对象。
             <2> 处,调用 MapperProxyFactory#newInstance(SqlSession sqlSession) 方法,创建 Mapper Proxy 对象。

3. MapperProxyFactory

org.apache.ibatis.binding.MapperProxyFactory ,Mapper Proxy 工厂类。

3.1 构造方法


 //Mapper 接口
private final Class<T> mapperInterface;
 
//方法与 MapperMethod 的映射
private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();

public MapperProxyFactory(Class<T> mapperInterface) {
    this.mapperInterface = mapperInterface;
}

3.2 newInstance

newInstance(...) 方法,创建 Mapper Proxy 对象。代码如下:


protected T newInstance(MapperProxy<T> mapperProxy) {
    return (T) Proxy.newProxyInstance(mapperInterface.getClassLoader(), new Class[]{mapperInterface}, mapperProxy);
}

public T newInstance(SqlSession sqlSession) {
    final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
    return newInstance(mapperProxy);
}

依然是稳稳的基于 JDK Proxy 实现,而 InvocationHandler 参数是 MapperProxy 对象。

4. MapperProxy 

4.1 构造方法 

//SqlSession 对象 
private final SqlSession sqlSession;
// Mapper 接口

private final Class<T> mapperInterface;

// 方法与 MapperMethod 的映射 ,从  MapperProxyFactory的属性methodCache 传递过来
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;
}

 4.2 invoke

@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
    try {
        // 1、如果是 Object 定义的方法,直接调用
        if (Object.class.equals(method.getDeclaringClass())) {
            return method.invoke(this, args);
        //2、判断是否为 default 修饰的方法
        } else if (isDefaultMethod(method)) {
            return invokeDefaultMethod(proxy, method, args);
        }
    } catch (Throwable t) {
        throw ExceptionUtil.unwrapThrowable(t);
    }
    // 3、获得 MapperMethod 对象
    final MapperMethod mapperMethod = cachedMapperMethod(method);
    // 4、 执行 MapperMethod 方法
    return mapperMethod.execute(sqlSession, args);
}

<1> 处,如果是 Object 定义的方法,直接调用。
            <2> 处,调用 #isDefaultMethod((Method method) 方法,判断是否为 default 修饰的方法,若是,则调用 invokeDefaultMethod(Object proxy, Method method, Object[] args) 方法,进行反射调用。

<3> 处,调用 #cachedMapperMethod(Method method) 方法,获得 MapperMethod 对象。代码如下:


private MapperMethod cachedMapperMethod(Method method) {
    return methodCache.computeIfAbsent(method, k -> new MapperMethod(mapperInterface, method, sqlSession.getConfiguration()));
}

<4> 处,调用 MapperMethod#execute(SqlSession sqlSession, Object[] args) 方法,执行 MapperMethod 方法。

5. MapperMethod

org.apache.ibatis.binding.MapperMethod ,Mapper 方法。在 Mapper 接口中,每个定义的方法,对应一个 MapperMethod 对象。

5.1 构造方法


// SqlCommand 对象

private final SqlCommand command;

// MethodSignature 对象

private final MethodSignature method;

public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
    this.command = new SqlCommand(config, mapperInterface, method);
    this.method = new MethodSignature(config, mapperInterface, method);
}

command 属性,SqlCommand 对象,是 MapperMethod 的内部静态类,SQL 命令。
             method 属性,MethodSignature 对象,是 MapperMethod 的内部静态类,方法签名。

5.2 execute

这个方法涉及到执行一个Sql命令的完整流程,所以另起一篇续写。

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值