这篇文章中,我们将讲解配置文件中 properties,typeAliases,settings和environments这些节点的解析过程。

一 properties的解析

 private void propertiesElement(XNode context) throws Exception {
if (context != null) {
//解析properties的子节点,并将这些子节点内容转为属性对象Properties
Properties defaults = context.getChildrenAsProperties();
//获取properties节点中resource的属性值
String resource = context.getStringAttribute("resource");
//获取properties节点中url的属性值
String url = context.getStringAttribute("url");
//resource和url不能同时存在
if (resource != null && url != null) {
throw new BuilderException("The properties element cannot specify both a URL and a resource based property file reference. Please specify one or the other.");
}
if (resource != null) {
// 从文件系统中加载并解析属性文件,此时会覆盖在xml中配置的properties子节点的同名key-value
defaults.putAll(Resources.getResourceAsProperties(resource));
} else if (url != null) {
// 从url中解析并加载属性文件
defaults.putAll(Resources.getUrlAsProperties(url));
}
// 获取configuration中已经定义的属性对象Properties
Properties vars = configuration.getVariables();
if (vars != null) {
// configuration中的key-value会覆盖上面两种情况中的key-value
defaults.putAll(vars);
}
// 将解析出的内容set到parser中
parser.setVariables(defaults);
// 将解析出的内容set到configuration中,configuration会装载所解析的配置文件中所有的节点内容,后面会使用到这个对象
configuration.setVariables(defaults);
}
}

二 settings的解析

先看下settings的配置,下面只是settings配置中的一部分:

<!-- settings是 MyBatis 中极为重要的调整设置,它们会改变 MyBatis 的运行时行为。 -->
<settings>
<!-- 该配置影响的所有映射器中配置的缓存的全局开关。默认值true -->
<setting name="cacheEnabled" value="true"/>
<!--延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。 特定关联关系中可通过设置fetchType属性来覆盖该项的开关状态。默认值false -->
<setting name="lazyLoadingEnabled" value="true"/>
<!-- 是否允许单一语句返回多结果集(需要兼容驱动)。 默认值true -->
<setting name="multipleResultSetsEnabled" value="true"/>
</settings>

源码部分:

 private void settingsElement(XNode context) throws Exception {
if (context != null) {
// 获取settings节点下所有的子节点信息,然后封装成Properties对象
Properties props = context.getChildrenAsProperties();
// Check that all settings are known to the configuration class
// 获取Configuration的元信息对象
MetaClass metaConfig = MetaClass.forClass(Configuration.class);
for (Object key : props.keySet()) {
// 检测Configuration中是否存在相关的属性,如果不存在,那么抛出异常
if (!metaConfig.hasSetter(String.valueOf(key))) {
throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive).");
}
}
//以下是把获取到的setting信息封装到Configuration中,所以才需要上面的检测
configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL")));
configuration.setCacheEnabled(booleanValueOf(props.getProperty("cacheEnabled"), true));
configuration.setProxyFactory((ProxyFactory) createInstance(props.getProperty("proxyFactory")));
configuration.setLazyLoadingEnabled(booleanValueOf(props.getProperty("lazyLoadingEnabled"), false));
configuration.setAggressiveLazyLoading(booleanValueOf(props.getProperty("aggressiveLazyLoading"), true));
configuration.setMultipleResultSetsEnabled(booleanValueOf(props.getProperty("multipleResultSetsEnabled"), true));
configuration.setUseColumnLabel(booleanValueOf(props.getProperty("useColumnLabel"), true));
configuration.setUseGeneratedKeys(booleanValueOf(props.getProperty("useGeneratedKeys"), false));
configuration.setDefaultExecutorType(ExecutorType.valueOf(props.getProperty("defaultExecutorType", "SIMPLE")));
configuration.setDefaultStatementTimeout(integerValueOf(props.getProperty("defaultStatementTimeout"), null));
configuration.setMapUnderscoreToCamelCase(booleanValueOf(props.getProperty("mapUnderscoreToCamelCase"), false));
configuration.setSafeRowBoundsEnabled(booleanValueOf(props.getProperty("safeRowBoundsEnabled"), false));
configuration.setLocalCacheScope(LocalCacheScope.valueOf(props.getProperty("localCacheScope", "SESSION")));
configuration.setJdbcTypeForNull(JdbcType.valueOf(props.getProperty("jdbcTypeForNull", "OTHER")));
configuration.setLazyLoadTriggerMethods(stringSetValueOf(props.getProperty("lazyLoadTriggerMethods"), "equals,clone,hashCode,toString"));
configuration.setSafeResultHandlerEnabled(booleanValueOf(props.getProperty("safeResultHandlerEnabled"), true));
configuration.setDefaultScriptingLanguage(resolveClass(props.getProperty("defaultScriptingLanguage")));
configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false));
configuration.setLogPrefix(props.getProperty("logPrefix"));
configuration.setLogImpl(resolveClass(props.getProperty("logImpl")));
configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory")));
}
}

