Mybatis 源码解析 -- 基于配置的源码解析(二)
2017-11-14 15:13
681 查看
mapper解析
接着上篇的配置,本篇主要讲解mappers标签
该标签的主要功能:标记Mybatis数据库接口或Mybatis映射文件,配置方式有三:
源码解析:org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XNode root)
进入到方法
得到mappers节点后,解析其子节点。
如果子节点中存在 package 节点,则解析方式如下:
获取包路径 name属性值
解析:configuration.addMappers(mapperPackage);
具体的解析(非package节点的解析直接进入这):
映射文件解析
接着上篇的配置,本篇主要讲解mappers标签
<?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> <environments default="dev"> <environment id="dev"> <transactionManager type="JDBC" /> <dataSource type="com.xiaoye.clearworld.datasource.CustomDruidDataSource"> <property name="driverClassName" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://127.0.0.1:3306/zlgedu" /> <property name="username" value="root" /> <property name="password" value="root" /> </dataSource> </environment> <environment id="uat"> <transactionManager type="JDBC" /> <dataSource type="POOLED"> <property name="driver" value="com.mysql.jdbc.Driver" /> <property name="url" value="jdbc:mysql://127.0.0.1:3306/zlgedu" /> <property name="username" value="root" /> <property name="password" value="root" /> </dataSource> </environment> </environments> <mappers> <mapper class="com.xiaoye.clearworld.persistence.WebsiteZlgeduCompanyMapper"/> <!-- 更多………… --> </mappers> </configuration>
该标签的主要功能:标记Mybatis数据库接口或Mybatis映射文件,配置方式有三:
<package name=""/> 数据库接口所在父包路径 <mapper class=""/> 数据库接口类路径 <mapper resource=""/> 数据库映射文件路径 <mapper url=""/> 数据库映射文件资源路径
源码解析:org.apache.ibatis.builder.xml.XMLConfigBuilder.parseConfiguration(XNode root)
private void parseConfiguration(XNode root) { try { //issue #117 read properties first propertiesElement(root.evalNode("properties")); Properties settings = settingsAsProperties(root.evalNode("settings")); loadCustomVfs(settings); typeAliasesElement(root.evalNode("typeAliases")); pluginElement(root.evalNode("plugins")); objectFactoryElement(root.evalNode("objectFactory")); objectWrapperFactoryElement(root.evalNode("objectWrapperFactory")); reflectorFactoryElement(root.evalNode("reflectorFactory")); settingsElement(settings); // read it after objectFactory and objectWrapperFactory issue #631 environmentsElement(root.evalNode("environments")); databaseIdProviderElement(root.evalNode("databaseIdProvider")); typeHandlerElement(root.evalNode("typeHandlers")); // mappers 标签解析 mapperElement(root.evalNode("mappers")); } catch (Exception e) { throw new BuilderException("Error parsing SQL Mapper Configuration. Cause: " + e, e); } }
进入到方法
private void mapperElement(XNode parent) throws Exception { if (parent != null) { for (XNode child : parent.getChildren()) { // 解析 mappers 的子节点 if ("package".equals(child.getName())) { // 配置接口扫描包 package 节点(扫描包) String mapperPackage = child.getStringAttribute("name"); configuration.addMappers(mapperPackage); // 如果配置了package节点,mybatis默认会将该包下所有的接口类加载到mybatis中 } else { // 单独配置 String resource = child.getStringAttribute("resource"); String url = child.getStringAttribute("url"); String mapperClass = child.getStringAttribute("class"); if (resource != null && url == null && mapperClass == null) { // resource xml 文件 ErrorContext.instance().resource(resource); InputStream inputStream = Resources.getResourceAsStream(resource); XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments()); mapperParser.parse(); } 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(); } else if (resource == null && url == null && mapperClass != null) { 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."); } } } } }
得到mappers节点后,解析其子节点。
如果子节点中存在 package 节点,则解析方式如下:
获取包路径 name属性值
解析:configuration.addMappers(mapperPackage);
public void addMappers(String packageName) { mapperRegistry.addMappers(packageName); } /** * @since 3.2.2 也就是mybatis3.2.2版本才增加了这一功能 */ public void addMappers(String packageName) { // 添加mappers,并指定要添加的类或子类的类型 addMappers(packageName, Object.class); } /** * @since 3.2.2 */ public void addMappers(String packageName, Class<?> superType) { // 工具类:用来判断是否是指定类或其子类,并存储结果 ResolverUtil<Class<?>> resolverUtil = new ResolverUtil<Class<?>>(); // 查找类 resolverUtil.find(new ResolverUtil.IsA(superType), packageName); Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses(); for (Class<?> mapperClass : mapperSet) { addMapper(mapperClass); } } public ResolverUtil<T> find(Test test, String packageName) { // 获取包路径 String path = getPackagePath(packageName); try { // 获取包路径下的所有文件路径(格式:com/mysql/jdbc/jdbc2/optional/JDBC4SuspendableXAConnection.class) List<String> children = VFS.getInstance().list(path); // 遍历包路径下的所有文件路径 for (String child : children) { // 过滤 class 文件 if (child.endsWith(".class")) { // 判断是否是指定类或指定类的子类 addIfMatching(test, child); } } } catch (IOException ioe) { log.error("Could not read package: " + packageName, ioe); } return this; } // 所以包路径的格式类似:com.xxx.xxx.xxx protected String getPackagePath(String packageName) { return packageName == null ? null : packageName.replace('.', '/'); } // 判断是否是指定类或指定类的子类 protected void addIfMatching(Test test, String fqn) { try { String externalName = fqn.substring(0, fqn.indexOf('.')).replace('/', '.'); ClassLoader loader = getClassLoader(); if (log.isDebugEnabled()) { log.debug("Checking to see if class " + externalName + " matches criteria [" + test + "]"); } // 得到类的Class类型 Class<?> type = loader.loadClass(externalName); // 判断,符合存储到set集合中 if (test.matches(type)) { matches.add((Class<T>) type); } } catch (Throwable t) { log.warn("Could not examine class '" + fqn + "'" + " due to a " + t.getClass().getName() + " with message: " + t.getMessage()); } } // 添加 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 { // 缓存接口及其代理 knownMappers.put(type, new MapperProxyFactory<T>(type)); // It's important that the type is added before the parser is run // otherwise the binding may automatically be attempted by the // mapper parser. If the type is already known, it won't try. MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type); parser.parse(); loadCompleted = true; } finally { if (!loadCompleted) { knownMappers.remove(type); } } } }分析上面的代码:a、根据包名称转换得到包路径;b、根据包路径获取该包下所有的文件;c、遍历文件,取出class结尾的文件;d、判断是否是指定类或指定类的子类;e:添加并解析
具体的解析(非package节点的解析直接进入这):
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 { knownMappers.put(type, new MapperProxyFactory<T>(type)); // It's important that the type is added before the parser is run // otherwise the binding may automatically be attempted by the // mapper parser. If the type is already known, it won't try. MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type); parser.parse(); loadCompleted = true; } finally { if (!loadCompleted) { knownMappers.remove(type); } } } }分析:1、首先是接口;2、之前未添加过haddMapper;3、存储:knownMappers.put(type, new MapperProxyFactory<T>(type));4、映射文件解析
映射文件解析
MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type); parser.parse();
public MapperAnnotationBuilder(Configuration configuration, Class<?> type) { String resource = type.getName().replace('.', '/') + ".java (best guess)"; this.assistant = new MapperBuilderAssistant(configuration, resource); this.configuration = configuration; this.type = type; sqlAnnotationTypes.add(Select.class); sqlAnnotationTypes.add(Insert.class); sqlAnnotationTypes.add(Update.class); sqlAnnotationTypes.add(Delete.class); sqlProviderAnnotationTypes.add(SelectProvider.class); sqlProviderAnnotationTypes.add(InsertProvider.class); sqlProviderAnnotationTypes.add(UpdateProvider.class); sqlProviderAnnotationTypes.add(DeleteProvider.class); }
public void parse() { String resource = type.toString(); if (!configuration.isResourceLoaded(resource)) { // 判断是否解析过 loadXmlResource(); // 解析映射文件 configuration.addLoadedResource(resource); // 添加已解析 assistant.setCurrentNamespace(type.getName()); // 校验映射文件命名空间 parseCache(); // 缓存解析 parseCacheRef(); // TODO Method[] methods = type.getMethods(); // 获取接口的所有方法 for (Method method : methods) { // 遍历 try { // issue #237 if (!method.isBridge()) { // parseStatement(method); // } } catch (IncompleteElementException e) { configuration.addIncompleteMethod(new MethodResolver(this, method)); } } } parsePendingMethods(); // }映射文件解析:loadXmlResource();
private void loadXmlResource() { // Spring may not know the real resource name so we check a flag // to prevent loading again a resource twice // this flag is set at XMLMapperBuilder#bindMapperForNamespace if (!configuration.isResourceLoaded("namespace:" + type.getName())) { // 映射文件默认和数据库接口文件在同一包下 String xmlResource = type.getName().replace('.', '/') + ".xml"; InputStream inputStream = null; try { inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource); } catch (IOException e) { // ignore, resource is not required } if (inputStream != null) { XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName()); xmlParser.parse(); } } }
XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName()); xmlParser.parse();
相关文章推荐
- Mybatis 源码阅读 -- 基于配置的源码解析(一)
- 【MyBatis源码分析】settings解析属性配置元素详述
- mybatis源码解析-启动配置&使用spring启动配置(一)
- Spring3.2 中 Bean 定义之基于 XML 配置方式的源码解析
- (一)MyBatis源码解析之配置文件
- 【MyBatis源码分析】plugins解析属性配置元素详述
- Spring3.2 中 Bean 定义之基于 XML 配置方式的源码解析
- (二)MyBatis源码解析之配置文件
- Mybatis3源码分析(三):解析mapper的xml配置文件
- MyBatis源码——解析MyBatis配置文件
- (三)MyBatis源码解析之配置文件
- mybatis源码学习之执行过程分析(2)——config.xml配置文件和mapper.xml映射文件解析过程
- MyBatis 源码分析 - 配置文件解析过程
- Spring3.2 中 Bean 定义之基于 XML 配置方式的源码解析
- 基于mybatis源码从源码中读取远程数据库配置信息
- mybatis底层源码分析之--配置文件读取和解析
- 【MyBatis源码分析】TypeHandler解析属性配置元素详述及相关枚举使用高级进阶
- 基于源码编译安装htppd配置解析
- mybatis源码-解析配置文件(四-1)之配置文件Mapper解析(cache)
- Spring3.2 中 Bean 定义之基于 XML 配置方式的源码解析