mybatis学习(3)---加载配置文件

根据上次的小Demo我们来看一下mybatis是如何加载配置文件的。

这是上次的测试成功运行的代码,把其他代码注释,留下创建SqlSessionFactory的代码

package cn.wtzvae.test;

import org.apache.ibatis.session.SqlSession;
import org.apache.ibatis.session.SqlSessionFactory;
import org.junit.Test;

import cn.wtzvae.dao.PersonDao;
import cn.wtzvae.model.Person;
import cn.wtzvae.util.SqlSessionFactoryUtil;

public class TestFactory {

    @Test
    public void test(){
        SqlSessionFactory sqlSessionFactory = SqlSessionFactoryUtil.getSqlSessionFactory();
//      SqlSession sqlSession = sqlSessionFactory.openSession();
//      sqlSession.getConnection();
//      PersonDao personDao =  sqlSession.getMapper(PersonDao.class);
//      Person p = new Person();
//      p.setAddress("广东省");
//      p.setAge(12);
//      p.setEmail("jinanvae@163.com");
//      p.setName("啊角");
//      p.setPhone("110");
//      personDao.insert(p);
//      sqlSession.commit();
//      sqlSession.close();
    }
}

在看一下SqlSessionFactoryUtil的部分代码。

public static SqlSessionFactory getSqlSessionFactory(){
        String path = "mybatis-config.xml";
        SqlSessionFactory sqlSessionFactory = null;
        try {
            Reader reader = Resources.getResourceAsReader(path);
//          sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
        } catch (IOException e) {
            System.out.println("获取配置文件失败");
            e.printStackTrace();
        }

        return sqlSessionFactory;
    }

其中的Resources类是mybatis自带的类,方法getResourceAsReader(String str);返回Reader。ctrl+左键跟踪一下这个方法,发现如下

public static Reader getResourceAsReader(String resource) throws IOException {
    Reader reader;
    if (charset == null) {
      reader = new InputStreamReader(getResourceAsStream(resource));
    } else {
      reader = new InputStreamReader(getResourceAsStream(resource), charset);
    }
    return reader;
  }

继续跟踪getResourceAsStream(String str); 最终跟踪到ClassLoaderWrapper类(可以用快捷键ctrl+shift+t搜索这个类,eclipse的快捷键)

InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
    for (ClassLoader cl : classLoader) {

      if (null != cl) {
          System.out.println(cl.getClass().getName());
        // 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) {
          System.out.println("resouce = "+resource);
          System.out.println(cl.getClass().getName()+"   load");
          return returnValue;
        }
      }
    }
    return null;
  }

上面的方法中三句输出语句是我自己加上的,其中的resource参数是我们自己传进来的,就是”mybatis-config.xml”这个文件名,可以看到上面的语句
InputStream returnValue = cl.getResourceAsStream(resource);
可以获得一个输入流,而这个输入流就是我们的mybatis-config.xml文件的流,得到这个输入流逐步返回给上一层方法,最后SqlSessionFactoryUtil类就能获得mybatis-config.xml文件的流。那现在我们就会好奇,cl.getResourceAsStream(resource); 它又是怎么获取到配置文件的呢。让我们再看这个方法的另外一个参数,ClassLoader[] classLoader。ClassLoader类是类加载器,我们先要了解一下这货是干嘛的。

ClassLoader类:
学java的都知道,java文件编译后会生成class文件。jvm在运行的时候会把class文件加载到jvm的内存方法区中,方法区保存所有class和static变量,为线程共享。而这些要把文件流加载进jvm内存的工作都是类加载器干的活,所以加载mybatis配置文件的工作自然也是ClassLoader的活。

ClassLoader分为三类,分别为Bootstrap ClassLoader、Extension ClassLoader和App ClassLoader。Bootstrap是最顶层的加载器,随jvm启动,负责构造Extension 和App加载器及加载jdk的核心类库,由c++编写。Extension为扩展加载器,加载java_home下的部分类。App系统加载器,加载项目classpath下的资源文件,mybatis的配置文件全由它加载进虚拟机。

回归正题,继续看ClassLoaderWrapper源码。看下面这段源码,ClassLoader[] classLoader参数是getClassLoaders(classLoader)方法返回值传进来的,查看这个方法的源码。

public InputStream getResourceAsStream(String resource, ClassLoader classLoader) {
    return getResourceAsStream(resource, getClassLoaders(classLoader));
  }

下面getClassLoaders(classLoader)方法,其中的systemClassLoader就是我们说的App系统加载类,写了一个输出语句,等会用于验证。