二 typeAliases的解析

先看下在配置文件中的写法:

    <typeAliases>
<typeAlias alias="goods" type="com.yht.mybatisTest.entity.Goods"/>
</typeAliases>

源码部分:

 private void typeAliasesElement(XNode parent) {
if (parent != null) {
// 循环处理typeAliases下所有的子节点
for (XNode child : parent.getChildren()) {
// 这是针对子节点是package的处理
if ("package".equals(child.getName())) {
String typeAliasPackage = child.getStringAttribute("name");
configuration.getTypeAliasRegistry().registerAliases(typeAliasPackage);
} else {
// 如果子节点是typeAlias
String alias = child.getStringAttribute("alias"); //获取alias的属性值
String type = child.getStringAttribute("type"); //获取type的属性值
try {
// 获取type对应的类型
Class<?> clazz = Resources.classForName(type);
if (alias == null) {
// 注册别名到类型的映射 进入该方法
typeAliasRegistry.registerAlias(clazz);
} else {
typeAliasRegistry.registerAlias(alias, clazz);
}
} catch (ClassNotFoundException e) {
throw new BuilderException("Error registering typeAlias for '" + alias + "'. Cause: " + e, e);
}
}
}
}
}

进入registerAlias方法:

  public void registerAlias(Class<?> type) {
// 如果在配置文件中没有配置alias属性,这里会获取type的类名
String alias = type.getSimpleName();
// 获取注解上的别名
Alias aliasAnnotation = type.getAnnotation(Alias.class);
if (aliasAnnotation != null) {
alias = aliasAnnotation.value();
}
// 进入此方法
registerAlias(alias, type);
}
  public void registerAlias(String alias, Class<?> value) {
if (alias == null) throw new TypeException("The parameter alias cannot be null");
// 将别名转为小写
String key = alias.toLowerCase(Locale.ENGLISH); // issue #748
// 如果TYPE_ALIASES已经存在该别名,并且对应的类型不为空,同时已经存在的类型不等于将要注册的类型value,那么抛出异常
if (TYPE_ALIASES.containsKey(key) && TYPE_ALIASES.get(key) != null && !TYPE_ALIASES.get(key).equals(value)) {
throw new TypeException("The alias '" + alias + "' is already mapped to the value '" + TYPE_ALIASES.get(key).getName() + "'.");
}
// 将别名和对应的全限定名放入到HashMap中,TYPE——ALIASES是一个HashMap
TYPE_ALIASES.put(key, value);
}
接着看下Mybatis内部常见别名注册:
 private final Map<String, Class<?>> TYPE_ALIASES = new HashMap<String, Class<?>>();

  public TypeAliasRegistry() {
registerAlias("string", String.class); registerAlias("byte", Byte.class);
registerAlias("long", Long.class);
registerAlias("short", Short.class);
registerAlias("int", Integer.class);
registerAlias("integer", Integer.class);
registerAlias("double", Double.class);
registerAlias("float", Float.class);
registerAlias("boolean", Boolean.class); registerAlias("byte[]", Byte[].class);
registerAlias("long[]", Long[].class);
registerAlias("short[]", Short[].class);
registerAlias("int[]", Integer[].class);
registerAlias("integer[]", Integer[].class);
registerAlias("double[]", Double[].class);
registerAlias("float[]", Float[].class);
registerAlias("boolean[]", Boolean[].class); registerAlias("_byte", byte.class);
registerAlias("_long", long.class);
registerAlias("_short", short.class);
registerAlias("_int", int.class);
registerAlias("_integer", int.class);
registerAlias("_double", double.class);
registerAlias("_float", float.class);
registerAlias("_boolean", boolean.class); registerAlias("_byte[]", byte[].class);
registerAlias("_long[]", long[].class);
registerAlias("_short[]", short[].class);
registerAlias("_int[]", int[].class);
registerAlias("_integer[]", int[].class);
registerAlias("_double[]", double[].class);
registerAlias("_float[]", float[].class);
registerAlias("_boolean[]", boolean[].class); registerAlias("date", Date.class);
registerAlias("decimal", BigDecimal.class);
registerAlias("bigdecimal", BigDecimal.class);
registerAlias("biginteger", BigInteger.class);
registerAlias("object", Object.class); registerAlias("date[]", Date[].class);
registerAlias("decimal[]", BigDecimal[].class);
registerAlias("bigdecimal[]", BigDecimal[].class);
registerAlias("biginteger[]", BigInteger[].class);
registerAlias("object[]", Object[].class); registerAlias("map", Map.class);
registerAlias("hashmap", HashMap.class);
registerAlias("list", List.class);
registerAlias("arraylist", ArrayList.class);
registerAlias("collection", Collection.class);
registerAlias("iterator", Iterator.class); registerAlias("ResultSet", ResultSet.class);
}

