mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用

前言:

Start:2020.4.25 End2020.11.4 1:24

在看本文章前,需要你对JDK动态代理有所了解,
我的另一篇文件种详细讲解了JDK动态代理源码
地址:https://blog.csdn.net/qq_37391371/article/details/109460734

内容:

mybatis源码分析+手写简化版mybatis

[根据目录选择你需要的内容查看]

Mybatis源码

源码流程宏观

mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用_第1张图片

Mybatis替代了传统JDBC实现了相同的功能,那么mybatis也一定有经历了以上与数据库交互的必要操作:

  1. 数据源信息从哪里来?

    解析mybatis的主配置文件得到DOM文档对象模型,根节点存入Configuration对象中,其属性储存每一个元素节点,属性节点和文本节点,其属性名与主配置文件中的标签名相同

  2. sql语句从哪里来?

    解析mybatis的主配置文件拿到mapper映射文件,根据namespace+id生成dao接口的实现类代理对象

  3. 与数据库交互操作如何实现?

    dao接口实现类代理对象属性中装配有数据源,可以连接数据库;其方法是根据mapper映射文件实现的,每个方法有其要执行的sql,并对返回值进行了解析.

源码流程微观

  public static void main(String[] args) throws IOException {
     
    String resource = "mybatis.xml";
    InputStream inputStream = Resources.getResourceAsStream(resource);
    
    
    //核心代码1    						//build(inputStream)方法执行到最后关闭了流
    SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(inputStream);

    //核心代码2
    SqlSession sqlSession = factory.openSession();

    //核心代码3
    		//方式一:类似原生jdbc的preparedStatement.execute()
    Blog blog = session.selectOne("xzq.BlogMapper.selectBlog", 1);
		    
    		//方式二:利用Dao接口实现类的代理对象(这种方式留给给后面单独探究)
    //BlogMapper blogMapper = session.getMapper(BlogMapper.class);
    //Blog blog = blogMapper.selectBlog(1);
  }

SqlSessionFactoryBuilder.build(is)

Mybatis主配置文件流---------->sqlSessionFactoryBuild.build(流)核心操作--------->返回一个Sql会话工厂

因为build(is)设计到流的操作,可以推测build(is)解决的问题如下:

  1. 如何解析mybatis主配置文件中的标签内容所包含的信息?
  2. 解析出信息后又是如何保存在内存中的?
  3. 信息保存在内存中后又根据这些信息做了什么工作?
  4. 解析Environment标签和Mapper标志最为重要,因为这两个标签包含的信息涉及到建立数据库连接和执行SQL语句
//核心代码1
SqlSessionFactory factory = new SqlSessionFactoryBuilder().build(is);

mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用_第2张图片

mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用_第3张图片

核心操作是什么?就是对主配置文件进行解析,取出所有配置信息封装进Configuration对象及其属性存入内存

mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用_第4张图片

解析数据源:

mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用_第5张图片
mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用_第6张图片

一、mybatis是如何获取数据库源的
org.apache.ibatis.session.SqlSessionFactoryBuilder.build(java.io.InputStream)
》org.apache.ibatis.builder.xml.XMLConfigBuilder.parse
》org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration
》org.apache.ibatis.builder.xml.XMLConfigBuilder.environmentsElement
》org.apache.ibatis.builder.xml.XMLConfigBuilder.dataSourceElement
》org.apache.ibatis.session.Configuration.setEnvironment#######

解析Mapper:

给Dao接口方法指定SQL语句方法有哪几种?

  1. mapper映射文件中:namespase-id+SQL语句

    mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用_第7张图片
  2. 给dao接口方法上标注解
    mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用_第8张图片

Mappers文件有几种方式???

mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用_第9张图片

mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用_第10张图片

mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用_第11张图片

mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用_第12张图片

源码分析–Mappers标签的处理

package和mapperClass都是加载基于注解的Dao接口

mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用_第13张图片

