mybatis源码解析-加载Mapper配置文件

一,加载Mapper配置文件

1.1mappers有四种配置方式:

方式一:

  <mappers>   
  	<package name="被代理对象(接口)的所在包"/> 
  </mappers>    

​ 注意:注册指定包下的所有mapper接口;此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个包中。

方式二:

 <mappers>   
 	<mapper resource="mapper/myuser.xml"/> 
 </mappers>           

注意:resource是以项目路径开始的

方式三:

<mappers>   
    <mapper url="http://xxx/xxx/xx.xml"/>
 </mappers>  

​ 注意:url是远程配置文件

方式四:

<mappers>  
	<mapper class="被代理对象(接口)的全路径名"/> 
</mappers>      

注意:此种方法要求mapper接口名称和mapper映射文件名称相同,且放在同一个包中

mybatis是通过this.mapperElement(root.evalNode("mappers"));方法,至于为什么调用该方法,可以看上一篇文章,介绍解析mybatis的核心配置文件的文章。

private void mapperElement(XNode parent) throws Exception {
   
        if (parent != null) {
   
            // 获取mappers标签的子标签
            Iterator var2 = parent.getChildren().iterator();
            while(true) {
   
                while(var2.hasNext()) {
   
                    // 循环遍历
                    XNode child = (XNode)var2.next();
                    String resource;
                    // 方式一:包扫描的方式
                    if ("package".equals(child.getName())) {
   
                        // 如果mappers标签的子标签是package,获取package的name属性。
                        resource = child.getStringAttribute("name");
                        // 通过name值,获取name下的所有mapper配置文件
                        this.configuration.addMappers(resource);
                    } else {
   
                        // 获取resource、url和class对应属性值
                        resource = child.getStringAttribute("resource");
                        String url = child.getStringAttribute("url");
                        String mapperClass = child.getStringAttribute("class");
                        XMLMapperBuilder mapperParser;
                        InputStream inputStream;
                        //方式二:通过resource属性加载文件
                        if (resource != null && url == null && mapperClass == null) {
   
                            ErrorContext.instance().resource(resource);
                            inputStream = Resources.getResourceAsStream(resource);
                            mapperParser = new XMLMapperBuilder(inputStream, this.configuration, resource, this.configuration.getSqlFragments());
                            mapperParser.parse();
                        } else if (resource == null && url != null && mapperClass == null) {
   
                            //方式三:通过url属性加载文件
                            ErrorContext.instance().resource(url);
                            inputStream = Resources.getUrlAsStream(url);
                            mapperParser = new XMLMapperBuilder(inputStream, this.configuration, url, this.configuration.getSqlFragments());
                            mapperParser.parse();
                        } else {
   
                            if (resource != null || url != null || mapperClass == null) {
   
                                throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");
                            }
							// 方式四:通过class属性加载文件
                            Class<?> mapperInterface = Resources.classForName(mapperClass);
                            this.configuration.addMapper(mapperInterface);
                        }
                    }
                }

                return;
            }
        }
    }

方式一包扫描的方式,查看this.configuration.addMappers(resource);方法:

public void addMappers(String packageName) {
   
        this.mapperRegistry.addMappers(packageName);
    }
public void addMappers(String packageName) {
   
        this.addMappers(packageName, Object.class);
    }
public void addMappers(String packageName, Class<?> superType) {
   
       // 又是创建ResolverUtil工具类
        ResolverUtil<Class<?>> resolverUtil = new ResolverUtil();
       // 根据packageName获取,该包下的所有Class
        resolverUtil.find(new IsA(superType), packageName);
    	// 遍历Class
        Set<Class<? extends Class<?>>> mapperSet = resolverUtil.getClasses();
        Iterator var5 = mapperSet.iterator();
        while(var5.hasNext()) {
   
            Class<?> mapperClass = (Class)var5.next();
            // 方式四通过class属性加载文件
            this.addMapper(mapperClass);
        }

    }

方式四通过class属性加载文件,查看this.configuration.addMapper(mapperInterface);和上面的this.addMapper(mapperClass);方法最终都是调用如下方法:

public <T> void addMapper(Class<T> type) {
   
        // 判断Class是否是接口
        if (type.isInterface()) {
   
            // 配置文件是否正在加载
            if (this.hasMapper(type)) {
   
                // 正在加载过就抛出异常
                throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
            }

            boolean loadCompleted = false;

            try {
   
                // 加载之前,存入this.knownMappers中
                this.knownMappers.put(type, new MapperProxyFactory(type));
                // 创建MapperAnnotationBuilder对象,
                MapperAnnotationBuilder parser = new MapperAnnotationBuilder(this.config, type);
                // 解析
                parser.parse();
                loadCompleted = true;
            } finally {
   
                // 完成解析
                if (!loadCompleted) {
   
                    // 移出
                    this.knownMappers.remove(type);
                }

            }
        }

    }