ClassLoader[] getClassLoaders(ClassLoader classLoader) {
      System.out.println("系统加载类:"+systemClassLoader.getClass().getName());
      return new ClassLoader[]{
        classLoader,
        defaultClassLoader,
        Thread.currentThread().getContextClassLoader(),
        getClass().getClassLoader(),
        systemClassLoader};
  }

准备运行测试类,在本次中我们注释了测试类和SqlSessionFactoryUtil中的部分代码,让程序只加载mybatis总配置文件。
运行测试类,输出如下:

系统加载类:sun.misc.Launcher$AppClassLoader
sun.misc.Launcher$AppClassLoader
resouce = mybatis-config.xml
sun.misc.Launcher$AppClassLoader load

输出语句在上面粘的代码里有,可以看出,mybatis配置文件AppClassLoader加载进来的。这里只是加载了配置文件,并没有进行相关验证,在mybatis-config.xml乱输入一些字符也可以运行。

//          sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);

配置文件的验证是有上面这行代码完成,只不过被我们注释了。现在去掉这行的注释,如果配置文件有错误字符是不能成功运行的。去掉这条注释和sysout(“系统加载类:”…)这条输出,运行输出如下:

sun.misc.Launcher$AppClassLoader

resouce = mybatis-config.xml

sun.misc.Launcher$AppClassLoader load

sun.misc.Launcher$AppClassLoader

resouce = org/apache/ibatis/builder/xml/mybatis-3-config.dtd
sun.misc.Launcher AppClassLoaderloadsun.misc.Launcher AppClassLoader
resouce = cn/wtzvae/mappers/PersonMapper.xml
sun.misc.Launcher AppClassLoaderloadsun.misc.Launcher AppClassLoader
resouce = org/apache/ibatis/builder/xml/mybatis-3-mapper.dtd
sun.misc.Launcher$AppClassLoader load

一共加载了四个配置文件。
//sqlSessionFactory = new SqlSessionFactoryBuilder().build(reader);
上面语句的执行过程先不深究,继续回到ClassLoaderWapper类的
InputStream getResourceAsStream(String resource, ClassLoader[] classLoader)方法。

方法里面的语句
InputStream returnValue = cl.getResourceAsStream(resource);

我们查看ClassLoad的getResourceAsStream方法,这个已经是jdk自带的类,不属于mybatis。

下面的这个是ClassLoad的getResourceAsStream方法,继续查看getResource方法

public InputStream getResourceAsStream(String name) {
        URL url = getResource(name);
        try {
            return url != null ? url.openStream() : null;
        } catch (IOException e) {
            return null;
        }
    }

可以看到这个方法存在递归调用,Bootstrap ClassLoader是最顶层的加载器,parent为空,先由它加载资源,没加载成功再由其他ClassLoader加载。

/**
     * Finds the resource with the given name.  A resource is some data
     * (images, audio, text, etc) that can be accessed by class code in a way
     * that is independent of the location of the code.
     *
     * <p> The name of a resource is a '<tt>/</tt>'-separated path name that
     * identifies the resource.
     *
     * <p> This method will first search the parent class loader for the
     * resource; if the parent is <tt>null</tt> the path of the class loader
     * built-in to the virtual machine is searched.  That failing, this method
     * will invoke {@link #findResource(String)} to find the resource.  </p>
     *
     * @param  name
     *         The resource name
     *
     * @return  A <tt>URL</tt> object for reading the resource, or
     *          <tt>null</tt> if the resource could not be found or the invoker
     *          doesn't have adequate  privileges to get the resource.
     *
     * @since  1.1
     */
    public URL getResource(String name) {
        URL url;
        if (parent != null) {
            url = parent.getResource(name);
        } else {
            url = getBootstrapResource(name);
        }
        if (url == null) {
            url = findResource(name);
        }
        return url;
    }

再看看jdk文档给getBootstrapResource和findResource方法的定义。

发现这个方法getBootstrapResource是私有的,jdk文档没有说明,这就尴尬了。从源码中有这么一句注释
/**
* Find resources from the VM’s built-in classloader.
*/

看完发觉也没啥卵用

/**
     * Finds the resource with the given name. Class loader implementations
     * should override this method to specify where to find resources.  </p>
     *
     * @param  name
     *         The resource name
     *
     * @return  A <tt>URL</tt> object for reading the resource, or
     *          <tt>null</tt> if the resource could not be found
     *
     * @since  1.2
     */
    protected URL findResource(String name) {
        return null;
    }

最后,认真写下来,感觉就对类加载器了解多了一些

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值