/**
* mapper信息解析入口
* 源码位置:org.apache.ibatis.builder.xml.XMLConfigBuilder.mapperElement(XNode)
*/privatevoidmapperElement(XNode parent)throwsException{// 解析的节点不为nullif(parent !=null){// 遍历每个节点for(XNode child : parent.getChildren()){// package的解析方式if("package".equals(child.getName())){String mapperPackage = child.getStringAttribute("name");
configuration.addMappers(mapperPackage);}else{String resource = child.getStringAttribute("resource");String url = child.getStringAttribute("url");String mapperClass = child.getStringAttribute("class");if(resource !=null&& url ==null&& mapperClass ==null){// resouse的解析方式ErrorContext.instance().resource(resource);InputStream inputStream =Resources.getResourceAsStream(resource);XMLMapperBuilder mapperParser =newXMLMapperBuilder(inputStream, configuration, resource,
configuration.getSqlFragments());
mapperParser.parse();}elseif(resource ==null&& url !=null&& mapperClass ==null){// url的解析方式ErrorContext.instance().resource(url);InputStream inputStream =Resources.getUrlAsStream(url);XMLMapperBuilder mapperParser =newXMLMapperBuilder(inputStream, configuration, url,
configuration.getSqlFragments());
mapperParser.parse();}elseif(resource ==null&& url ==null&& mapperClass !=null){// class的解析方式Class<?> mapperInterface =Resources.classForName(mapperClass);
configuration.addMapper(mapperInterface);}else{thrownewBuilderException("A mapper element may only specify a url, resource or class, but not more than one.");}}}}}
包路径解析Mapper源码分析
/**
* 包路径解析Mapper的入口
* 源码位置:org.apache.ibatis.session.Configuration.addMappers(String)
*/publicvoidaddMappers(String packageName){
mapperRegistry.addMappers(packageName);}/**
* 继续调用
* 源码位置:org.apache.ibatis.binding.MapperRegistry.addMappers(String)
*/publicvoidaddMappers(String packageName){addMappers(packageName,Object.class);}/**
* 继续调用
* 源码位置:org.apache.ibatis.binding.MapperRegistry.addMappers(String, Class<?>)
*/publicvoidaddMappers(String packageName,Class<?> superType){ResolverUtil<Class<?>> resolverUtil =newResolverUtil<Class<?>>();
resolverUtil.find(newResolverUtil.IsA(superType), packageName);// 得到所有的包路径内的文件Set<Class<?extendsClass<?>>> mapperSet = resolverUtil.getClasses();// 遍历包中的所有文件for(Class<?> mapperClass : mapperSet){addMapper(mapperClass);}}/**
* 添加单个Mapper
* 源码位置:org.apache.ibatis.binding.MapperRegistry.addMapper(Class<T>)
*/public<T>voidaddMapper(Class<T> type){// 只有是接口才会往下走!if(type.isInterface()){// 判断缓存中有没有这种类型。。重复性校验!if(hasMapper(type)){thrownewBindingException("Type "+ type +" is already known to the MapperRegistry.");}boolean loadCompleted =false;try{// 添加到缓存。。值是一个MapperProxyFactor,后面会使用jdk的动态代理去参数代理对象
knownMappers.put(type,newMapperProxyFactory<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 =newMapperAnnotationBuilder(config, type);// 解析mapper文件
parser.parse();// 设置解析成功
loadCompleted =true;}finally{// 解析不成功,移除掉缓存!if(!loadCompleted){
knownMappers.remove(type);}}}}/**
* 解析mapper文件
* 源码位置:org.apache.ibatis.builder.annotation.MapperAnnotationBuilder.parse()
*/publicvoidparse(){// 得到mapper路径String resource = type.toString();// 是否已经解析mapper对应的xmlif(!configuration.isResourceLoaded(resource)){// 根据文件接口名,获取对应的xml文件loadXmlResource();// 标记位已经解析
configuration.addLoadedResource(resource);// 设置解析的nameSpace
assistant.setCurrentNamespace(type.getName());// 解析缓存parseCache();parseCacheRef();// 得到里面的方法,看是不是用了注解Method[] methods = type.getMethods();for(Method method : methods){try{// issue #237// 判断是不是用了注解if(!method.isBridge()){// 用了注解的解析为MappedStatementparseStatement(method);}}catch(IncompleteElementException e){
configuration.addIncompleteMethod(newMethodResolver(this, method));}}}// 解析方法parsePendingMethods();}
加载XML文件源码分析
/**
* 加载XML文件
* 源码位置:org.apache.ibatis.builder.annotation.MapperAnnotationBuilder.loadXmlResource()
*/privatevoidloadXmlResource(){// 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#bindMapperForNamespaceif(!configuration.isResourceLoaded("namespace:"+ type.getName())){// 更觉接口名称找xml的名称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 =newXMLMapperBuilder(inputStream, assistant.getConfiguration(),
xmlResource, configuration.getSqlFragments(), type.getName());// 解析XML
xmlParser.parse();}}}/**
* 解析XML文件
* 源码位置:org.apache.ibatis.builder.xml.XMLMapperBuilder.parse()
*/publicvoidparse(){// 判断当前的mapper是否在加载过if(!configuration.isResourceLoaded(resource)){// 加载mapper节点configurationElement(parser.evalNode("/mapper"));// 资源保存在configuration中
configuration.addLoadedResource(resource);bindMapperForNamespace();}parsePendingResultMaps();parsePendingCacheRefs();parsePendingStatements();}/**
* 加载mapper节点
* 源码位置:org.apache.ibatis.builder.xml.XMLMapperBuilder.configurationElement(XNode)
*/privatevoidconfigurationElement(XNode context){try{// 得到节点中namespace的值String namespace = context.getStringAttribute("namespace");// 没有值,抛异常if(namespace ==null|| namespace.equals("")){thrownewBuilderException("Mapper's namespace cannot be empty");}// 将namespace进行存储(里面判断是否与类名相同)
builderAssistant.setCurrentNamespace(namespace);// 解析缓存引用cacheRefElement(context.evalNode("cache-ref"));// 解析cache标签。二级缓存cacheElement(context.evalNode("cache"));// 解析parameterMap。3.5之后不推荐使用了parameterMapElement(context.evalNodes("/mapper/parameterMap"));// 解析resultMap节点resultMapElements(context.evalNodes("/mapper/resultMap"));// 解析sql节点sqlElement(context.evalNodes("/mapper/sql"));// 解析增删改查节点 buildStatementFromContext(context.evalNodes("select|insert|update|delete"));}catch(Exception e){thrownewBuilderException("Error parsing Mapper XML. The XML location is '"+ resource +"'. Cause: "+ e,
e);}}