Spring集成Mybatis源码分析(三)-配置文件初始化及XML解析
程序员文章站
2022-05-23 16:23:50
...
启动入口
首先我们还是看启动入口,一般情况下涉及到远程访问的,都需要拿到Session,mybatis-spring提供了SqlSessionFactoryBean,将该工厂bean注入到容器中,在Spring启动过程中会去调用该类的的afterPropertiesSet方法和getObject方法获取session
@Autowired
@Value("classpath*:com/test/mapper/**/*.xml")
private Resource[] mapperLocations;
@Bean
public SqlSessionFactoryBean sessionFactoryBean(DataSource dataSource) {
SqlSessionFactoryBean sessionFactory = new SqlSessionFactoryBean();
sessionFactory.setDataSource(multipleDataSource);
sessionFactory.setMapperLocations(mapperLocations);
return sessionFactory;
}
下面来看SqlSessionFactoryBean的afterPropertiesSet方法,这个方法在属性注入后才会执行,该方法直接调用了buildSqlSessionFactory方法
public void afterPropertiesSet() throws Exception {
// 校验空
notNull(dataSource, "Property 'dataSource' is required");
notNull(sqlSessionFactoryBuilder, "Property 'sqlSessionFactoryBuilder' is required");
this.sqlSessionFactory = buildSqlSessionFactory();
}
buildSqlSessionFactory方法比较长, 大致做了以下这些事,后面我们一个一个分析
- 创建mybatis配置对象,如果指定了XML配置文件,则利用XMLConfigBuilder对象创建Configuration对象,如果没有指定XML配置文件,那么久创建一个默认的Configuration对象
- 设置创建实例对象的工厂,可以决定如何创建对象
- 设置实例包装的工厂,可以通过该Factory改变返回实例的值等
- 注册别名
- 添加插件
- 添加类型转换器
- 解析Mybatis XML配置文件
- 添加事务工厂
- 设置数据库ID
- 解析Mapper XML
主流程分析
- 创建XMLConfigBuilder以获取Configuration对象或新建Configuration对象
- 在该类中创建一个Configuration对象
- 创建一个XPathParser对象,用来后续解析XML文件
- 传入new SqlSessionFactoryBean时指定的Properties
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;
}
- 设置对象工厂和对象包装工厂
if (this.objectFactory != null) {
configuration.setObjectFactory(this.objectFactory);
}
if (this.objectWrapperFactory != null) {
configuration.setObjectWrapperFactory(this.objectWrapperFactory);
}
- 如果不指定,默认为DefaultObjectFactory,DefaultObjectWrapperFactory
protected ObjectFactory objectFactory = new DefaultObjectFactory();
protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory();
- 别名注册
if (hasLength(this.typeAliasesPackage)) {
String[] typeAliasPackageArray = tokenizeToStringArray(this.typeAliasesPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
for (String packageToScan : typeAliasPackageArray) {
configuration.getTypeAliasRegistry().registerAliases(packageToScan,
typeAliasesSuperType == null ? Object.class : typeAliasesSuperType);
if (logger.isDebugEnabled()) {
logger.debug("Scanned package: '" + packageToScan + "' for aliases");
}
}
}
if (!isEmpty(this.typeAliases)) {
for (Class<?> typeAlias : this.typeAliases) {
configuration.getTypeAliasRegistry().registerAlias(typeAlias);
if (logger.isDebugEnabled()) {
logger.debug("Registered type alias: '" + typeAlias + "'");
}
}
}
- 包名别名指定字符串,用,; \t\n分隔,这些包下的接口都会注册别名
- 接口别名默认注册未类的接口名,也可以在接口上加@Alias指定别名
- 默认采用TypeAliasRegistry注册器注册别名,在实例化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);
- 添加插件
- 添加实现了Interceptor接口的插件
- 添加到Configuration属性的InterceptorChain下
if (!isEmpty(this.plugins)) {
for (Interceptor plugin : this.plugins) {
configuration.addInterceptor(plugin);
if (logger.isDebugEnabled()) {
logger.debug("Registered plugin: '" + plugin + "'");
}
}
}
- 添加类型处理器
- 同别名注册器查不到,提供了包或者执行接口的类型处理器
if (hasLength(this.typeHandlersPackage)) {
String[] typeHandlersPackageArray = tokenizeToStringArray(this.typeHandlersPackage,
ConfigurableApplicationContext.CONFIG_LOCATION_DELIMITERS);
for (String packageToScan : typeHandlersPackageArray) {
configuration.getTypeHandlerRegistry().register(packageToScan);
if (logger.isDebugEnabled()) {
logger.debug("Scanned package: '" + packageToScan + "' for type handlers");
}
}
}
if (!isEmpty(this.typeHandlers)) {
for (TypeHandler<?> typeHandler : this.typeHandlers) {
configuration.getTypeHandlerRegistry().register(typeHandler);
if (logger.isDebugEnabled()) {
logger.debug("Registered type handler: '" + typeHandler + "'");
}
}
}
- 默认实现类是protected final TypeHandlerRegistry typeHandlerRegistry = new TypeHandlerRegistry();默认实现了如下类型处理器
- 实现自定义的TypeHandler需要实现该接口,并加上注解@MappedTypes用来表明哪些类型需要使用该TypeHandler转换,或者继承TypeReference通过泛型自动识别类型转换
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());
}
- 解析XML配置
- 如果指定了Mybatis的配置文件,那么就需要解析
if (xmlConfigBuilder != null) {
try {
// 如果
xmlConfigBuilder.parse();
if (logger.isDebugEnabled()) {
logger.debug("Parsed configuration file: '" + this.configLocation + "'");
}
} catch (Exception ex) {
throw new NestedIOException("Failed to parse config resource: " + this.configLocation, ex);
} finally {
ErrorContext.instance().reset();
}
}
- 解析的主要流程和上面提到的流程差不多,主要读取各个节点的信息,然后将各种信息赋值到Configuration对象上
private void parseConfiguration(XNode root) {
try {
//issue #117 read properties first
// 加载配置文件,本地resource或者远程url
propertiesElement(root.evalNode("properties"));
// 别名
typeAliasesElement(root.evalNode("typeAliases"));
// 插件
pluginElement(root.evalNode("plugins"));
// 对象实例化及包装工厂
objectFactoryElement(root.evalNode("objectFactory"));
objectWrapperFactoryElement(root.evalNode("objectWrapperFactory"));
// 反射工厂
reflectionFactoryElement(root.evalNode("reflectionFactory"));
// 设置选项
settingsElement(root.evalNode("settings"));
// read it after objectFactory and objectWrapperFactory issue #631
// 环境
environmentsElement(root.evalNode("environments"));
// 数据库ID提供者
databaseIdProviderElement(root.evalNode("databaseIdProvider"));
// 类型处理器
typeHandlerElement(root.evalNode("typeHandlers"));
// mapper
mapperElement(root.evalNode("mappers"));
} catch (Exception e) {
throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e);
}
}
- 其中settings的配置包括如下, 也就是把设置的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.setDefaultFetchSize(integerValueOf(props.getProperty("defaultFetchSize"), 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")));
- 创建事务工厂
- 没有指定则使用Mybatis实现的SpringManagedTransactionFactory
if (this.transactionFactory == null) {
this.transactionFactory = new SpringManagedTransactionFactory();
}
- 为当前Configuration设置DatabaseId
- 当我们有多个数据源的时候我们可以new 多个SqlSessionFactoryBean,并为其指定DatabaseIdProvider,该provider可以返回一个String类型的databaseId,可以实现数据库分离
if (this.databaseIdProvider != null) {
try {
configuration.setDatabaseId(this.databaseIdProvider.getDatabaseId(this.dataSource));
} catch (SQLException e) {
throw new NestedIOException("Failed getting a databaseId", e);
}
}
- 解析Mapper XML文件
- 本人认为这个解析是整个XML解析中最重要的,另外,不知道大家有没有注意到,在之前的xmlConfigBuilder.parse();里面也有mapperElement(root.evalNode(“mappers”));方法,这个方式最终的逻辑其实和这里一直,即新建XMLMapperBuilder,然后调用它的parse方法,一般在使用mabatis和Spring集成时,我比较倾向与使用sqlSessionFactoryBean.setMapperLocations方法指定Mapper Xml的位置,而mybatis的XML一般很少使用
- 此处会遍历所有mapperLocations位置下的文件
if (!isEmpty(this.mapperLocations)) {
for (Resource mapperLocation : this.mapperLocations) {
if (mapperLocation == null) {
continue;
}
try {
XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
configuration, mapperLocation.toString(), configuration.getSqlFragments());
xmlMapperBuilder.parse();
} catch (Exception e) {
throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
} finally {
ErrorContext.instance().reset();
}
if (logger.isDebugEnabled()) {
logger.debug("Parsed mapper file: '" + mapperLocation + "'");
}
}
} else {
if (logger.isDebugEnabled()) {
logger.debug("Property 'mapperLocations' was not specified or no matching resources found");
}
}
- 那么我们现在来看下这个XMLMapperBuilder的parse方法
public void parse() {
//首先判断当前资源是否已经被加载过,主要时根据resource的全路径名称是否集合中来判断
if (!configuration.isResourceLoaded(resource)) {
//然后开始解析mapper节点
configurationElement(parser.evalNode("/mapper"));
//标识为已解析过
configuration.addLoadedResource(resource);
//绑定命名空间
bindMapperForNamespace();
}
// 转换未完成的ResultMap
parsePendingResultMaps();
// 转换之前未处理成功的缓存引用
parsePendingChacheRefs();
parsePendingStatements();
}
- 继续分析configurationElement(parser.evalNode("/mapper"))方法
private void configurationElement(XNode context) {
try {
String namespace = context.getStringAttribute("namespace");
if (namespace == null || namespace.equals("")) {
throw new BuilderException("Mapper's namespace cannot be empty");
}
builderAssistant.setCurrentNamespace(namespace);
cacheRefElement(context.evalNode("cache-ref"));
cacheElement(context.evalNode("cache"));
parameterMapElement(context.evalNodes("/mapper/parameterMap"));
resultMapElements(context.evalNodes("/mapper/resultMap"));
sqlElement(context.evalNodes("/mapper/sql"));
buildStatementFromContext(context.evalNodes("select|insert|update|delete"));
} catch (Exception e) {
throw new BuilderException("Error parsing Mapper XML. Cause: " + e, e);
}
}
- cacheRefElement:解析缓存引用
- cacheElement:解析缓存配置
- 第一步:读取<cache>上的属性
private void cacheElement(XNode context) throws Exception {
if (context != null) {
// 缓存的类型,默认PerpetualCache
String type = context.getStringAttribute("type", "PERPETUAL");
Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);
// 剔除策略,默认LruCache
String eviction = context.getStringAttribute("eviction", "LRU");
Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);
// 刷新间隔
Long flushInterval = context.getLongAttribute("flushInterval");
// 缓存数量
Integer size = context.getIntAttribute("size");
// 可读可写
boolean readWrite = !context.getBooleanAttribute("readOnly", false);
// 阻塞
boolean blocking = context.getBooleanAttribute("blocking", false);
// 其他属性
Properties props = context.getChildrenAsProperties();
builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);
}
}
- 第二步:然后调用useNewCache方法新建Cache并添加一个Cache到Configuration,具体做了哪些动作参考下面的代码
public Cache useNewCache(Class<? extends Cache> typeClass,
Class<? extends Cache> evictionClass,
Long flushInterval,
Integer size,
boolean readWrite,
boolean blocking,
Properties props) {
typeClass = valueOrDefault(typeClass, PerpetualCache.class);
evictionClass = valueOrDefault(evictionClass, LruCache.class);
// 建造者模式
Cache cache = new CacheBuilder(currentNamespace)
.implementation(typeClass)
.addDecorator(evictionClass)
.clearInterval(flushInterval)
.size(size)
.readWrite(readWrite)
.blocking(blocking)
.properties(props)
.build();
configuration.addCache(cache);
currentCache = cache;
return cache;
}
// 以下代码未CacheBuilder的build方法
public Cache build() {
setDefaultImplementations();
// id为当前命名空间即currentNamespace,默认新建了PerpetualCache
Cache cache = newBaseCacheInstance(implementation, id);
// 为某些Cache实现设置属性
setCacheProperties(cache);
// issue #352, do not apply decorators to custom caches
// 为PerpetualCache添加装饰器模式,如默认的LruCache,他有一个参数为Cache的构造器,将PerpetualCache传进去
if (PerpetualCache.class.equals(cache.getClass())) {
for (Class<? extends Cache> decorator : decorators) {
cache = newCacheDecoratorInstance(decorator, cache);
setCacheProperties(cache);
}
// 继续装饰缓存,我把这段代码copy到这里
/**
try {
MetaObject metaCache = SystemMetaObject.forObject(cache);
// 根据<cache>中的size设置能缓存的条数
if (size != null && metaCache.hasSetter("size")) {
metaCache.setValue("size", size);
}
// 如果clearInterval不为空,装饰一个ScheduledCache
if (clearInterval != null) {
cache = new ScheduledCache(cache);
((ScheduledCache) cache).setClearInterval(clearInterval);
}
// 装饰SerializedCache,写缓存时序列化后的字节数组,读缓存从字节码中反序列化
if (readWrite) {
cache = new SerializedCache(cache);
}
// 装饰日志功能,dubug级别下可打印缓存命中率
cache = new LoggingCache(cache);
// 装饰同步功能,读写都加了synchonized,读写同步
cache = new SynchronizedCache(cache);
// 装饰阻塞功能,获取缓存的时候不允许写入,锁粒度为namespace + 方法名 + 参数值
if (blocking) {
cache = new BlockingCache(cache);
}
return cache;
} catch (Exception e) {
throw new CacheException("Error building standard cache decorators. Cause: " + e, e);
}
*/
cache = setStandardDecorators(cache);
// 如果不是PerpetualCache,也不是LoggingCache,只装饰一个LoggingCache
} else if (!LoggingCache.class.isAssignableFrom(cache.getClass())) {
cache = new LoggingCache(cache);
}
return cache;
}
- parameterMapElement:解析参数配置
- 解析<parameterMap>节点
private void parameterMapElement(List<XNode> list) throws Exception {
for (XNode parameterMapNode : list) {
String id = parameterMapNode.getStringAttribute("id");
String type = parameterMapNode.getStringAttribute("type");
// java类
Class<?> parameterClass = resolveClass(type);
List<XNode> parameterNodes = parameterMapNode.evalNodes("parameter");
List<ParameterMapping> parameterMappings = new ArrayList<ParameterMapping>();
// 获取各种参数映射
for (XNode parameterNode : parameterNodes) {
String property = parameterNode.getStringAttribute("property");
String javaType = parameterNode.getStringAttribute("javaType");
String jdbcType = parameterNode.getStringAttribute("jdbcType");
String resultMap = parameterNode.getStringAttribute("resultMap");
String mode = parameterNode.getStringAttribute("mode");
String typeHandler = parameterNode.getStringAttribute("typeHandler");
Integer numericScale = parameterNode.getIntAttribute("numericScale");
// 参数类型,IN/OUT/INOUT
ParameterMode modeEnum = resolveParameterMode(mode);
// java参数类型
Class<?> javaTypeClass = resolveClass(javaType);
// jdbcType类型
JdbcType jdbcTypeEnum = resolveJdbcType(jdbcType);
@SuppressWarnings("unchecked")
// 类型处理器
Class<? extends TypeHandler<?>> typeHandlerClass = (Class<? extends TypeHandler<?>>) resolveClass(typeHandler);
ParameterMapping parameterMapping = builderAssistant.buildParameterMapping(parameterClass, property, javaTypeClass, jdbcTypeEnum, resultMap, modeEnum, typeHandlerClass, numericScale);
parameterMappings.add(parameterMapping);
}
// 将上面得出的所有parameterMapping加入到configuration中的map中,以currentNamespace + "." + <parameterMap>的id属性作为key
builderAssistant.addParameterMap(id, parameterClass, parameterMappings);
}
}
// 该方法在MapperBuilderAssistant中
public ParameterMapping buildParameterMapping(
Class<?> parameterType,
String property,
Class<?> javaType,
JdbcType jdbcType,
String resultMap,
ParameterMode parameterMode,
Class<? extends TypeHandler<?>> typeHandler,
Integer numericScale) {
// 指明resultMap的命名空间
resultMap = applyCurrentNamespace(resultMap, true);
// Class parameterType = parameterMapBuilder.type();
// 如果javaType指定了,返回javaType,否则推断javaType
// 如果是JdbcType.CURSOR,那么jdbcType=ResultSet
// 否则如果<parameterMap>指定的type为Map,jabcType=Object
// 否则根据type和property获取get方法的返回类型
// 否则为Object
Class<?> javaTypeClass = resolveParameterJavaType(parameterType, property, javaType, jdbcType);
// 从初始化的TypeHandlerRegistry中获取类型处理器或者自定义类型处理器
TypeHandler<?> typeHandlerInstance = resolveTypeHandler(javaTypeClass, typeHandler);
// 又是建造者模式,返回一个ParameterMapping实例
ParameterMapping.Builder builder = new ParameterMapping.Builder(configuration, property, javaTypeClass);
builder.jdbcType(jdbcType);
builder.resultMapId(resultMap);
builder.mode(parameterMode);
builder.numericScale(numericScale);
builder.typeHandler(typeHandlerInstance);
return builder.build();
}
- resultMapElements:解析结果集配置
- 流程基本与ParameterMapping差不多,但是这里有一个地方有区别,即extends,resultMap可以继承自另一个resultMap,而由于解析是根据XML文件由上至下一个一个解析,因此如果前面解析出来的ResultMap是依赖于其后面的ResultMap,那么此时它就无法得到完整的ResultMap,因此需要等整个文件解析完成后再来处理这种继承关系。
private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception {
ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());
// 获取到id
String id = resultMapNode.getStringAttribute("id",
resultMapNode.getValueBasedIdentifier());
// 获取到type
String type = resultMapNode.getStringAttribute("type",
resultMapNode.getStringAttribute("ofType",
resultMapNode.getStringAttribute("resultType",
resultMapNode.getStringAttribute("javaType"))));
String extend = resultMapNode.getStringAttribute("extends");
// 自动映射
Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");
Class<?> typeClass = resolveClass(type);
Discriminator discriminator = null;
List<ResultMapping> resultMappings = new ArrayList<ResultMapping>();
resultMappings.addAll(additionalResultMappings);
List<XNode> resultChildren = resultMapNode.getChildren();
for (XNode resultChild : resultChildren) {
if ("constructor".equals(resultChild.getName())) {
processConstructorElement(resultChild, typeClass, resultMappings);
} else if ("discriminator".equals(resultChild.getName())) {
discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);
} else {
List<ResultFlag> flags = new ArrayList<ResultFlag>();
// 如果是<id>节点,打上标识
if ("id".equals(resultChild.getName())) {
flags.add(ResultFlag.ID);
}
// 构建resultMapping,这里就是采用MapperBuilderAssistant的buildResultMapping方法根据节点上的属性,构建一个resultMapping对象
resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));
}
}
// 创建一个ResultMap解决器,如果本次失败,捕获异常,会丢到未完成的ResultMap中,后续处理
ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);
try {
// 解析完成后添加到Configuration中,key和parmeterMap的格式一样
return resultMapResolver.resolve();
} catch (IncompleteElementException e) {
configuration.addIncompleteResultMap(resultMapResolver);
throw e;
}
}
- sqlElement:解析sql片段
- 这段比较简单,就是把整个<sql>添加到sqlFragments中,后续在buildStatement时如果有引用该sql,那么把它的内容拼接进去
private void sqlElement(List<XNode> list, String requiredDatabaseId) throws Exception {
for (XNode context : list) {
String databaseId = context.getStringAttribute("databaseId");
String id = context.getStringAttribute("id");
id = builderAssistant.applyCurrentNamespace(id, false);
if (databaseIdMatchesCurrent(id, databaseId, requiredDatabaseId)) {
sqlFragments.put(id, context);
}
}
}
- buildStatementFromContext:解析各种增删改查
- 从这个方法跟进来
public void parseStatementNode() {
String id = context.getStringAttribute("id");
String databaseId = context.getStringAttribute("databaseId");
if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {
return;
}
Integer fetchSize = context.getIntAttribute("fetchSize");
Integer timeout = context.getIntAttribute("timeout");
String parameterMap = context.getStringAttribute("parameterMap");
String parameterType = context.getStringAttribute("parameterType");
Class<?> parameterTypeClass = resolveClass(parameterType);
String resultMap = context.getStringAttribute("resultMap");
String resultType = context.getStringAttribute("resultType");
String lang = context.getStringAttribute("lang");
LanguageDriver langDriver = getLanguageDriver(lang);
Class<?> resultTypeClass = resolveClass(resultType);
String resultSetType = context.getStringAttribute("resultSetType");
StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));
ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);
String nodeName = context.getNode().getNodeName();
SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);
boolean useCache = context.getBooleanAttribute("useCache", isSelect);
boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);
// Include Fragments before parsing
// 将include替换成sqlFragments中的内容,主要逻辑是遍历判断当前节点,如果是include,就去sqlFagments中查找对应的,
// 如果不是,递归判断子节点,直到所有节点都是Attribute节点或者是Text节点为止
XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);
includeParser.applyIncludes(context.getNode());
// Parse selectKey after includes and remove them.
// 解析selectKey,会生成一个单独的statement存储在Configuration中,key设置为引入<selectKey>的标签的namespace + "." + id + "!selectKey"
// 同时生成一个SelectKeyGenerator的对象存储在Configuration中
// 最后把<selectKey>节点删除
processSelectKeyNodes(id, parameterTypeClass, langDriver);
// Parse the SQL (pre: <selectKey> and <include> were parsed and removed)
SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);
String resultSets = context.getStringAttribute("resultSets");
String keyProperty = context.getStringAttribute("keyProperty");
String keyColumn = context.getStringAttribute("keyColumn");
KeyGenerator keyGenerator;
String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;
keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);
// 如果在节点下定义了selectKey,那么就是使用自己定义的,如果没有定义,并且mybatis配置文件中配置了useGeneratedKeys,并且当前节点为INSERT
// 那么就是用Jdbc3KeyGenerator,否则为NoKeyGenerator,即不使用键生成器
if (configuration.hasKeyGenerator(keyStatementId)) {
keyGenerator = configuration.getKeyGenerator(keyStatementId);
} else {
keyGenerator = context.getBooleanAttribute("useGeneratedKeys",
configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))
? new Jdbc3KeyGenerator() : new NoKeyGenerator();
}
// 根据各种配置为statement绑定cache,paremeterMap, resultMap以及各种属性
// 最后添加到Configuration的map属性中
builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,
fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,
resultSetTypeEnum, flushCache, useCache, resultOrdered,
keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);
}
- 到这类XML格式定义的Mapper就已经解析完成了,我们回到XMLMapperBuilder的parse方法,再来继续看bindMapperForNamespace方法
- 根据XML中定义的namespace,找到对应的接口,然后调用configuration.addMapper方法
private void bindMapperForNamespace() {
String namespace = builderAssistant.getCurrentNamespace();
if (namespace != null) {
Class<?> boundType = null;
try {
boundType = Resources.classForName(namespace);
} catch (ClassNotFoundException e) {
//ignore, bound type is not required
}
if (boundType != null) {
if (!configuration.hasMapper(boundType)) {
// Spring may not know the real resource name so we set a flag
// to prevent loading again this resource from the mapper interface
// look at MapperAnnotationBuilder#loadXmlResource
configuration.addLoadedResource("namespace:" + namespace);
configuration.addMapper(boundType);
}
}
}
}
- 然后调用到了mapperRegistry的addMapper方法
public <T> void addMapper(Class<T> type) {
// 必须是接口
if (type.isInterface()) {
// 不能有重复
if (hasMapper(type)) {
throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
}
boolean loadCompleted = false;
try {
// 定义一个MapperProxyFactory,这个mapperProxyFactory可以创建出MapperProxy,及JDK动态代理对象
knownMappers.put(type, new MapperProxyFactory<T>(type));
// 然后开始解析接口中注解
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
parser.parse();
loadCompleted = true;
} finally {
if (!loadCompleted) {
knownMappers.remove(type);
}
}
}
}
- parse方法
public void parse() {
String resource = type.toString();
// 判断接口是否解析过,如果是从前面的流程一路走下来的,这里肯定不会被加载过,而下面的loadXmlResource()方法中的!configuration.isResourceLoaded("namespace:" + type.getName()这个判断就一定会返回false,
// 因为我们在bindMapperForNamespace方法中调用了configuration.addLoadedResource("namespace:" + namespace);方法,因此不会再去加载XML文件了
if (!configuration.isResourceLoaded(resource)) {
loadXmlResource();
configuration.addLoadedResource(resource);
assistant.setCurrentNamespace(type.getName());
// 缓存注解解析@CacheNamespace @CacheNamespaceRef,这里不会出现XML哪里的未完成状态,因为是直接指定的对象,不存在解析先后顺序的问题
parseCache();
parseCacheRef();
Method[] methods = type.getMethods();
for (Method method : methods) {
try {
// issue #237
if (!method.isBridge()) {
// 这里的解析和XML差不多,只不过是把XML中的配置搬到了注解中
parseStatement(method);
}
} catch (IncompleteElementException e) {
configuration.addIncompleteMethod(new MethodResolver(this, method));
}
}
}
parsePendingMethods();
}
- parseStatement
void parseStatement(Method method) {
// 找到参数类型,如果不是RowBound和ResultHandler,如果只有一个参数,就返回该参数的类型,如果是多个就返回ParamMap
Class<?> parameterTypeClass = getParameterType(method);
LanguageDriver languageDriver = getLanguageDriver(method);
// 从方法上读取Sql
SqlSource sqlSource = getSqlSourceFromAnnotations(method, parameterTypeClass, languageDriver);
if (sqlSource != null) {
// 读取可选配置
Options options = method.getAnnotation(Options.class);
// 设置默认值
final String mappedStatementId = type.getName() + "." + method.getName();
Integer fetchSize = null;
Integer timeout = null;
StatementType statementType = StatementType.PREPARED;
ResultSetType resultSetType = ResultSetType.FORWARD_ONLY;
SqlCommandType sqlCommandType = getSqlCommandType(method);
boolean isSelect = sqlCommandType == SqlCommandType.SELECT;
// 非select语句默认刷新缓存
boolean flushCache = !isSelect;
// select语句使用缓存
boolean useCache = isSelect;
KeyGenerator keyGenerator;
String keyProperty = "id";
String keyColumn = null;
// 如果是insert或者update语句,读取SelectKey注解
if (SqlCommandType.INSERT.equals(sqlCommandType) || SqlCommandType.UPDATE.equals(sqlCommandType)) {
// first check for SelectKey annotation - that overrides everything else
SelectKey selectKey = method.getAnnotation(SelectKey.class);
// 如果标注有SlectKey注解,读取该注解,然后和XML一样注册一个MappedStatement,和一个SelectKeyGenerator
if (selectKey != null) {
keyGenerator = handleSelectKeyAnnotation(selectKey, mappedStatementId, getParameterType(method), languageDriver);
keyProperty = selectKey.keyProperty();
} else if (options == null) {
// 如果mybatis配置了useGenerateKeys,默认使用Jdbc3KeyGenerator,否则不适用KeyGenerator
keyGenerator = configuration.isUseGeneratedKeys() ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
} else {
// 如果可选配置上配置了useGeneratedKeys,同上
keyGenerator = options.useGeneratedKeys() ? new Jdbc3KeyGenerator() : new NoKeyGenerator();
keyProperty = options.keyProperty();
keyColumn = options.keyColumn();
}
} else {
keyGenerator = new NoKeyGenerator();
}
// 可选配置覆盖默认配置
if (options != null) {
flushCache = options.flushCache();
useCache = options.useCache();
fetchSize = options.fetchSize() > -1 || options.fetchSize() == Integer.MIN_VALUE ? options.fetchSize() : null; //issue #348
timeout = options.timeout() > -1 ? options.timeout() : null;
statementType = options.statementType();
resultSetType = options.resultSetType();
}
// 构建resultMapId
String resultMapId = null;
ResultMap resultMapAnnotation = method.getAnnotation(ResultMap.class);
if (resultMapAnnotation != null) {
String[] resultMaps = resultMapAnnotation.value();
StringBuilder sb = new StringBuilder();
for (String resultMap : resultMaps) {
if (sb.length() > 0) {
sb.append(",");
}
sb.append(resultMap);
}
resultMapId = sb.toString();
} else if (isSelect) {
resultMapId = parseResultMap(method);
}
// 添加MappedStatement
assistant.addMappedStatement(
mappedStatementId,
sqlSource,
statementType,
sqlCommandType,
fetchSize,
timeout,
// ParameterMapID
null,
parameterTypeClass,
resultMapId,
getReturnType(method),
resultSetType,
flushCache,
useCache,
// TODO issue #577
false,
keyGenerator,
keyProperty,
keyColumn,
// DatabaseID
null,
languageDriver,
// ResultSets
null);
}
}
- 最后把之前缓存的未完成的方法,再调一次resolve方法,流程和之前解析的时候一样,因为这时整个XML中所有的resultMap,CacheRef,都已经被正确解析了,XML前面出错的就可以引用到后面的了。
parsePendingResultMaps();
parsePendingChacheRefs();
parsePendingStatements();
- 最后创建一个SqlSessionFactory的实例赋值给SqlSessionFactoryBean的sqlSessionFactory属性,后续在调用FactoryBean的getObject方法时就会返回该对象。
public SqlSessionFactory build(Configuration config) {
return new DefaultSqlSessionFactory(config);
}
此时最终在Configuration对象中会产生如下部分重要的属性,
- interceptorChain:拦截器链
- mappedStatements:已解析好的Sql表达式
- caches:配置过的缓存
- resultMaps: 结果映射集
- parameterMaps:参数映射
- keyGenerator:主键生成器
- sqlFragments:sql片段
- cacheRefMap:缓存引用