admin 管理员组

文章数量: 887021

番外篇

有时候我们明明只注册了UserMapper.xml资源,但是并没有注册UserMapper.java接口,但是却可以进行使用,原因是因为mybatis底层帮我们自动进行转换了,反过来也是一样的,以下就把两种情况都看下

xml自动注册接口

xml解析入口为:org.apache.ibatis.builder.xml.XMLMapperBuilder#parsepublic void parse() {//如果没解析过才会进行解析,其实就是用set来进行判断if (!configuration.isResourceLoaded(resource)) {//解析mapper标记,这也是最核心的一个位置configurationElement(parser.evalNode("/mapper"));//放到mapper里面去configuration.addLoadedResource(resource);//绑定到接口上,如果namespace配置的是接口的话,这也是一个规范bindMapperForNamespace();}parsePendingResultMaps();parsePendingCacheRefs();parsePendingStatements();}在解析完以后mybatis帮我们注册对应的接口了, bindMapperForNamespaceprivate void bindMapperForNamespace() {//获取xml中配置的接口全类名String namespace = builderAssistant.getCurrentNamespace();if (namespace != null) {Class<?> boundType = null;try {//Class.forName获取Class对象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#loadXmlResourceconfiguration.addLoadedResource("namespace:" + namespace);configuration.addMapper(boundType);}}}}可见,这个过程还是比较简单的,我们只需要把xml文件中的namespace配置为接口的全类名即可

接口自动注册xml

接口自己注册位置:org.apache.ibatis.binding.MapperRegistry#addMapper//只有接口才会进来,因为要生成代理对象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);//解析,这里面又会把接口相关的再解析一遍,根据接口全类名去找关联的xml文件,//格式为 String xmlResource = type.getName().replace('.', '/') + ".xml";//所以要遵循它的规定parser.parse();loadCompleted = true;} finally {if (!loadCompleted) {knownMappers.remove(type);}}}}等接口注册完以后它又会去注册对应的xml文件   parse方法中的org.apache.ibatis.builder.annotation.MapperAnnotationBuilder#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 {/*** xmlResource 格式为   com/zxc/study/test/mapper/UserMapper.xml     它会把接口替换为具体的xml位置* 所以你的xml文件就必须遵循对应的规范了,否则mybatis就无法帮你自动解析xml* * 当然了,你也可以自己在这里定义mapper文件的地址去加载,但是原生的mybatis这里不提扩展,要改只能改源码,,一般也是不建议的* mybatis-spring和mybatis-plus可能覆盖了这里的方法进行可以指定吧*///或者对应的资源文件inputStream = Resources.getResourceAsStream(type.getClassLoader(), xmlResource);} catch (Exception e) {// ignore, resource is not required}if (inputStream != null) {//不为空时又调用原来的解析XMLMapperBuilder xmlParser = new XMLMapperBuilder(inputStream, assistant.getConfiguration(), xmlResource, configuration.getSqlFragments(), type.getName());xmlParser.parse();}}}其实原理也不难,只不过这里稍微要遵循下规范,比xml自动注册接口麻烦了一点点

以上便是本篇的内容,其实就是要遵循一些规范,代码单独就这个逻辑的话也并不是很复杂

本文标签: 番外篇