Mybatis源码分析三读取配置文件

34次阅读

共计 3039 个字符,预计需要花费 8 分钟才能阅读完成。

在第一篇文章中应用 Mybatis 代码如下:

@Test
    public void test() throws IOException {
        String resource = "file/mybatis-config.xml";
        // 读取配置文件
        InputStream inputStream = Resources.getResourceAsStream(resource);
        // 构建 sqlSessionFactory
        SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
        // 获取 sqlSession
        SqlSession sqlSession = sqlSessionFactory.openSession();

        // 构建参数
        Map map=new HashMap<>();
        map.put("id",1);
        User user=sqlSession.selectOne("last.soul.mapper.UserMapper.selectById",map);
        Assert.assertTrue(user.getId()==1);
    }

咱们来剖析一下 Resources.getResourceAsStream() 这个办法,查看代码外部。咱们发现这个办法次要是调用 ClassLoaderWrapper.getResourceAsStream() 来实现的,ClassLoaderWrapper 位于 Mybatis 源码的 io 包中,是一个 classLoader 的包装器。其中蕴含多个 classLoader 对象,通过调整加载器的程序确保返回正确的类加载器,从而加载正确的文件。

在介绍 ClassLoaderWrapper 之前先具体讲一下类加载器。类加载器的文章能够参考:http://blog.itpub.net/3156126…
ClassLoader 通常有以下几种:
A.this.getClass().getClassLoader(); // 以后类的 ClassLoader
B.Thread.currentThread().getContextClassLoader(); // 以后线程的 ClassLoader
C.ClassLoader.getSystemClassLoader(); // 应用零碎 ClassLoader,即零碎的入口点所应用的 ClassLoader

依据 ClassLoader 传递性,以后类会由初始调用 main 办法的这个 ClassLoader 全权负责,它就是 AppClassLoader。也就是说在没有指定加载器的时候,1 和 3 是同一个类加载器 AppClassLoader。2 中的线程加载器是为了突破双亲委托模型而设计的加载器,它能够做到跨线程共享类,只有它们共享同一个 contextClassLoader。父子线程之间会主动传递 contextClassLoader,所以共享起来将是自动化的。
如果不同的线程应用不同的 contextClassLoader,那么不同的线程应用的类就能够隔离开来。突破双亲委托机制的文章能够参考一下:https://blog.csdn.net/xiaobao…
contextClassLoader 在没有设置的状况下,默认应用的也是 AppClassLoader。

ClassLoaderWrapper 中依照性能分为三类,classForName() 办法,getResourceAsURL() 办法和 getResourceAsStream() 办法,它们最终调用的都是 ClassLoader 类中的 Class.forName(name, true, cl) 办法、getResourceAsStream() 办法以及 getResource() 办法。以 getResourceAsStream() 办法为例,源代码如下:

InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {for (ClassLoader cl : classLoader) {if (null != cl) {

        // try to find the resource as passed
        InputStream returnValue = cl.getResourceAsStream(resource);

        // now, some class loaders want this leading "/", so we'll add it and try again if we didn't find the resource
        if (null == returnValue) {returnValue = cl.getResourceAsStream("/" + resource);
        }

        if (null != returnValue) {return returnValue;}
      }
    }
    return null;
  }

它的次要逻辑就是遍历 ClassLoader 对象数组,尝试读取文件,只有读取胜利就返回,数组里的程序就是优先级。其它两个办法逻辑相似,那么 ClassLoader 对象数组的程序是如何定义的呢,这个逻辑在该类的另一个重要办法 getClassLoaders() 中,源代码如下:

ClassLoader[] getClassLoaders(ClassLoader classLoader) {return new ClassLoader[]{
        classLoader,
        defaultClassLoader,
        Thread.currentThread().getContextClassLoader(),
        getClass().getClassLoader(),
        systemClassLoader};
  }

联合类的申明和结构器代码:

  ClassLoader defaultClassLoader;
  ClassLoader systemClassLoader;

  ClassLoaderWrapper() {
    try {systemClassLoader = ClassLoader.getSystemClassLoader();
    } catch (SecurityException ignored) {// AccessControlException on Google App Engine}
  }

按数组程序,
1.classLoader 是参数指定的类加载器,
2.defaultClassLoader 就默认的类加载器,mybatis 并没有给初始值,
3.Thread.currentThread().getContextClassLoader() 是上文中的 B,线程加载器,
4.getClass().getClassLoader() 是上文中的 A,类加载器,
5.systemClassLoader 是上文中的 C,零碎加载器即零碎的入口点所应用的 ClassLoader, 是 AppClassLoader。
以文章结尾的代码来看,没有应用参数指定类加载器所以 1 的加载器为 null,mybatis 没有给初始 defaultClassLoader 也为 null, 所以 getResourceAsStream() 办法应用的是第三个类加载器,即线程加载器,因为应用了第 3 个加载器,第 4、5 个加载器将不会应用。成果如下图所示。

前两个对象都是 null,后三个对象都是 AppClassLoader,应用第三个对象线程加载器。

// 上面再介绍一下,这几个加载器门路区别

正文完
 0