mybatis源码分析二(看源码前的思考、源码入口)

一:先考虑几个问题

1:BlogMapper是个接口,接口不能实例化,如何直接调用selectBlog并返回

public interface BlogMapper {
    public Blog selectBlog(Long id);
}
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(1L);
mybatis通过jdk提供动态代理解决以上问题,例如:
BlogMapper b = (BlogMapper) Proxy.
        newProxyInstance(JdkProxy.class.getClassLoader(), new Class[]{BlogMapper.class}, new InvocationHandler() {
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        //在调用selectBlog方法的时候执行invoke
        if (method.getName().equals("selectBlog"))
            System.out.println(method.getName());
 return null; }
});
b.selectBlog(1l);

2:XML解析

解析xml的方式有多种,DOM,SAX,DOM4J,JDOM各有优劣。mybatis通过SAX解析xml,并构造需要的对象及功能,XML映射接口功能

二:源码入口

String resource = "mybatis-config.xml";
InputStream inputStream = Resources.getResourceAsStream(resource);
SqlSessionFactory sqlSessionFactory =
        new SqlSessionFactoryBuilder().build(inputStream);
SqlSession sqlSession = sqlSessionFactory.openSession();
BlogMapper mapper = sqlSession.getMapper(BlogMapper.class);
Blog blog = mapper.selectBlog(1L);

1:加载xml,ClassLoaderWrapper装饰模式增强ClassLoader功能

//mybatis封装Resources加载xml,实际上是调用classLoaderWrapper.getResourceAsStream
Resources.getResourceAsStream(resource);

//ClassLoaderWrapper##getResourceAsStream
public static InputStream getResourceAsStream(ClassLoader loader, String resource) throws IOException {
  InputStream in = classLoaderWrapper.getResourceAsStream(resource, loader);
 if (in == null) {
    throw new IOException("Could not find resource " + resource);
 }
  return in;
}

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

InputStream getResourceAsStream(String resource, ClassLoader[] classLoader) {
  for (ClassLoader cl : classLoader) {
    if (null != cl) {
        //加载资源
        InputStream returnValue = cl.getResourceAsStream(resource);
        if (null == returnValue) {
            //再次加载资源
            returnValue = cl.getResourceAsStream("/" + resource);
        }
        if (null != returnValue) {
            return returnValue;
        }
    }
 }
  return null;
}

//ClassLoaderWrapper获取多级类加载器加载xml
ClassLoader[] getClassLoaders(ClassLoader classLoader) {
  return new ClassLoader[]{
      //参数指定的类加载器
      classLoader,
 //系统指定的默认加载器
 defaultClassLoader,
 //当前线程的类加载器
 Thread.currentThread().getContextClassLoader(),
 //当前类使用的类加载器
 getClass().getClassLoader(),
 //JVM启动时就加载了类加载器
 systemClassLoader};
}

2:SqlSessionFactoryBuilder

SqlSessionFactoryBuilder主要构建SqlSessionFactory,SqlSessionFactory构建SqlSession,SqlSession主要对数据库增删改查进行了封装

image.png

上图基本上是mybatis的壳,为对外的接口类关系
看看build()方法中的细节
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
  try {
    //mybatis-config.xml 创建XMLConfigBuilderxml解析类
    XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
    //返回DefaultSqlSessionFactory##Configuration
    return build(parser.parse());
 } catch (Exception e) {
    throw ExceptionFactory.wrapException("Error building SqlSession.", e);
 } finally {
    ErrorContext.instance().reset();
 try {
      inputStream.close();
 } catch (IOException e) {
      // Intentionally ignore. Prefer previous error.
 }
}
}
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);

//XMLConfigBuilder为主要解析xml类,其中主要调用封装的XPathParser解析
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
  this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}

private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {
 //new Configuration()调用父类构造方法初始化一些值
 super(new Configuration());
 ErrorContext.instance().resource("SQL Mapper Configuration");
 this.configuration.setVariables(props);
 this.parsed = false;
 this.environment = environment;
 this.parser = parser;
}

public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
  commonConstructor(validation, variables, entityResolver);
  //createDocument根据xml构建文档对象
  this.document = createDocument(new InputSource(inputStream));
}

image.png

mybatis用建造者模式构造Configuration这个主要类,这个类主要包含数据源信息,别名缓存,mapper映射缓存等等,从super(new Configuration())开始构建Configuration,传递给其他子类构建Configuration其他属性
阅读 77

推荐阅读