四 environments的解析

在mybatis中事务管理器和数据源是在environments中配置的,配置如下:

    <environments default="development">
<environment id="development">
<transactionManager type="JDBC" />
<dataSource type="POOLED">
<property name="driver" value="${driver}" />
<property name="url" value="${url}" />
<property name="username" value="${username}" />
<property name="password" value="${password}" />
</dataSource>
</environment>
</environments>

对应的源码如下:

  private void environmentsElement(XNode context) throws Exception {
if (context != null) {
if (environment == null) {
// 获取environments中default的属性值
environment = context.getStringAttribute("default");
}
for (XNode child : context.getChildren()) {
// 获取id的属性
String id = child.getStringAttribute("id");
// 判断子节点id的属性和父节点environments的default属性是否相同,相同返回true,否则返回false
if (isSpecifiedEnvironment(id)) {
// 解析transactionManager节点
TransactionFactory txFactory = transactionManagerElement(child.evalNode("transactionManager"));
// 解析DataSource节点
DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));
// 获取DataSource对象
DataSource dataSource = dsFactory.getDataSource();
Environment.Builder environmentBuilder = new Environment.Builder(id)
.transactionFactory(txFactory)
.dataSource(dataSource);
// 构建environment对象并设置到configuration中
configuration.setEnvironment(environmentBuilder.build());
}
}
}
}

现在还有一个问题值得思考:对于<property name="driver" value="${driver}" /> 中,value值是如何被赋值的?接下里,我们跟踪源码进行分析:

进入XMLConfigBuilder类的environmentsElement方法,有这么一行代码: DataSourceFactory dsFactory = dataSourceElement(child.evalNode("dataSource"));是解析DataSource节点的,上述的过程就是在这个方法中发生的,进入该方法:

  private DataSourceFactory dataSourceElement(XNode context) throws Exception {
if (context != null) {
// 获取DataSource的type属性
String type = context.getStringAttribute("type");
// 解析DataSource所有的子节点信息并封装为属性对象Properties,进入该方法
Properties props = context.getChildrenAsProperties();
DataSourceFactory factory = (DataSourceFactory) resolveClass(type).newInstance();
factory.setProperties(props);
return factory;
}
throw new BuilderException("Environment declaration requires a DataSourceFactory.");
}

