通过Mybatis应用和源码学习-如何通过指定配置文件获取InputStream 这一章获取的InputStream对象传入到SqlSessionFactoryBuilder.build()方法中把InputStream对象代表的XML文件转化成Document对象,代码入口如下:
SqlSessionFactory sqlSessionFactory = new SqlSessionFactoryBuilder().build(inputStream);
SqlSessionFactoryBuilder 类解析
首先看下这个类的所有行为,可以看到这个类的主要行为就是通过build方法返回一个SqlSessionFactory对象,只是不同的方法重载而已,除了可以传入字节和字符流外,还可以传入一个Configuration对象来获取SqlSessionFactory对象。
因为我们的例子中传入的是InputStream参数,所以这里看下通过传入InputStream的Build方法,这个方法又只是执行了自己的一个重载方法,build(inputStream, null, null),
public SqlSessionFactory build(InputStream inputStream) {
return build(inputStream, null, null);
}
当上面方法执行的时候就进入到自己的重载方法build(inputStream, environment, properties),这个方法主要有两个逻辑处理,一个是利用XMLConfigBuilder这个类去把InputStream对象解析成Document对象封装到XMLConfigBuilder对象中,还有一个就是通过解析出来的XMLConfigBuilder对象解析Document对象中对应的XML节点,方法的后面两个参数environment,properties都是通过代码传入的我们没有传,所以这里都是null,代码如下:
public SqlSessionFactory build(InputStream inputStream, String environment, Properties properties) {
try {
//主要通过XMLConfigBuilder类去解析Config配置文件xml
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.
}
}
}
这里先看下 这行代码是如何把InputStream对象解析成Document对象的,至于节点的解析处理会在下一篇 Mybatis应用和源码学习-Document节点解析。
XMLConfigBuilder类解析
XMLConfigBuilder类主要作用就是Mybatis用来解析XML文件的其中有四个属性,environment 指定使用哪个环境,parsed 是否已经解析过了,parser XML解析对象,localReflectorFactory 反射工厂。
/**
* 用来标记这个配置类是否已经解析过了
*/
private boolean parsed;
/**
* XPath解析器类
*/
private final XPathParser parser;
/**
* environment 属性
*/
private String environment;
/**
* 反射工厂,
*/
private final ReflectorFactory localReflectorFactory = new DefaultReflectorFactory();
然后还有很多的辅助解析Document节点的方法,这一篇主要先解析一下两个构造函数,其余的在后续篇章中解析,下面代码是两个构造函数的代码:
public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) {
/**
* 先获得XMLMapperEntityResolver() config和Mapper xml对象的解析器
* 在传入XpathParser解析器去解析xml文件
*/
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对象把InputStream流代表的XML对象解析成Document对象,然后把Document对象封装到自己的属性中,再执行自己的第二个重载的构造函数来给相关的属性赋值,其中主要的逻辑会去新建一个Configuration对象先初始化这个对象的默认构造函数中的初始化操作,然后调用自己的父类BaseBuilder的构造方法,把Configuration,typeAliasRegistry,typeHandlerRegistry三个属性初始化。
Configuration 类
在执行new Configuration()的时候会初始化以下这些属性。
public Configuration() {
typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);
typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);
typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);
typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);
typeAliasRegistry.registerAlias("PERPETUAL", PerpetualCache.class);
typeAliasRegistry.registerAlias("FIFO", FifoCache.class);
typeAliasRegistry.registerAlias("LRU", LruCache.class);
typeAliasRegistry.registerAlias("SOFT", SoftCache.class);
typeAliasRegistry.registerAlias("WEAK", WeakCache.class);
typeAliasRegistry.registerAlias("DB_VENDOR", VendorDatabaseIdProvider.class);
typeAliasRegistry.registerAlias("XML", XMLLanguageDriver.class);
typeAliasRegistry.registerAlias("RAW", RawLanguageDriver.class);
typeAliasRegistry.registerAlias("SLF4J", Slf4jImpl.class);
typeAliasRegistry.registerAlias("COMMONS_LOGGING", JakartaCommonsLoggingImpl.class);
typeAliasRegistry.registerAlias("LOG4J", Log4jImpl.class);
typeAliasRegistry.registerAlias("LOG4J2", Log4j2Impl.class);
typeAliasRegistry.registerAlias("JDK_LOGGING", Jdk14LoggingImpl.class);
typeAliasRegistry.registerAlias("STDOUT_LOGGING", StdOutImpl.class);
typeAliasRegistry.registerAlias("NO_LOGGING", NoLoggingImpl.class);
typeAliasRegistry.registerAlias("CGLIB", CglibProxyFactory.class);
typeAliasRegistry.registerAlias("JAVASSIST", JavassistProxyFactory.class);
languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);
languageRegistry.register(RawLanguageDriver.class);
}
XPathParser 类解析XML
这个类主要是用来解析XML文件的,然后组装成Document对象,有几个属性的含义如下,把XML文件转成Document对象的docuement属性,一个是否需要校验的标识 validation ,一个实体转换处理类 entityResolver,一个属性对象 variables,最后一个解析XML的xpath对象
在执行new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()) 这个方法的时候会传入一个实体转换解析类XMLMapperEntityResolver,然后进入下面这个方法执行相关后续操作:
public XPathParser(InputStream inputStream, boolean validation, Properties variables, EntityResolver entityResolver) {
//这个方法主要是对几个属性赋值
commonConstructor(validation, variables, entityResolver);
//把inputStream对象封装成java中的inputSource对象
this.document = createDocument(new InputSource(inputStream));
}
上述的commonConstructor(validation, variables, entityResolver);方法没有做上面重要的工作,只是初始化了一些属性,其中一个属性xpath对象是利用JAVA API实例化的。
private void commonConstructor(boolean validation, Properties variables, EntityResolver entityResolver) {
this.validation = validation;
this.entityResolver = entityResolver;
this.variables = variables;
//这个工厂方法最后返回的是new XPathFactoryImpl()对象工厂实现
XPathFactory factory = XPathFactory.newInstance();
//根据上面的工厂返回一个xpath对象
this.xpath = factory.newXPath();
}
主要来看下createDocument(new InputSource(inputStream));这个方法,因为XML的处理过程在这个方法中执行,这里主要是按照JAVA API解析XML的要求组装对象,然后调用JAVA DocuementBuilder类提供的API解析XML,返回一个Document对象
private Document createDocument(InputSource inputSource) {
// important: this must only be called AFTER common constructor
try {
//这里主要是按照JAVA API解析XML的要求组装对象,然后调用JAVA DocuementBuilder类提供的API解析XML,返回一个Document对象
DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setFeature(XMLConstants.FEATURE_SECURE_PROCESSING, true);
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 {
// NOP
}
});
return builder.parse(inputSource);
} catch (Exception e) {
throw new BuilderException("Error creating document instance. Cause: " + e, e);
}
}
BaseBuilder 类解析
这个类主要作用就是通过默认构造函数来处理以下这几个属性。
/**
* Configuration类,主要映射mybatis配置xml里面所有的内容
*/
protected final Configuration configuration;
/**
* 别名解析注册类
*/
protected final TypeAliasRegistry typeAliasRegistry;
/**
* 类型处理注册类
*/
protected final TypeHandlerRegistry typeHandlerRegistry;
/**
* 在构造函数里面给三个变量赋值
* @param configuration
*/
public BaseBuilder(Configuration configuration) {
this.configuration = configuration;
this.typeAliasRegistry = this.configuration.getTypeAliasRegistry();
this.typeHandlerRegistry = this.configuration.getTypeHandlerRegistry();
}