配置文件是否加载过,查看this.hasMapper(type)方法:

public <T> boolean hasMapper(Class<T> type) {
   
   		 // 加载之前,存入this.knownMappers中
        return this.knownMappers.containsKey(type);
    }

创建MapperAnnotationBuilder对象

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;
    }

解析查看parser.parse();方法:

public void parse() {
   
        String resource = this.type.toString();
    	// 判断configuration的this.loadedResources集合中是否包含resource
        if (!this.configuration.isResourceLoaded(resource)) {
   
             // ----重点:加载该命名空间下对应的xml
            this.loadXmlResource();
            // ---- 下面是注解方式
            // 为configuration的this.loadedResources集合添加resource
            this.configuration.addLoadedResource(resource);
            // 设置命名空间,即类的全路径名
            this.assistant.setCurrentNamespace(this.type.getName());
            // 解析二级缓存注解CacheNamespace
            this.parseCache();
            // 解析二级缓存注解CacheNamespaceRef
            this.parseCacheRef();
            // 获取类的的所有方法
            Method[] var2 = this.type.getMethods();
            int var3 = var2.length;
            // 遍历所有方法
            for(int var4 = 0; var4 < var3; ++var4) {
   
                Method method = var2[var4];
                // 检查下method类型,不能是桥接方法和接口中的默认方法
                if (this.canHaveStatement(method)) {
   
                    // select操作解析@select,@SelectProvider注释方法中的带有@ResultMap的方法
                    if (this.getAnnotationWrapper(method, false, Select.class, SelectProvider.class).isPresent() && method.getAnnotation(ResultMap.class) == null) {
   
                        this.parseResultMap(method);
                    }

                    try {
   
                        // 解析Statement
                        this.parseStatement(method);
                    } catch (IncompleteElementException var7) {
   
                        this.configuration.addIncompleteMethod(new MethodResolver(this, method));
                    }
                }
            }
        }
		// 解析IncompleteMethod,解析失败的方法。
        this.parsePendingMethods();
    }

查看加载该命名空间下对应的xml的this.loadXmlResource();方法:

private void loadXmlResource() {
   
        if (!this.configuration.isResourceLoaded("namespace:" + this.type.getName())) {
   
            // 将包名中的"."转换为"/"
            String xmlResource = this.type.getName().replace('.', '/') + ".xml";
            InputStream inputStream = this.type.getResourceAsStream("/" + xmlResource);
            if (inputStream == null) {
   
                try {
   
                    inputStream = Resources.getResourceAsStream(this.type.getClassLoader(), xmlResource);
                } catch (IOException var4) {
   
                }
            }

            if (inputStream != null) {
   
                // 以流的形式创建XMLMapperBuilder对象,
                XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, this.assistant.getConfiguration(), xmlResource, this.configuration.getSqlFragments(), this.type.getName());
                xmlParser.parse();
            }
        }

    }

通过上面的方法可以看出,和方式二通过resource属性加载文件方式三通过url属性加载文件是一样的,继续跟进 xmlParser.parse();方法:

 public void parse() {
   
         // 该节点是否被解析过或加载过
        if (!this.configuration.isResourceLoaded(this.resource)) {
   
            //解析mapper节点
            this.configurationElement(this.parser.evalNode("/mapper"));
            // 加入到已经解析的列表,防止重复解析
            this.configuration.addLoadedResource(this.resource);
            // 将mapper注册给Configuration
            this.bindMapperForNamespace();
        }
		// 下面分别用来处理失败的<resultMap>、<cache-ref>、SQL语句
        this.parsePendingResultMaps();
        this.parsePendingCacheRefs();
        this.parsePendingStatements();
    }

查看如何解析mapper节点的this.configurationElement(this.parser.evalNode("/mapper"));方法:

private void configurationElement(XNode context) {
   
        try {
   
            // 获取mapper标签的namespace属性
            String namespace = context.getStringAttribute("namespace");
            if (namespace != null && !namespace.isEmpty()) {
   
                // 绑定当前命名空间
                this.builderAssistant.setCurrentNamespace(namespace);
                // 解析cache-ref标签
                this.cacheRefElement(context.evalNode("cache-ref"));
                // 解析cache标签
                this.cacheElement(context.evalNode("cache"));
                // 解析parameterMap标签
       
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值