进入context.getChildrenAsProperties();方法:

  public Properties getChildrenAsProperties() {
Properties properties = new Properties();
//进入getChildren()方法
for (XNode child : getChildren()) {
String name = child.getStringAttribute("name");
String value = child.getStringAttribute("value");
if (name != null && value != null) {
properties.setProperty(name, value);
}
}
return properties;
} public List<XNode> getChildren() {
List<XNode> children = new ArrayList<XNode>();
NodeList nodeList = node.getChildNodes();
if (nodeList != null) {
for (int i = 0, n = nodeList.getLength(); i < n; i++) {
Node node = nodeList.item(i);
if (node.getNodeType() == Node.ELEMENT_NODE) {
// 对DataSource中所有的子节点构建成XNode对象,并放到children集合中,进入这个构造方法
children.add(new XNode(xpathParser, node, variables));
}
}
}
return children;
}
// 这个是DataSource的子节点的构造方法,具体的说就是把 <property name="driver" value="${driver}" /> 构造成一个XNode节点
public XNode(XPathParser xpathParser, Node node, Properties variables) {
this.xpathParser = xpathParser;
this.node = node;
this.name = node.getNodeName();
this.variables = variables;
//value赋值过程在这个方法中
this.attributes = parseAttributes(node);
this.body = parseBody(node);
}
  private Properties parseAttributes(Node n) {
Properties attributes = new Properties();
// 获取子节点的所有属性 如:[name="driver", value="${driver}"]
NamedNodeMap attributeNodes = n.getAttributes();
if (attributeNodes != null) {
for (int i = 0; i < attributeNodes.getLength(); i++) {
Node attribute = attributeNodes.item(i);
// 对${dirver}的解析赋值在parse方法中
String value = PropertyParser.parse(attribute.getNodeValue(), variables);
attributes.put(attribute.getNodeName(), value);
}
}
return attributes;
}
// 根据varibles中已经存储的值,对给定的${driver},在varibles中找到driver对应的真实值,并进行替换
public static String parse(String string, Properties variables) {
VariableTokenHandler handler = new VariableTokenHandler(variables);
GenericTokenParser parser = new GenericTokenParser("${", "}", handler);
return parser.parse(string);
}

五 TypeHandler的解析

当向数据库中存储或者取出数据时,需要将数据库中的字段类型和java类型做一个转换,比如从数据库中取出的CHAR类型,转换为java中的String类型,这个功能就委托给类型处理器TypeHandler来处理。

mybatis已经提供了一些常见的类型处理器,如StringTypeHandler,ArrayTypeHandler,LongTypeHandler等,能够满足大多数的开发需求。对于某些特殊的需求,我们也可以自定义类型处理器,需要继承

BaseTypeHandler这个抽象类。下面我们自定义一个MyStringTypeHandler类型处理器,用于扩展StringTypeHandler的功能:

package com.yht.mybatisTest.typeHandlers;

import java.sql.CallableStatement;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException; import org.apache.ibatis.type.BaseTypeHandler;
import org.apache.ibatis.type.JdbcType; /**
* @author chenyk
* @date 2018年8月22日
*/ public class MyStringTypeHandler extends BaseTypeHandler<String>{ @Override
public void setNonNullParameter(PreparedStatement ps, int i, String parameter, JdbcType jdbcType)
throws SQLException {
System.out.println("新增逻辑");
ps.setString(i, parameter);
} @Override
public String getNullableResult(ResultSet rs, String columnName)
throws SQLException {
System.out.println("新增逻辑");
return rs.getString(columnName);
} @Override
public String getNullableResult(ResultSet rs, int columnIndex)
throws SQLException {
System.out.println("新增逻辑");
return rs.getString(columnIndex);
} @Override
public String getNullableResult(CallableStatement cs, int columnIndex)
throws SQLException {
System.out.println("新增逻辑");
return cs.getString(columnIndex);
} }

类型处理器在配置文件中有两种配置方法:

    <!-- 手动配置 -->
<typeHandlers>
<typeHandler jdbcType="CHAR" javaType="String" handler="com.yht.mybatisTest.typeHandlers.MyStringTypeHandler" />
</typeHandlers> <!-- 自动扫描 -->
<typeHandlers>
<package name="com.yht.mybatisTest.typeHandlers"/>
</typeHandlers>

