众所周知,Mybatis
有一个全局的配置,在程序启动时会加载XML配置文件,将配置信息映射到org.apache.ibatis.session.Configuration
类中,例如如下配置文件。
<?xml version="1.0" encoding="UTF-8" ?> <!DOCTYPE configuration PUBLIC "-//mybatis.org//DTD Config 3.0//EN" "http://mybatis.org/dtd/mybatis-3-config.dtd"> <configuration> <!--resource="db.properties"--> <properties resource="db.properties"> <property name="test" value="123456"></property> </properties> <settings> <!-- 控制全局缓存(二级缓存)--> <setting name="cacheEnabled" value="true"/> <!-- 延迟加载的全局开关。当开启时,所有关联对象都会延迟加载。默认 false --> <setting name="lazyLoadingEnabled" value="true"/> <!-- 当开启时,任何方法的调用都会加载该对象的所有属性。默认 false,可通过select标签的 fetchType来覆盖--> <setting name="aggressiveLazyLoading" value="false"/> <setting name="localCacheScope" value="SESSION"/> </settings> <typeAliases> <typeAlias type="com.fanpan26.source.code.mybatis.UserMapper" alias="userMapper"/> <package name="com.fanpan26.source.code.mybatis.model" /> </typeAliases> <plugins> <plugin interceptor="com.fanpan26.source.code.mybatis.plugin.MyPlugin"></plugin> </plugins> <environments default="development"> <environment id="development"> <transactionManager type="JDBC"/><!-- 单独使用时配置成MANAGED没有事务 --> <dataSource type="POOLED"> <property name="driver" value="${jdbc.driver}"/> <property name="url" value="${jdbc.url}"/> <property name="username" value="${jdbc.username}"/> <property name="password" value="${jdbc.password}"/> </dataSource> </environment> </environments> <mappers> <mapper resource="UserMapper.xml"/> </mappers> </configuration>
这里我们要注意的是,每个配置项目的顺序不能变,否则在做XML解析的时候会抛异常。
<!ELEMENT configuration (properties?, settings?, typeAliases?, typeHandlers?, objectFactory?, objectWrapperFactory?, reflectorFactory?, plugins?, environments?, databaseIdProvider?, mappers?)>
那么它是如何做到的呢?下面跟着我揭开它的神秘面纱吧。
代码分析
Configuration
对象是通过XMLConfigBuilder
的parse()
方法得到的,示例代码如下:
XMLConfigBuilder parser = new XMLConfigBuilder(Resources.getResourceAsStream("mybatis-config.xml")); Configuration configuration = parser.parse();
XMLConfigBuilder
继承自抽象类BaseBuilder
.
构造函数没什么好说的:
public XMLConfigBuilder(InputStream inputStream) { this(inputStream, null, null); } public XMLConfigBuilder(InputStream inputStream, String environment, Properties props) { this(new XPathParser(inputStream, true, props, new XMLMapperEntityResolver()), environment, props); }
其中,XPathParser
会解析XML中的内容,这里我们就不详细跟进了,我们主要看与MyBatis
息息相关的各种属性是如何加载的。
public Configuration parse() { //只能解析一次,否则异常 if (parsed) { throw new BuilderException("Each XMLConfigBuilder can only be used once."); } parsed = true; //执行具体的解析,从 configuration节点下解析 parseConfiguration(parser.evalNode("/configuration")); return configuration; }
下面主要跟进parseConfiguration
方法
private void parseConfiguration(XNode root) { try { //先读取properties propertiesElement(root.evalNode("properties")); //将 settings 转化为 Properties Properties settings = settingsAsProperties(root.evalNode("settings")); loadCustomVfs(settings); loadCustomLogImpl(settings); //解析typeAliase typeAliasesElement(root.evalNode("typeAliases")); //解析plugins pluginElement(root.evalNode("plugins")); //解析 objectFactory objectFactoryElement(root.evalNode("objectFactory")); //解析objectWrapperFactory objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); //解析 reflectorFactory reflectorFactoryElement(root.evalNode("reflectorFactory")); settingsElement(settings); //解析环境信息 environmentsElement(root.evalNode("environments")); //解析databaseIdProvider databaseIdProviderElement(root.evalNode("databaseIdProvider")); //解析typeHandlers typeHandlerElement(root.evalNode("typeHandlers")); //解析mappers mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } }
解析Properties
这一步就是将配置文件中的<Properties>
解析出,然后放到Configuration
对象的属性键值对。解析过程分为两个部分:
先解析XML中的配置项,例如配置中的
test:123456
信息.<properties resource="db.properties"> <property name="test" value="123456"></property> </properties>
然后在解析
resource
属性或者url
属性中的信息,注意它俩不能共存,也就是不能既配置resource
又配置url
解析XML中的属性配置项:
Properties defaults = context.getChildrenAsProperties(); //XNode 中的方法 public Properties getChildrenAsProperties() { Properties properties = new Properties(); //遍历子节点信息,读取出name和value属性,赋值给Properties对象 for (XNode child : getChildren()) { //获取name属性 String name = child.getStringAttribute("name"); //获取value属性 String value = child.getStringAttribute("value"); //name 和value 都不能为 null if (name != null && value != null) { properties.setProperty(name, value); } } return properties; }
然后开始解析resource
或者url
信息
//获取resource 属性 String resource = context.getStringAttribute("resource"); //获取Java属性 String url = context.getStringAttribute("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."); }
然后直接调用Resources
工具类的方法,将资源转化为Properties
的key value
值.
if (resource != null) { defaults.putAll(Resources.getResourceAsProperties(resource)); } else if (url != null) { defaults.putAll(Resources.getUrlAsProperties(url)); }
最后在和Configuration.variables
合并.
Properties vars = configuration.getVariables(); if (vars != null) { defaults.putAll(vars); } //XPathParser 保存变量属性留在后边解析备用 parser.setVariables(defaults); //所有的属性信息就解析完毕,存放到 Configuration 的 变量 variables (Properties类型) 中 configuration.setVariables(defaults);
将 settings 转化为 Properties
Properties settings = settingsAsProperties(root.evalNode("settings"));
private Properties settingsAsProperties(XNode context) { if (context == null) { return new Properties(); } //这里就能获取所有的属性键值对了,但是后续要判断键值对中的键是否是Configuration中的属性。 Properties props = context.getChildrenAsProperties(); // Check that all settings are known to the configuration class MetaClass metaConfig = MetaClass.forClass(Configuration.class, localReflectorFactory); for (Object key : props.keySet()) { //如果没有该属性的setter方法,就抛出异常,因为 setting中的配置是和Configuration中一一对应的,这里不能配置错 if (!metaConfig.hasSetter(String.valueOf(key))) { throw new BuilderException("The setting " + key + " is not known. Make sure you spelled it correctly (case sensitive)."); } } return props; }
赋值 VFS 虚拟文件系统
loadCustomVfs(settings); private void loadCustomVfs(Properties props) throws ClassNotFoundException { //读取 vfsImpl 配置信息 String value = props.getProperty("vfsImpl"); if (value != null) { String[] clazzes = value.split(","); for (String clazz : clazzes) { if (!clazz.isEmpty()) { @SuppressWarnings("unchecked") Class<? extends VFS> vfsImpl = (Class<? extends VFS>)Resources.classForName(clazz); configuration.setVfsImpl(vfsImpl); } } } }
赋值 Logger
loadCustomLogImpl(settings); private void loadCustomLogImpl(Properties props) { //读取 logImpl属性,获取类信息 Class<? extends Log> logImpl = resolveClass(props.getProperty("logImpl")); configuration.setLogImpl(logImpl); }
解析 typeAlias
<typeAliases> <typeAlias type="com.fanpan26.source.code.mybatis.UserMapper" alias="userMapper"/> <package name="com.fanpan26.source.code.mybatis.model" /> </typeAliases>
Configuration
对象中有一个TypeAliasRegistry
对象,TypeAliasRegistry
中有一个HashMap<String,Class<?>>
类型的 typeAlias
集合,所有的注册信息都是存放到该HashMap
中,MyBatis
本身内置了基础类型的映射关系。
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); }
当配置文件中存在<typeAlias>
属性的时候,就将alias
注册上。
typeAliasesElement(root.evalNode("typeAliases"));
其中,它有两种注册方式,一种就是通过package
批量注册,毕竟一个类一个类的写太麻烦了。另外就是单个注册。
单个注册方式:
//读取属性中的 alias 值 String alias = child.getStringAttribute("alias"); //读取类型 String type = child.getStringAttribute("type"); try { //反射获取类 Class<?> clazz = Resources.classForName(type); //如果没有配置 alias,没关系,按照默认 名称注册 if (alias == null) { typeAliasRegistry.registerAlias(clazz); } else { //否则直接注册 typeAliasRegistry.registerAlias(alias, clazz); }
按照默认名称注册
public void registerAlias(Class<?> type) { //反射获取类名 String alias = type.getSimpleName(); //这里是为了支持注解,如果存在Alias注解,那么使用Alias注解中的value值 Alias aliasAnnotation = type.getAnnotation(Alias.class); if (aliasAnnotation != null) { alias = aliasAnnotation.value(); } //注册 registerAlias(alias, type); }
按照package
注册,思路是这样的,遍历package
下所有的类进行注册。
public void registerAliases(String packageName, Class<?> superType) { ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<>(); resolverUtil.find(new ResolverUtil.IsA(superType), packageName); //获取packages下的所有的类 Set<Class<? extends Class<?>>> typeSet = resolverUtil.getClasses(); for (Class<?> type : typeSet) { //排除接口,内部类 if (!type.isAnonymousClass() && !type.isInterface() && !type.isMemberClass()) { registerAlias(type); } } }
注册plugins
<plugins> <plugin interceptor="com.fanpan26.source.code.mybatis.plugin.MyPlugin"></plugin> </plugins>
pluginElement(root.evalNode("plugins")); private void pluginElement(XNode parent) throws Exception { if (parent != null) { for (XNode child : parent.getChildren()) { //获取 interceptor 属性 String interceptor = child.getStringAttribute("interceptor"); //获取属性信息 Properties properties = child.getChildrenAsProperties(); //这里利用反射将 Interceptor 实例化,然后调用 setProperties 方法 Interceptor interceptorInstance = (Interceptor) resolveClass(interceptor).getDeclaredConstructor().newInstance(); interceptorInstance.setProperties(properties); //最后将 Interceptor 实例加入到拦截链中,List<Interceptor> 对象 configuration.addInterceptor(interceptorInstance); } } }
注册 ObjectFactory
其实大多数场景下,使用默认的 DefaultObjectFactory
即可。
<objectFactory type="com.fanpan26.source.code.mybatis.factory.MyObjectFactory"> <property name="a" value="b"/> </objectFactory>
objectFactoryElement(root.evalNode("objectFactory")); //注册 objectFactory private void objectFactoryElement(XNode context) throws Exception { if (context != null) { //获取类的全路径名称 String type = context.getStringAttribute("type"); //获取其中的属性 Properties properties = context.getChildrenAsProperties(); //利用反射创建实例 ObjectFactory factory = (ObjectFactory) resolveClass(type).getDeclaredConstructor().newInstance(); //调用接口方法,注入自定义属性 factory.setProperties(properties); //调用 setObjectFactory 方法 // protected ObjectFactory objectFactory = new DefaultObjectFactory(); configuration.setObjectFactory(factory); } }
注册 ObjectWrapperFactory
<objectWrapperFactory type="com.fanpan26.source.code.mybatis.factory.MyObjectWrapperFactory"/>
默认是 DefaultObjectWrapperFactory
,不过这个类好像没啥用
public class DefaultObjectWrapperFactory implements ObjectWrapperFactory { public DefaultObjectWrapperFactory() { } public boolean hasWrapperFor(Object object) { return false; } public ObjectWrapper getWrapperFor(MetaObject metaObject, Object object) { throw new ReflectionException("The DefaultObjectWrapperFactory should never be called to provide an ObjectWrapper."); } }
注册方法如下:
private void objectWrapperFactoryElement(XNode context) throws Exception { if (context != null) { //获取类全名 String type = context.getStringAttribute("type"); //反射创建 ObjectWrapperFactory factory = (ObjectWrapperFactory) resolveClass(type).getDeclaredConstructor().newInstance(); //设置属性 //protected ObjectWrapperFactory objectWrapperFactory = new DefaultObjectWrapperFactory(); configuration.setObjectWrapperFactory(factory); } }
注册reflectorFactory
<reflectorFactory type="com.fanpan26.source.code.mybatis.factory.MyReflectorFactory"/>
reflectorFactory
用户创建类的 Reflector 对象。其中带有缓存功能。使用 ConcurrentHashMap<Class<?>,Reflector>
缓存。
reflectorFactoryElement(root.evalNode("reflectorFactory")); private void reflectorFactoryElement(XNode context) throws Exception { if (context != null) { //获取类型 String type = context.getStringAttribute("type"); //反射生成对象 ReflectorFactory factory = (ReflectorFactory) resolveClass(type).getDeclaredConstructor().newInstance(); //设置 //protected ReflectorFactory reflectorFactory = new DefaultReflectorFactory(); configuration.setReflectorFactory(factory); } }
注册 settings
就是将<settings>
节点中的所有设置的属性值都设置到Configuration
对象中。
settingsElement(settings); private void settingsElement(Properties props) { //自动映射等 configuration.setAutoMappingBehavior(AutoMappingBehavior.valueOf(props.getProperty("autoMappingBehavior", "PARTIAL"))); configuration.setAutoMappingUnknownColumnBehavior(AutoMappingUnknownColumnBehavior.valueOf(props.getProperty("autoMappingUnknownColumnBehavior", "NONE"))); 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"), false)); 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.setDefaultResultSetType(resolveResultSetType(props.getProperty("defaultResultSetType"))); 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.setDefaultEnumTypeHandler(resolveClass(props.getProperty("defaultEnumTypeHandler"))); configuration.setCallSettersOnNulls(booleanValueOf(props.getProperty("callSettersOnNulls"), false)); configuration.setUseActualParamName(booleanValueOf(props.getProperty("useActualParamName"), true)); configuration.setReturnInstanceForEmptyRow(booleanValueOf(props.getProperty("returnInstanceForEmptyRow"), false)); configuration.setLogPrefix(props.getProperty("logPrefix")); configuration.setConfigurationFactory(resolveClass(props.getProperty("configurationFactory"))); }
注册 environments
environmentsElement(root.evalNode("environments")); private void environmentsElement(XNode context) throws Exception { if (context != null) { if (environment == null) { //这里如果之前没有传递 environment 参数,则取 default 值 environment = context.getStringAttribute("default"); } for (XNode child : context.getChildren()) { //获取id String id = child.getStringAttribute("id"); //这里会判断id是否为空或者 evnironment是否为空, if (isSpecifiedEnvironment(id)) { //获取事务相关配置,这里和之前的代码差不多,解析type内容,然后反射创建对象 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); //设置环境 configuration.setEnvironment(environmentBuilder.build()); } } } }
这里以 PooledDataSource
举例,看看DataSource
如何初始化的。
private DataSourceFactory dataSourceElement(XNode context) throws Exception { if (context != null) { //先获取DataSourceFactory类型,这里是 POOLED String type = context.getStringAttribute("type"); //获取子属性,相关数据库配置, url username password 等 Properties props = context.getChildrenAsProperties(); //反射创建 DataSourceFactory,这里是 PooledDataSourceFactory 实例 //org.apache.ibatis.datasource.pooled.PooledDataSourceFactory DataSourceFactory factory = (DataSourceFactory) resolveClass(type).getDeclaredConstructor().newInstance(); //这里设置属性的时候,利用了反射,将各个属性值赋值给了 DataSource 对象 factory.setProperties(props); return factory; } throw new BuilderException("Environment declaration requires a DataSourceFactory."); }
注册 DatabaseIdProvider
<databaseIdProvider type="com.fanpan26.source.code.mybatis.factory.MyDatabaseIdProvider"> <property name="dataBaseId" value="db1"/> </databaseIdProvider>
databaseIdProviderElement(root.evalNode("databaseIdProvider")); private void databaseIdProviderElement(XNode context) throws Exception { DatabaseIdProvider databaseIdProvider = null; if (context != null) { //获取类全名 String type = context.getStringAttribute("type"); // awful patch to keep backward compatibility if ("VENDOR".equals(type)) { type = "DB_VENDOR"; } //获取属性信息 Properties properties = context.getChildrenAsProperties(); //反射常见类对象 databaseIdProvider = (DatabaseIdProvider) resolveClass(type).getDeclaredConstructor().newInstance(); //调用属性赋值 databaseIdProvider.setProperties(properties); } Environment environment = configuration.getEnvironment(); if (environment != null && databaseIdProvider != null) { //获取dataBaseId String databaseId = databaseIdProvider.getDatabaseId(environment.getDataSource()); //将dataBaseId设置给environment configuration.setDatabaseId(databaseId); } }
注册 TypeHandlers
<typeHandlers> <typeHandler handler="com.fanpan26.source.code.mybatis.handler.MyTypeHandler"></typeHandler> <package name="com.fanpan26.source.code.mybatis.handler"/> </typeHandlers>
和 typeAlias
类似,都可以通过单独注册和package
批量注册
typeHandlerElement(root.evalNode("typeHandlers")); private void typeHandlerElement(XNode parent) { if (parent != null) { for (XNode child : parent.getChildren()) { if ("package".equals(child.getName())) { //获取package name String typeHandlerPackage = child.getStringAttribute("name"); //批量注册 typeHandlerRegistry.register(typeHandlerPackage); } else { /* private final Map<JdbcType, TypeHandler<?>> jdbcTypeHandlerMap = new EnumMap<>(JdbcType.class); private final Map<Type, Map<JdbcType, TypeHandler<?>>> typeHandlerMap = new ConcurrentHashMap<>(); private final TypeHandler<Object> unknownTypeHandler = new UnknownTypeHandler(this); private final Map<Class<?>, TypeHandler<?>> allTypeHandlersMap = new HashMap<>(); */ //获取java类型 String javaTypeName = child.getStringAttribute("javaType"); //获取jdbcType类型 String jdbcTypeName = child.getStringAttribute("jdbcType"); //获取handler String handlerTypeName = child.getStringAttribute("handler"); //反射获取类型 Class<?> javaTypeClass = resolveClass(javaTypeName); //反射获取JdbcType类型 JdbcType jdbcType = resolveJdbcType(jdbcTypeName); Class<?> typeHandlerClass = resolveClass(handlerTypeName); //注册自定义映射处理器,后续源码分析会分析 TypeHandler的作用 if (javaTypeClass != null) { if (jdbcType == null) { typeHandlerRegistry.register(javaTypeClass, typeHandlerClass); } else { typeHandlerRegistry.register(javaTypeClass, jdbcType, typeHandlerClass); } } else { typeHandlerRegistry.register(typeHandlerClass); } } } } }
注册 mappers
<mappers> <mapper resource="UserMapper.xml"/> </mappers>
注册核心业务处理接口 Mapper
mapperElement(root.evalNode("mappers")); private void mapperElement(XNode parent) throws Exception { if (parent != null) { for (XNode child : parent.getChildren()) { //扫描包注册 if ("package".equals(child.getName())) { String mapperPackage = child.getStringAttribute("name"); //内部执行了批量注册,将 package 下的接口都注册到mappers中 configuration.addMappers(mapperPackage); } else { //resource 注册,通常是 xml 文件 String resource = child.getStringAttribute("resource"); //或者通过url注册,同理url或者resource不能同时使用 String url = child.getStringAttribute("url"); //获取 class String mapperClass = child.getStringAttribute("class"); //当只有resource的时候 if (resource != null && url == null && mapperClass == null) { ErrorContext.instance().resource(resource); InputStream inputStream = Resources.getResourceAsStream(resource); XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); mapperParser.parse(); //当使用url的时候 } else if (resource == null && url != null && mapperClass == null) { ErrorContext.instance().resource(url); InputStream inputStream = Resources.getUrlAsStream(url); XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments()); mapperParser.parse(); //最后解析class } else if (resource == null && url == null && mapperClass != null) { //注册class Class<?> mapperInterface = Resources.classForName(mapperClass); configuration.addMapper(mapperInterface); } else { throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one."); } } } } }
mapper的解析比较复杂,尤其是解析XML文件,需要解析内部的各种属性元素等。
从xml文件解析Mapper流程
从xml文件解析Mapper
和解析Configuration
差不多,无非就是解析xml元素,然后找到对应属性赋值即可。
InputStream inputStream = Resources.getResourceAsStream(resource); //读取配置文件 XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); //详细解析流程 mapperParser.parse();
下面看一下parse
方法
public void parse() { //如果该文件已经加载过,不必重新加载 if (!configuration.isResourceLoaded(resource)) { //解析 mapper 节点下的信息 select insert update delete configurationElement(parser.evalNode("/mapper")); //将资源加入到已加载资源列表中 configuration.addLoadedResource(resource); //将mapper加入到configuration中 bindMapperForNamespace(); } parsePendingResultMaps(); parsePendingCacheRefs(); parsePendingStatements(); }
总结
MyBatis
的Configuration
对象的加载就告一段落,这就意味着MyBatis
准备就绪了。接下来就要解析一个重要的对象SqlSessionFactory
了,因为它负责创建SqlSession
,而SqlSession
则负责执行各种SQL和方法。