• 关于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。
  • 相关阅读:
    P4049 [JSOI2007]合金
    CF1073C Vasya and Robot
    输出100以内奇数,偶数,质数,合数的脚本
    取/etc/password文件最后一个单词的最后一个字符
    window下进程退出后自动重启
    如何让DOS命令在新窗口打开
    dos命令关闭所有dos窗口
    使用jps查看JVM进程信息
    windows .bat批处理实现进程监控确保程序运行
    经典博客4(六尺帐篷)
  • 原文地址:https://www.cnblogs.com/canmeng-cn/p/9467821.html
Copyright © 2020-2023  润新知