mybatis资源加载
上一篇文章介绍了搭建mybatis源码分析的工程,在测试工程里面看到,在进行数据库操作前,必须先加载mybatis的相关资源。
在start方法里面,首先通过Resources.getResourceAsStream(“myBatis-config.xml”)方法,读取mybatis的全局配置,接着通过SqlSessionFactoryBuilder().build(inputStream)方法生成一个SqlSessionFactory,最后SqlSessionFactory调用openSession()方法生成SqlSession。
public void start(){
try{
//读取myBatis全局配置文件信息把数据源、mapper映射文件等配置信息读取出来
InputStream inputStream = Resources.getResourceAsStream("myBatis-config.xml");
//通过SqlSessionFactoryBuilder生成SqlSessionFactory
SqlSessionFactory factory=new SqlSessionFactoryBuilder().build(inputStream);
//生成SqlSession
session=factory.openSession();
}catch(Exception exception){
exception.printStackTrace();
}
}
- SqlSessionFactoryBuilder
在SqlSessionFactoryBuilder里面主要有3种方式构造SqlSessionFactory,第一种是通过InputStream构造,第二种是通过Reader构造,第三种是直接传入org.apache.ibatis.session.Configuration,三种构造方式没有本质上的区别,前面两种是通过类似配置文件的方式构造,第三种方式是先构造了配置类,然后通过配置类构造SqlSessionFactory,前面两种方法最后面在真正执行。
public class SqlSessionFactoryBuilder {
public SqlSessionFactory build(Reader reader) {
return build(reader, null, null);
}
public SqlSessionFactory build(Reader reader, String environment) {
return build(reader, environment, null);
}
public SqlSessionFactory build(Reader reader, Properties properties) {
return build(reader, null, properties);
}
public SqlSessionFactory build(Reader reader, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);
return build(parser.parse());
} catch (Exception e) {
throw ExceptionFactory.wrapException("Error building SqlSession.", e);
} finally {
ErrorContext.instance().reset();
try {
reader.close();
} catch (IOException e) {
// Intentionally ignore. Prefer previous error.
}
}
}
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
public SqlSessionFactory build(InputStream inputStream, String environment) {
return build(inputStream, environment, null);
}
public SqlSessionFactory build(InputStream inputStream, Properties properties) {
return build(inputStream, null, properties);
}
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
XMLConfigBuilder parser = new XMLConfigBuilder(inputStream, environment, properties);
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.
}
}
}
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
- XMLConfigBuilder,在SqlSessionFactoryBuilder中,首先创建XMLConfigBuilder对象,这个对象是专门解析MyBatis配置类用的,构造函数如下:
public XMLConfigBuilder(Reader reader, String environment, Properties props) {
this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);
}
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props);
}
从上面可以看出,其实不管是传入的是Reader还是传入的是InputStream,两者都是用来创建XPathParser对象的,因此正如上面说的,传入两者没什么区别。刚才的测试类的start方法中,创建SqlSessionFactoryBuilder的时候,在执行build方法的时候,只传进去了InputStream因此这里的environment和props两个参数都是空
SqlSessionFactory factory=new SqlSessionFactoryBuilder().build(inputStream);
在创建XMLConfigBuilder的时候,先创建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) {
super(new Configuration());
ErrorContext.instance().resource("SQL Mapper Configuration");
this.configuration.setVariables(props);
this.parsed = false;
this.environment = environment;
this.parser = parser;
}
在创建XPathParser的时候,传入4个参数配置文件的流,true,props以及新创建的XMLMapperEntityResolver对象
public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
commonConstructor(validation, variables, entityResolver);
this.document = createDocument(new InputSource(inputStream));
}
private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {
this.validation = validation;
this.entityResolver = entityResolver;
this.variables = variables;
XPathFactory factory = XPathFactory.newInstance();
this.xpath = factory.newXPath();
}
private Document createDocument(InputSource inputSource) {
// important: this must only be called AFTER common constructor
try {
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(validation);
factory.setNamespaceAware(false);
factory.setIgnoringComments(true);
factory.setIgnoringElementContentWhitespace(false);
factory.setCoalescing(false);
factory.setExpandEntityReferences(true);
DocumentBuilder builder = factory.newDocumentBuilder();
builder.setEntityResolver(entityResolver);
builder.setErrorHandler(new ErrorHandler() {
@Override
public void error(SAXParseException exception) throws SAXException {
throw exception;
}
@Override
public void fatalError(SAXParseException exception) throws SAXException {
throw exception;
}
@Override
public void warning(SAXParseException exception) throws SAXException {
}
});
return builder.parse(inputSource);
} catch (Exception e) {
throw new BuilderException("Error creating document instance. Cause: " + e, e);
}
}
从上面的代码可以看到,在SqlSessionFactoryBuilder创建XMLConfigBuilder的时候,只是把配置文件初始化到对象里面,此时还没对配置文件进行解析。接着,执行return build(parser.parse());代码对配置文件进行解析。
在parser.parse()方法中,即调用的是XMLConfigBuilder的parse()方法
public Configuration parse() {
if (parsed) {
throw new BuilderException("Each XMLConfigBuilder can only be used once.");
}
parsed = true;
parseConfiguration(parser.evalNode("/configuration"));
return configuration;
}
在parse方法里面通过调用parseConfiguration(parser.evalNode("/configuration"))方法初始化配置信息,这里传入的参数是配置文件里面configuration这个节点的信息。