不管是通过拿种方式( )指定mapper(xml映射文件,Dao接口),上面这段代码表明,mybatis都会优先尝试操作mapper.xml映射文件

像mybatis主配置文件又XMLConfiguration类处理一样,mapper映射文件由XMLMapperBuilder处理

mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用_第14张图片

二、mybatis是如何获取SQL语句

org.apache.ibatis.session.SqlSessionFactoryBuilder.build(java.io.InputStream)
》org.apache.ibatis.builder.xml.XMLConfigBuilder.parse
》org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration
》org.apache.ibatis.builder.xml.XMLConfigBuilder.mapperElement
》org.apache.ibatis.builder.xml.XMLMapperBuilder.configurationElement
》org.apache.ibatis.builder.xml.XMLStatementBuilder.parseStatementNode
>org.apache.ibatis.session.Configuration.addMappedStatement######

sqlSessionfactory.openSession();

//核心代码2
SqlSession sqlSession = factory.openSession();

根据Configuration对象的属性environment,创建一个sql会话(执行器,事务管理器,数据源信息),通过sql会话我们与数据库创建连接进行通信

mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用_第15张图片

从数据源中开启一个sql会话

image-20201030225050817

mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用_第16张图片

mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用_第17张图片

mybatis执行器如下

mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用_第18张图片

mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用_第19张图片

mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用_第20张图片

总结:
openSession()方法中对DOM中的environment元素节点中的信息成分利用.并最终实例化了一个装配有事务管理器SQL语句执行器的SQL会话(数据源->事务->执行器=sql会话)

Mybatis如何执行Sql语句源码

方式一:SqlSession中的通用模块方法

//核心代码3
		//方式一:mybatis封装的通用模板方法,类似原生jdbc的preparedStatement.execute()
Blog blog = session.selectOne("org.mybatis.example.BlogMapper.selectBlog", 1);

注意上面代码没有生成Dao接口实现类的代理对象,执行过程类似于原生JDBC的preparedStatement.execut()

mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用_第21张图片

mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用_第22张图片

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-u3WOCN53-1604422499008)(C:\Users\80685\AppData\Roaming\Typora\typora-user-images\image-20201031193603213.png)]

mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用_第23张图片

mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用_第24张图片mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用_第25张图片

mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用_第26张图片

mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用_第27张图片

三、通过SqlSession中的通用模板方法执行sql的过程

org.apache.ibatis.session.defaults.DefaultSqlSessionFactory.openSession()
   》org.apache.ibatis.session.Configuration.newExecutor(org.apache.ibatis.transaction.Transaction, org.apache.ibatis.session.ExecutorType)
    》org.apache.ibatis.executor.SimpleExecutor
     》org.apache.ibatis.session.defaults.DefaultSqlSession.selectOne(java.lang.String, java.lang.Object)
      》org.apache.ibatis.session.defaults.DefaultSqlSession.selectList(java.lang.String, java.lang.Object)
        》org.apache.ibatis.executor.CachingExecutor.query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler)
          》org.apache.ibatis.executor.CachingExecutor.query(org.apache.ibatis.mapping.MappedStatement, java.lang.Object, org.apache.ibatis.session.RowBounds, org.apache.ibatis.session.ResultHandler, org.apache.ibatis.cache.CacheKey, org.apache.ibatis.mapping.BoundSql)
           》org.apache.ibatis.executor.BaseExecutor.queryFromDatabase
             》org.apache.ibatis.executor.SimpleExecutor.doQuery
               》org.apache.ibatis.executor.statement.PreparedStatementHandler.query
                 》org.apache.ibatis.executor.resultset.DefaultResultSetHandler.handleResultSets

preparestatement.execute() 最终"关闭"资源,sqlsession.selectOne()底层就是远程jdbc.

以上步骤流程图

mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用_第28张图片

方式二:Dao接口实现类的代理对象

		//发送二:利用Dao接口实现类的代理对象
