在第一篇文章中应用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,应用第三个对象线程加载器。
//上面再介绍一下,这几个加载器门路区别