看源码部分:

private void typeHandlerElement(XNode parent) throws Exception {
if (parent != null) {
for (XNode child : parent.getChildren()) {
if ("package".equals(child.getName())) {
// 扫描指定的包 然后注册TypeHandler
String typeHandlerPackage = child.getStringAttribute("name");
typeHandlerRegistry.register(typeHandlerPackage);
} else {
// 获取javaType属性值,jdbcType属性值,handler属性值,然后注册TypeHandler
String javaTypeName = child.getStringAttribute("javaType");
String jdbcTypeName = child.getStringAttribute("jdbcType");
String handlerTypeName = child.getStringAttribute("handler");
Class<?> javaTypeClass = resolveClass(javaTypeName);
JdbcType jdbcType = resolveJdbcType(jdbcTypeName);
Class<?> typeHandlerClass = resolveClass(handlerTypeName);
if (javaTypeClass != null) {
if (jdbcType == null) {
typeHandlerRegistry.register(javaTypeClass, typeHandlerClass);
} else {
typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);
}
} else {
typeHandlerRegistry.register(typeHandlerClass);
}
}
}
}
}

根据不同的情况,注册TypeHandler共有四种方法,这里我们选一种进行分析:typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass);

  private void register(Type javaType, JdbcType jdbcType, TypeHandler<?> handler) {
if (javaType != null) {
Map<JdbcType, TypeHandler<?>> map = TYPE_HANDLER_MAP.get(javaType);
if (map == null) {
map = new HashMap<JdbcType, TypeHandler<?>>();
// 存储JavaType到Map<JdbcType,TypeHandler>的映射
TYPE_HANDLER_MAP.put(javaType, map);
}
map.put(jdbcType, handler);
if (reversePrimitiveMap.containsKey(javaType)) {
register(reversePrimitiveMap.get(javaType), jdbcType, handler);
}
}
//存储所有的TypeHandler
ALL_TYPE_HANDLERS_MAP.put(handler.getClass(), handler);
}

mybatis内置的TypeHandler有哪些呢?贴出源码:

 private final Map<JdbcType, TypeHandler<?>> JDBC_TYPE_HANDLER_MAP = new EnumMap<JdbcType, TypeHandler<?>>(JdbcType.class);