BlogMapper blogMapper = session.getMapper(BlogMapper.class);
Blog blog = blogMapper.selectBlog(1);

mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用_第29张图片

mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用_第30张图片

详细查看一下这个mapperProxy的信息:

mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用_第31张图片

mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用_第32张图片

先总结一下:

JDK动态代理 Proxy.newInstance(classLoader,interfacesType,invocationHandler)

  • interfaceTypes:BlogMapper.class

  • invocationHandler:mapperProxy

但是目标类是谁?目标了的目标方法肯定是参与执行sql语句的.

哪一个对象有这个功能?没错,就是sqlsession,它就是目标类对象

但是目标对象和代理对象要实现相同的接口,sqlsession实现了BlogMapper接口?

mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用_第33张图片

来看看JDK动态代理的步骤

mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用_第34张图片

一样的顺序!只不过人家通过了InvocationHandler实现类就是MapperProxy!

Blog blog = blogMapper.selectBlog(1);

mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用_第35张图片

mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用_第36张图片

总结:

首先方式二与方式一没有很大区别,底层一样!

底层?

其实劳了一圈经过JDK动态代理,最后还是调用sqlSesion.selectOne(sql,param)方法!

org.apache.ibatis.session.defaults.DefaultSqlSession.getMapper

  • org.apache.ibatis.session.Configuration.getMapper

    • org.apache.ibatis.binding.MapperRegistry.getMapper

      • org.apache.ibatis.binding.MapperProxyFactory.newInstance(SqlSession)

        • Proxy.newProxyInstance(mp.getClassLoader(),new Class[]{ mapperInterface },mapperProxy);

          [Spring AOP cglib java动态代理]

          • org.apache.ibatis.binding.MapperProxy.invoke

Mybatis底层是JDK动态代理,设计到JDK动态代理就有如下问题:

  1. 接口? Dao接口
  2. 目标类? SqlSession
  3. InvocationHandler实现类? MapperProxy
  4. 代理类对象(JDK动态代理帮我们在内存中生成)

手写简易版Mybatis

根据以上源码总结,手写如下简化版Mybatis.

package xzq;


import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Select {
     

    String[] value();
}
package xzq;


//1.接口
public interface UserMapper {
     

    @Select({
     "select id,name from user where id = #{id}"})
    public User getUserById(int id);
}

package xzq;



import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

// invocationHandler实现类
public class MapperProxy implements InvocationHandler {
     

    SqlSession targetObject;

    public MapperProxy(SqlSession targetObject) {
     
        this.targetObject = targetObject;
    }

    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
     

        Select annotation = method.getAnnotation(Select.class);
        String sql = annotation.value()[0];

        System.out.println("sql语句是:"+sql+"----参数是:"+args[0]);

        Object user = method.invoke(targetObject, args[0]);

        return user;
    }
}

实体类:对应数据库表中一条记录

package xzq;

//实体类
public class User {
     
    public int id;
    public String name;

    public User(int id, String name) {
     
        this.id = id;
        this.name = name;
    }

    @Override
    public String toString() {
     
        return "User{" +
                "id=" + id +
                ", name='" + name + '\'' +
                '}';
    }
}

测试类:

package xzq;

public class HandWriteMybatisTest {
     
    public static void main(String[] args) {
     
        //1.创建目标类对象
        SqlSession sqlSession = new SqlSession();

        //2.创建InvocationHandler实现类(同时传入目标类对象)  这个步骤在mybatis中是内部实现的,我们这里也在sqlsession内部实现

        //3.获取代理对象
        UserMapper UserMapper = (xzq.UserMapper) sqlSession.getMapperProxy(UserMapper.class);

        //执行操作
        User user = UserMapper.getUserById(1);
        System.out.println(user);
    }
}

mybatis源码分析-利用JDK动态代理分析源码-JDK动态代理在mybatis中的应用_第37张图片

你可能感兴趣的:(mybatis,java,源码,jdk动态代理,ssm)