IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> MyBatis Mapper JDK动态代理方式源码分析 -> 正文阅读

[Java知识库]MyBatis Mapper JDK动态代理方式源码分析

从JDBC优化到传统XML配置Mybatis主线源码解析


上篇文章从JDBC优化开始,对Mybatis传统xml配置主线流程源码剖析,这里不多对JDBC,xml配置的框架源码做多的分析,直接看源码:

还是从框架的启动入口着手,SqlSession接口的解析,在上篇文章中有提到这里不多赘述。

通常情况,UserMapper我们并没有对其进行接口的实现,在接口的声明时通过@Mapper配置或XML配置Mapper接口路径去暴露接口位置信息。 接口中包含的一些Mybatis框架自己封装的数据库操作方法或是映射xml中配置的方法,它们是怎样被调用的?可以肯定的是通过了接口路径去做了动态的加载,而方法的调用则是通过了动态代理的方式去执行。

 InputStream inputStream =Resources.getResourceAsStream("sqlMapConfig.xml");
 SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);
 SqlSession sqlSession = factory.openSession();
 UserMapper mapper = sqlSession.getMapper(UserMapper.class);
 List<User> list = mapper.getUserByName("test")

getMapper解析

上面说到了UserMapper接口的加载,是怎样去加载的?直接点进getMapper方法去查看。进入到SqlSession接口中的getMapper方法,有两个实现类?DefaultSqlSession &?SqlSessionManager。?

/**
     * Retrieves a mapper.
     * @param <T> the mapper type
     * @param type Mapper interface class
     * @return a mapper bound to this SqlSession
     */
    <T> T getMapper(Class<T> type);

这里不用多去分析具体去查阅哪一个,他们中的实现方法都会跳转到Configuration类中getMapper方法中去

    public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        return mapperRegistry.getMapper(type, sqlSession);
    }

然后看一下MapperRegistry类,上一篇文章中有说到过Configure类,它里面存放了Mybatis读取的配置信息,XML中解析出来的MapperStatement对象,解析拼接而成的sql....,这里的源码中可以发现MapperRegistry是用来做了一个配置的传递对象,有点类似工厂类去做的事情。

?MapperRegistry的对象实例化后,调用了它的getMapper方法。然后会发现它去获取了一个MapperProxyFactory对象。

public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
        // 获得 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.");
        }
        /// 通过动态代理工厂生成实例。
        try {
            return mapperProxyFactory.newInstance(sqlSession);
        } catch (Exception e) {
            throw new BindingException("Error getting mapper instance. Cause: " + e, e);
        }
    }

不急着往下看,先看knownMappers这个属性中是如何存入值的,这里就需要一步一步的倒放去看。knownMappers是定义的一个MapperProxyFactory的存放容器。既然是容器那么肯定有一个存入容器的方法提供,在这个类中往下翻。

?果然发现了一个addMapper的方法,去往容器中注值。前面的判断就是判断传过来的这类是不是一个接口然后就开始放入,然后对一些注解进行了解析,解析的无非就是Mapper接口中不想去配置XML去加了@Select等等一些sql直接暴露的注解,这里不多深挖,去看这个addMapper方法被谁调用了!

?然后继续的去倒着查,发现又回到了Configure对象中去了...,还是老一套的Configure对象中调用Resiger对象方法,然后继续的倒追,然后到了Mybatis框架启动build类中的mapperElement方法,这个里的MapperClass我用红箭头标记了,是解析xml中获取的Mapper接口的路径信息,既然到这里了,回顾下Mapper接口的配置吧?,直接贴配置,配置不做多的分析。到了这里就跟我前面说的吻合了,通过配置的类的路径去动态加载这个类的实例。

<mappers>
 <mapper class="cn.yihan.mapper.UserMapper"/>
 <package name="cn.yihan.mapper"/>
</mappers>

回到上面的MapperProxyFactory,这里就是重点了,拿到这个类后下面调用了它的newInstance方法,从命名上来看应该是实例化,刚刚一路跟踪过来也看了,拿到的只是一个Class对象并没有进行实例化的Mapper.class。

点进去到MapperProxyFactory的newInstance方法,先从这个类的初始化方法说起,之前knownMappers方法去实例化MapperFactory对象传入的Mapper.class通过初始化方法赋值属性mapperInterface,而methodCache则是用来存放mapper的方法的容器。参数解释完,然后去看MapperProxy方法。

public class MapperProxyFactory<T> {

    /**
     * Mapper 接口
     */
    private final Class<T> mapperInterface;

    /**
     * 方法与 MapperMethod 的映射
     */
    private final Map<Method, MapperMethod> methodCache = new ConcurrentHashMap<>();

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


   public T newInstance(SqlSession sqlSession) {
        // 创建了JDK动态代理的invocationHandler接口的实现类mapperProxy
        final MapperProxy<T> mapperProxy = new MapperProxy<>(sqlSession, mapperInterface, methodCache);
        // 调用了重载方法
        return newInstance(mapperProxy);
    }
}

MapperProxy对象实现了InvocationHandler接口,去做JDK动态代理,JDK动态代理这里不做多的顶层赘述,简要说一下,JDK动态代理返回了示例后,可以直接调用mapper类中的方法了,但代理对象不管调用什么方法,在调方法时,执行的是 在MapperProxy中的invoke方法中,这也是AOP面向切面编程的重要方式之一。

贴出invoke方法,?Proxy是动态代理对象,method是你调用的动态代理的方法,args则是你传入的参数,等同Object ... params 的写法。点进去看execute方法,然后发现到这里之后,则开始执行Sql的处理基本操作了,然后返回Sql执行的对象。?然后就是方法执行中做了很多的反射判断,对方法是不是存在啊,参数是不是对应的上啊之类的判断然后返回异常给开发人员等等操作,不做多的赘述。

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        try {
            // 如果是 Object 定义的方法,直接调用
            if (Object.class.equals(method.getDeclaringClass())) {
                return method.invoke(this, args);

            } else if (isDefaultMethod(method)) {
                return invokeDefaultMethod(proxy, method, args);
            }
        } catch (Throwable t) {
            throw ExceptionUtil.unwrapThrowable(t);
        }
        // 获得 MapperMethod 对象
        final MapperMethod mapperMethod = cachedMapperMethod(method);
        // 重点在这:MapperMethod最终调用了执行的方法
        return mapperMethod.execute(sqlSession, args);
    }

?

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2021-08-15 15:22:28  更:2021-08-15 15:23:57 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年5日历 -2024/5/20 6:43:49-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码