private final Map<Type, Map<JdbcType, TypeHandler<?>>> TYPE_HANDLER_MAP = new HashMap<Type, Map<JdbcType, TypeHandler<?>>>();
private final TypeHandler<Object> UNKNOWN_TYPE_HANDLER = new UnknownTypeHandler(this);
private final Map<Class<?>, TypeHandler<?>> ALL_TYPE_HANDLERS_MAP = new HashMap<Class<?>, TypeHandler<?>>(); public TypeHandlerRegistry() {
register(Boolean.class, new BooleanTypeHandler());
register(boolean.class, new BooleanTypeHandler());
register(JdbcType.BOOLEAN, new BooleanTypeHandler());
register(JdbcType.BIT, new BooleanTypeHandler()); register(Byte.class, new ByteTypeHandler());
register(byte.class, new ByteTypeHandler());
register(JdbcType.TINYINT, new ByteTypeHandler()); register(Short.class, new ShortTypeHandler());
register(short.class, new ShortTypeHandler());
register(JdbcType.SMALLINT, new ShortTypeHandler()); register(Integer.class, new IntegerTypeHandler());
register(int.class, new IntegerTypeHandler());
register(JdbcType.INTEGER, new IntegerTypeHandler()); register(Long.class, new LongTypeHandler());
register(long.class, new LongTypeHandler()); register(Float.class, new FloatTypeHandler());
register(float.class, new FloatTypeHandler());
register(JdbcType.FLOAT, new FloatTypeHandler()); register(Double.class, new DoubleTypeHandler());
register(double.class, new DoubleTypeHandler());
register(JdbcType.DOUBLE, new DoubleTypeHandler()); register(String.class, new StringTypeHandler());
register(String.class, JdbcType.CHAR, new StringTypeHandler());
register(String.class, JdbcType.CLOB, new ClobTypeHandler());
register(String.class, JdbcType.VARCHAR, new StringTypeHandler());
register(String.class, JdbcType.LONGVARCHAR, new ClobTypeHandler());
register(String.class, JdbcType.NVARCHAR, new NStringTypeHandler());
register(String.class, JdbcType.NCHAR, new NStringTypeHandler());
register(String.class, JdbcType.NCLOB, new NClobTypeHandler());
register(JdbcType.CHAR, new StringTypeHandler());
register(JdbcType.VARCHAR, new StringTypeHandler());
register(JdbcType.CLOB, new ClobTypeHandler());
register(JdbcType.LONGVARCHAR, new ClobTypeHandler());
register(JdbcType.NVARCHAR, new NStringTypeHandler());
register(JdbcType.NCHAR, new NStringTypeHandler());
register(JdbcType.NCLOB, new NClobTypeHandler()); register(Object.class, JdbcType.ARRAY, new ArrayTypeHandler());
register(JdbcType.ARRAY, new ArrayTypeHandler()); register(BigInteger.class, new BigIntegerTypeHandler());
register(JdbcType.BIGINT, new LongTypeHandler()); register(BigDecimal.class, new BigDecimalTypeHandler());
register(JdbcType.REAL, new BigDecimalTypeHandler());
register(JdbcType.DECIMAL, new BigDecimalTypeHandler());
register(JdbcType.NUMERIC, new BigDecimalTypeHandler()); register(Byte[].class, new ByteObjectArrayTypeHandler());
register(Byte[].class, JdbcType.BLOB, new BlobByteObjectArrayTypeHandler());
register(Byte[].class, JdbcType.LONGVARBINARY, new BlobByteObjectArrayTypeHandler());
register(byte[].class, new ByteArrayTypeHandler());
register(byte[].class, JdbcType.BLOB, new BlobTypeHandler());
register(byte[].class, JdbcType.LONGVARBINARY, new BlobTypeHandler());
register(JdbcType.LONGVARBINARY, new BlobTypeHandler());
register(JdbcType.BLOB, new BlobTypeHandler()); register(Object.class, UNKNOWN_TYPE_HANDLER);
register(Object.class, JdbcType.OTHER, UNKNOWN_TYPE_HANDLER);
register(JdbcType.OTHER, UNKNOWN_TYPE_HANDLER); register(Date.class, new DateTypeHandler());
register(Date.class, JdbcType.DATE, new DateOnlyTypeHandler());
register(Date.class, JdbcType.TIME, new TimeOnlyTypeHandler());
register(JdbcType.TIMESTAMP, new DateTypeHandler());
register(JdbcType.DATE, new DateOnlyTypeHandler());
register(JdbcType.TIME, new TimeOnlyTypeHandler()); register(java.sql.Date.class, new SqlDateTypeHandler());
register(java.sql.Time.class, new SqlTimeTypeHandler());
register(java.sql.Timestamp.class, new SqlTimestampTypeHandler()); // issue #273
register(Character.class, new CharacterTypeHandler());
register(char.class, new CharacterTypeHandler());
}

到现在为止,对于配置文件中常见节点的解析过程做了分析,接下来在下一篇文章中我们继续对映射文件的解析过程进行讲解。

