zoukankan      html  css  js  c++  java
  • 关于mybaitis

    mybatis启动流程

    1、首先来看看最简单的mybatis项目启动过程

    public static void mybatisTest() throws IOException {
        String resource = "mybatis/mybatis-config.xml";
        //配置文件
        InputStream inputStream = Resources.getResourceAsStream(resource);
        //SqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        SqlSession sqlSession = null;
        try {
            //开启一个session
            sqlSession = sqlSessionFactory.openSession();
            //获取mapper
            JobConfigDao jobConfigDao = sqlSession.getMapper(JobConfigDao.class);
            //查询数据
            List<JobConfig> jobConfigList = jobConfigDao.listConfig();
            System.out.println(jobConfigList);
        } finally {
            if (sqlSession != null)
                sqlSession.close();
        }
    }
    

    这个过程主要是SqlSessionFactory 的创建,先获取配置信息,然后通过SqlSessionFactoryBuilder来创建SqlSessionFactory 。
    build方法的核心代码是:

    XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties); 
    var5 = this.build(parser.parse());
    

    可以看到mybatis是通过XMLConfigBuilder来解析配置文件的。来看看XMLConfigBuilder的parse方法:

    public Configuration parse() {
        //XMLConfigBuilder中有一个boolean类型parsed,来标记是否解析过配置文件,解析过之后就设置为true,防止同样的配置被重复解析
        if (this.parsed) {
            throw new BuilderException("Each XMLConfigBuilder can only be used once.");
        } else {
            this.parsed = true;
            // 解析configuration节点下的配置
            this.parseConfiguration(this.parser.evalNode("/configuration"));
            return this.configuration;
        }
    }
    
    private void parseConfiguration(XNode root) {
        try {
            this.propertiesElement(root.evalNode("properties"));
            Properties settings = this.settingsAsProperties(root.evalNode("settings"));
            this.loadCustomVfs(settings);
            this.typeAliasesElement(root.evalNode("typeAliases"));
            this.pluginElement(root.evalNode("plugins"));
            this.objectFactoryElement(root.evalNode("objectFactory"));
            this.objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
            this.reflectorFactoryElement(root.evalNode("reflectorFactory"));
            this.settingsElement(settings);
            this.environmentsElement(root.evalNode("environments"));
            this.databaseIdProviderElement(root.evalNode("databaseIdProvider"));
            this.typeHandlerElement(root.evalNode("typeHandlers"));
            // 解析mapper
            this.mapperElement(root.evalNode("mappers"));
        } catch (Exception var3) {
            throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + var3, var3);
        }
    }
    

    可以看到,在parseConfiguration方法中会将mybatis-config.xml配置下的各种属性获取出来一一解析,并映射到相关的属性上面去。

    mybatis定义的接口,怎么找到实现的?

    通过上面的启动过程,已经知道mybatis是通过XMLConfigBuilder来解析配置文件的,具体解析是在parseConfiguration方法的中,获取到mappers配置节点,最后一行this.mapperElement(root.evalNode("mappers"))将节点信息转交给XMLMapperBuilder来完成对mapper的解析。

    来看看XMLMapperBuilder的parse方法:

    public void parse() {
        if (!this.configuration.isResourceLoaded(this.resource)) {
            this.configurationElement(this.parser.evalNode("/mapper"));
            this.configuration.addLoadedResource(this.resource);
            //通过命名空间绑定mapper
            this.bindMapperForNamespace();
        }
    
        this.parsePendingResultMaps();
        this.parsePendingCacheRefs();
        this.parsePendingStatements();
    }
    

    因为这里的重点是想知道定义的接口是怎么实现的,所以直接来看看bindMapperForNamespace方法对如何来实现mapper的绑定:

    private void bindMapperForNamespace() {
        String namespace = this.builderAssistant.getCurrentNamespace();
        if (namespace != null) {
            Class boundType = null;
    
            try {
                //通过反射获取类类型
                boundType = Resources.classForName(namespace);
            } catch (ClassNotFoundException var4) {
                ;
            }
    
            //如果当前类还没有绑定到配置的缓存中
            if (boundType != null && !this.configuration.hasMapper(boundType)) {
                this.configuration.addLoadedResource("namespace:" + namespace);
                //通过mybatis的Configuration将mapper注册到MapperRegistry
                this.configuration.addMapper(boundType);
            }
        }
    }
    

    来看看MapperRegistry 的addMapper:

    public <T> void addMapper(Class<T> type) {
        this.mapperRegistry.addMapper(type);
    }
    
    public <T> void addMapper(Class<T> type) {
        if (type.isInterface()) {//先判断是否为接口
            if (this.hasMapper(type)) {//如果已经注册,抛出异常
                throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
            }
            boolean loadCompleted = false;
            try {
                //创建一个代理MapperProxyFactory 并将该mapper缓存到内存的Map中去
                this.knownMappers.put(type, new MapperProxyFactory(type));
                //绑定注解
                MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type);
                parser.parse();
                loadCompleted = true;
            } finally {
                if (!loadCompleted) {
                    this.knownMappers.remove(type);
                }
    
            }
        }
    }
    

    通过对上面源码的跟踪分析,终于知道,mybatis通过MapperProxyFactory为每个接口都提供了一个代理实现,然后通过MapperRegistry将mapper注册到容器中的。

    那么在使用的时候又是如何来获取这个mapper的呢?

    答案是:反射加上代理

    首先通过SqlSession的getMapper方法,mybatis为SqlSession提供了一个默认的实现DefaultSqlSession,DefaultSqlSession的内部属性如下:

    //Configuration配置
    private final Configuration configuration;
    private final Executor executor;
    private final boolean autoCommit;
    private boolean dirty;
    private List<Cursor<?>> cursorList;
    

    DefaultSqlSession获取配置,然后配置中获取MapperRegistry,从MapperRegistry中获取到mapper代理工厂,再通过MapperProxyFactory的newInstance来创建mapper代理类MapperProxy

    总结:对于Mybatis,记住几个关键的处理类:SqlSessionFactoryBuilder创建SqlSessionFactory,SqlSessionFactory中开启一个SqlSession,通过sqlSession来执行用户操作;RegisterMapper将mapper注册到容器中,通过MapperProxyFactory来创建mapper代理,mapper代理最终是通过jdk反射包中Proxy代理类来创建的。

    启动时创建的SqlSessionFactory是DefaultSqlSessionFactory,DefaultSqlSessionFactory中拥有属性Configuration,Configuration是通过XMLConfigBuilder从配置文件中解析出来的各种配置信息,Configuration中有MapperRegistry,MapperRegistry中用一个final类型的Map存储了用户定义Mapper代理实现工厂MapperProxyFactory,Mapper代理实现是MapperProxy。
  • 相关阅读:
    Atitit.ati orm的设计and架构总结 适用于java c# php版
    Atitit.ati dwr的原理and设计 attilax 总结 java php 版本
    Atitit.ati dwr的原理and设计 attilax 总结 java php 版本
    Atitit. 软件设计 模式 变量 方法 命名最佳实践 vp820 attilax总结命名表大全
    Atitit. 软件设计 模式 变量 方法 命名最佳实践 vp820 attilax总结命名表大全
    Atitit 插件机制原理与设计微内核 c# java 的实现attilax总结
    Atitit 插件机制原理与设计微内核 c# java 的实现attilax总结
    atitit.基于  Commons CLI 的命令行原理与 开发
    atitit.基于  Commons CLI 的命令行原理与 开发
    atitit.js 与c# java交互html5化的原理与总结.doc
  • 原文地址:https://www.cnblogs.com/canmeng-cn/p/9467821.html
Copyright © 2011-2022 走看看