mybatis源码分析(二)------------配置文件的解析的更多相关文章

  1. Heritrix源码分析(二) 配置文件order.xml介绍(转)

    本博客属原创文章,欢迎转载!转载请务必注明出处:http://guoyunsky.iteye.com/blog/613412      本博客已迁移到本人独立博客: http://www.yun5u. ...

  2. mybatis源码分析二

    这次分析mybatis的xml文件 1. <?xml version="1.0" encoding="UTF-8" ?> <configura ...

  3. MyBatis源码分析(二)

    MyBatis的xml配置(核心配置) configuration(配置) properties(属性) settings(设置) typeAliases(类型别名) typeHandlers(类型处 ...

  4. Mybatis源码分析之Mapper文件解析

    感觉CSDN对markdown的支持不够友好,总是伴随各种问题,很恼火! xxMapper.xml的解析主要由XMLMapperBuilder类完成,parse方法来完成解析: public void ...

  5. mybatis 源码分析二

    1.SqlSession下的四大对象 Executor.StatementHandler.ParameterHandler.ResultSetHandler StatementHandler的作用是使 ...

  6. MyBatis 源码分析 - 配置文件解析过程

    * 本文速览 由于本篇文章篇幅比较大,所以这里拿出一节对本文进行快速概括.本篇文章对 MyBatis 配置文件中常用配置的解析过程进行了较为详细的介绍和分析,包括但不限于settings,typeAl ...

  7. 精尽MyBatis源码分析 - MyBatis初始化(二)之加载Mapper接口与XML映射文件

    该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...

  8. MyBatis 源码分析 - 映射文件解析过程

    1.简介 在上一篇文章中,我详细分析了 MyBatis 配置文件的解析过程.由于上一篇文章的篇幅比较大,加之映射文件解析过程也比较复杂的原因.所以我将映射文件解析过程的分析内容从上一篇文章中抽取出来, ...

  9. (一) Mybatis源码分析-解析器模块

    Mybatis源码分析-解析器模块 原创-转载请说明出处 1. 解析器模块的作用 对XPath进行封装,为mybatis-config.xml配置文件以及映射文件提供支持 为处理动态 SQL 语句中的 ...

  10. 精尽MyBatis源码分析 - SQL执行过程(二)之 StatementHandler

    该系列文档是本人在学习 Mybatis 的源码过程中总结下来的,可能对读者不太友好,请结合我的源码注释(Mybatis源码分析 GitHub 地址.Mybatis-Spring 源码分析 GitHub ...

随机推荐

  1. docker1.13.1的安装与卸载及mysql5.5安装实例

    docker中国官方地址:https://www.docker-cn.com/ 您可以使用以下命令直接从该镜像加速地址进行拉取: $ docker pull registry.docker-cn.co ...

  2. MySQL高级知识(十六)——小表驱动大表

    前言:本来小表驱动大表的知识应该在前面就讲解的,但是由于之前并没有学习数据批量插入,因此将其放在这里.在查询的优化中永远小表驱动大表. 1.为什么要小表驱动大表呢 类似循环嵌套 for(int i=5 ...

  3. Scrapy 框架 总结

    总结: 1.中间件:下载中间件(拦截请求和响应) - process_request: - prceess_response: - process_exception: - 请求: - UA伪装: - ...

  4. Python:Day05 格式化输出、列表

    注释:3个单引号或3个双引号 3个引号(单引或双引)还有另外一个作用:打印多行. msg = """hello 1 hello 2 hello 3"" ...

  5. wxWidgets 在 Windows 下开发环境配置

    本文基于 CodeBlocks (16.01) 和 wxWidgets (3.0.2) 搭建 Windows 环境下 GUI 开发环境. 1.  CodeBlocks 官网,下载最新版安装包 code ...

  6. Spring Security(二十):6.2.3 Form and Basic Login Options

    You might be wondering where the login form came from when you were prompted to log in, since we mad ...

  7. freopen

    一定要记住哇 求求你了 记住吧 freopen("balabala.in","r",stdin); freopen("balabala.out&quo ...

  8. SimpleDialogBox

    import 'package:flutter/material.dart';import 'dart:ui';import 'dart:async';enum Option{A,B,C}void m ...

  9. 跨平台Redis可视化工具Web Redis Manager

    一.简介 最近因为工作需要,使用了一些单机版Redis的界面化管理工具,使用过程中那惨痛的体验真的只有用过的人才能体会:为此本人和小伙伴准备动手一个Redis可视化工具,但是因为小伙伴最近工作比较忙, ...

  10. 在ASP.NET Core MVC中子类Controller拦截器要先于父类Controller拦截器执行

    我们知道在ASP.NET Core MVC中Controller上的Filter拦截器是有执行顺序的,那么如果我们在有继承关系的两个Controller类上,声明同一种类型的Filter拦截器,那么是 ...