夜勿语资料

本文主要介绍夜勿语资料 方法和在新技术下所面对的“挑战”,方便大家深入理解夜勿语资料 过程。本文也将分享夜勿语资料 所遇到的问题和应对策略,怎么解决怎么做的问题。
通过深入本文可以理解代码原理,进行代码文档的下载,也可以查看相应 Demo 部署效果。

@

目录
  • 前言
  • 正文
    • 配置解析
      • 1. cacheRefElement/cacheElement
      • 2. resultMapElements
      • 3. sqlElement
      • 4. buildStatementFromContext
  • 总结

前言

上一篇分析了Mybatis的基础组件,Mybatis的运行调用就是建立在这些基础组件之上的,那它的执行原理又是怎样的呢?在往下之前不妨先思考下如果是你会怎么实现。

正文

熟悉Mybatis的都知道,在使用Mybatis时需要配置一个mybatis-config.xml文件,另外还需要定义Mapper接口和Mapper.xml文件,在config文件中引入或扫描对应的包才能被加载解析(现在由于大多是SpringBoot工程,基本上都不会配置config文件,而是通过注解进行扫描就行了,但本质上的实现和xml配置没有太大区别,所以本篇仍以xml配置方式进行分析。),所以Mybatis的第一个阶段必然是要去加载并解析配置文件,这个阶段在项目启动时就应该完成,后面直接调用即可。加载完成之后,自然就是等待调用,但是我们在项目中只会定义Mapper接口和Mapper.xml文件,那具体的实现类在哪呢?Mybatis是通过动态代理实现的,所以第二个阶段应该是生成Mapper接口的代理实现类。通过调用代理类,最终会生成对应的sql访问数据库并获取结果,所以最后一个阶段就是SQL解析(参数映射、SQL映射、结果映射)。本文主要分析配置解析阶段。

配置解析

Mybatis可以通过下面的方式解析配置文件:

    final String resource = "org/apache/ibatis/builder/MapperConfig.xml";     final Reader reader = Resources.getResourceAsReader(resource);     SqlSessionFactory sqlMapper = new SqlSessionFactoryBuilder().build(reader); 

所以入口就是build方法(从名字可以看出使用了建造者模式,它和工厂模式一样,也是解用于创建对象的一种模式,不过与工厂模式不一样的是,前者需要我们自己参与构建的细节,而后者则不需要):

  public SqlSessionFactory build(Reader reader, String environment, Properties properties) {     try {       //读取配置文件       XMLConfigBuilder parser = new XMLConfigBuilder(reader, environment, properties);       return build(parser.parse());//解析配置文件得到configuration对象,并返回SqlSessionFactory     } catch (Exception e) {       throw ExceptionFactory.wrapException("Error building SqlSession.", e);     } finally {       ErrorContext.instance().reset();       try {         reader.close();       } catch (IOException e) {         // Intentionally ignore. Prefer previous error.       }     }   } 

这里先是创建了一个XMLConfigBuilder对象,这个对象就是用来加载解析config文件的,先看看它的构造方法中做了些什么事情:

  public XMLConfigBuilder(Reader reader, String environment, Properties props) {     this(new XPathParser(reader, true, props, new XMLMapperEntityResolver()), environment, props);   }    private XMLConfigBuilder(XPathParser parser, String environment, Properties props) {     super(new Configuration());     ErrorContext.instance().resource("SQL Mapper Configuration");     this.configuration.setVariables(props);     this.parsed = false;     this.environment = environment;     this.parser = parser;   } 

需要注意的是这里创建了一个Configuration对象,他就是Mybatis的核心CPU,保存了所有的配置信息,在后面的执行阶段所需要的信息都是从这个类取的,因为这个类比较大,这里就不贴详细代码了,读者请务必阅读源码熟悉该类。因为这个类对象保存了所有的配置信息,那么必然这个类是全局单例的,事实上这个对象的创建也只有这里一个入口,保证了全局唯一。
在该类的构造方法中,首先就注册了核心组件的别名和对应的类映射关系:

  public Configuration() {     typeAliasRegistry.registerAlias("JDBC", JdbcTransactionFactory.class);     typeAliasRegistry.registerAlias("MANAGED", ManagedTransactionFactory.class);      typeAliasRegistry.registerAlias("JNDI", JndiDataSourceFactory.class);     typeAliasRegistry.registerAlias("POOLED", PooledDataSourceFactory.class);     typeAliasRegistry.registerAlias("UNPOOLED", UnpooledDataSourceFactory.class);  	......省略      languageRegistry.setDefaultDriverClass(XMLLanguageDriver.class);     languageRegistry.register(RawLanguageDriver.class);   } 

而注册类在实例化时同样也注册了一些基础类型的别名映射:

  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("ResultSet", ResultSet.class);   } 

看到这相信你就知道parameterType和resultType属性的简写是怎么实现的了。回到主流程,进入到parser.parse方法中:

  public Configuration parse() {     if (parsed) {       throw new BuilderException("Each XMLConfigBuilder can only be used once.");     }     parsed = true;     parseConfiguration(parser.evalNode("/configuration"));     return configuration;   }    private void parseConfiguration(XNode root) {     try {       //issue #117 read properties first      //解析<properties>节点       propertiesElement(root.evalNode("properties"));       //解析<settings>节点       Properties settings = settingsAsProperties(root.evalNode("settings"));       loadCustomVfs(settings);       //解析<typeAliases>节点       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);//将settings填充到configuration       // read it after objectFactory and objectWrapperFactory issue #631       //解析<environments>节点       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);     }   } 

这个方法就是去解析配置文件中的各个节点,并将其封装到Configuration对象中去,前面的节点解析没啥好说的,自己看一下就明白了,重点看一下最后一个对mapper节点的解析,这个就是加载我们的Mapper.xml文件:

  <mappers>     <mapper resource="org/apache/ibatis/builder/AuthorMapper.xml"/>     <mapper resource="org/apache/ibatis/builder/BlogMapper.xml"/>     <mapper resource="org/apache/ibatis/builder/CachedAuthorMapper.xml"/>     <mapper resource="org/apache/ibatis/builder/PostMapper.xml"/>     <mapper resource="org/apache/ibatis/builder/NestedBlogMapper.xml"/>   </mappers> 
 private void mapperElement(XNode parent) throws Exception {     if (parent != null) {       for (XNode child : parent.getChildren()) {//处理mapper子节点         if ("package".equals(child.getName())) {//package子节点           String mapperPackage = child.getStringAttribute("name");           configuration.addMappers(mapperPackage);         } else {//获取<mapper>节点的resource、url或mClass属性这三个属性互斥           String resource = child.getStringAttribute("resource");           String url = child.getStringAttribute("url");           String mapperClass = child.getStringAttribute("class");           if (resource != null && url == null && mapperClass == null) {//如果resource不为空             ErrorContext.instance().resource(resource);             InputStream inputStream = Resources.getResourceAsStream(resource);//加载mapper文件             //实例化XMLMapperBuilder解析mapper映射文件             XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());             mapperParser.parse();           } else if (resource == null && url != null && mapperClass == null) {//如果url不为空             ErrorContext.instance().resource(url);             InputStream inputStream = Resources.getUrlAsStream(url);//加载mapper文件             //实例化XMLMapperBuilder解析mapper映射文件             XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, url, configuration.getSqlFragments());             mapperParser.parse();           } else if (resource == null && url == null && mapperClass != null) {//如果class不为空             Class<?> mapperInterface = Resources.classForName(mapperClass);//加载class对象             configuration.addMapper(mapperInterface);//向代理中心注册mapper           } else {             throw new BuilderException("A mapper element may only specify a url, resource or class, but not more than one.");           }         }       }     }   } 

从上面的代码中我们可以看到这里有两种配置方式,一种是配置package子节点,即扫描并批量加载指定的包中的文件;另一种则是使用mapper子节点引入单个文件,而mapper节点又可以配置三种属性:resource、url、class。而解析XML的核心类是XMLMapperBuilder,进入parse方法:

  public void parse() { 	//判断是否已经加载该配置文件     if (!configuration.isResourceLoaded(resource)) {       configurationElement(parser.evalNode("/mapper"));//处理mapper节点       configuration.addLoadedResource(resource);//将mapper文件添加到configuration.loadedResources中       bindMapperForNamespace();//注册mapper接口     }     //处理解析失败的ResultMap节点     parsePendingResultMaps();     //处理解析失败的CacheRef节点     parsePendingCacheRefs();     //处理解析失败的Sql语句节点     parsePendingStatements();   }    private void configurationElement(XNode context) {     try {     	//获取mapper节点的namespace属性       String namespace = context.getStringAttribute("namespace");       if (namespace == null || namespace.equals("")) {         throw new BuilderException("Mapper's namespace cannot be empty");       }       //设置builderAssistant的namespace属性       builderAssistant.setCurrentNamespace(namespace);       //解析cache-ref节点       cacheRefElement(context.evalNode("cache-ref"));       //重点分析 :解析cache节点----------------1-------------------       cacheElement(context.evalNode("cache"));       //解析parameterMap节点(已废弃)       parameterMapElement(context.evalNodes("/mapper/parameterMap"));       //重点分析 :解析resultMap节点(基于数据结果去理解)----------------2-------------------       resultMapElements(context.evalNodes("/mapper/resultMap"));       //解析sql节点       sqlElement(context.evalNodes("/mapper/sql"));       //重点分析 :解析select、insert、update、delete节点 ----------------3-------------------       buildStatementFromContext(context.evalNodes("select|insert|update|delete"));     } catch (Exception e) {       throw new BuilderException("Error parsing Mapper XML. The XML location is '" + resource + "'. Cause: " + e, e);     }   } 

核心的处理逻辑又是通过configurationElement实现的,接下来挨个分析几个重要节点解析过程。

1. cacheRefElement/cacheElement

这两个节点都是解析二级缓存配置的,前者是引用其它namespace的二级缓存,后者则是直接开启当前namespace的二级缓存,所以重点看后者:

  private void cacheElement(XNode context) throws Exception {     if (context != null) {       //获取cache节点的type属性,默认为PERPETUAL       String type = context.getStringAttribute("type", "PERPETUAL");       //找到type对应的cache接口的实现       Class<? extends Cache> typeClass = typeAliasRegistry.resolveAlias(type);       //读取eviction属性,既缓存的淘汰策略,默认LRU       String eviction = context.getStringAttribute("eviction", "LRU");       //根据eviction属性,找到装饰器       Class<? extends Cache> evictionClass = typeAliasRegistry.resolveAlias(eviction);       //读取flushInterval属性,既缓存的刷新周期       Long flushInterval = context.getLongAttribute("flushInterval");       //读取size属性,既缓存的容量大小       Integer size = context.getIntAttribute("size");      //读取readOnly属性,既缓存的是否只读       boolean readWrite = !context.getBooleanAttribute("readOnly", false);       //读取blocking属性,既缓存的是否阻塞       boolean blocking = context.getBooleanAttribute("blocking", false);       Properties props = context.getChildrenAsProperties();       //通过builderAssistant创建缓存对象,并添加至configuration       builderAssistant.useNewCache(typeClass, evictionClass, flushInterval, size, readWrite, blocking, props);     }   }    public Cache useNewCache(Class<? extends Cache> typeClass,       Class<? extends Cache> evictionClass,       Long flushInterval,       Integer size,       boolean readWrite,       boolean blocking,       Properties props) { 	//经典的建造起模式,创建一个cache对象     Cache cache = new CacheBuilder(currentNamespace)         .implementation(valueOrDefault(typeClass, PerpetualCache.class))         .addDecorator(valueOrDefault(evictionClass, LruCache.class))         .clearInterval(flushInterval)         .size(size)         .readWrite(readWrite)         .blocking(blocking)         .properties(props)         .build();     //将缓存添加至configuration,注意二级缓存以命名空间为单位进行划分     configuration.addCache(cache);     currentCache = cache;     return cache;   }    public void addCache(Cache cache) {     caches.put(cache.getId(), cache);   } 

从这里我们可以看到默认创建了PerpetualCache对象,这个是缓存的基本实现类,然后根据配置给缓存加上装饰器,默认会装饰LRU。配置解析完成后,才会通过MapperBuilderAssistant类真正创建缓存对象并添加到Configuration对象中。为什么这里要通过MapperBuilderAssistant对象创建缓存对象呢?从名字可以看出它是XMLMapperBuilder的协助者,因为XML的解析和配置对象的装填是非常繁琐的一个过程,如果全部由一个类来完成,会非常的臃肿难看,并且耦合性较高,所以这里又雇佣了一个“协助者”。

2. resultMapElements

  private void resultMapElements(List<XNode> list) throws Exception { 	//遍历所有的resultmap节点     for (XNode resultMapNode : list) {       try {     	 //解析具体某一个resultMap节点         resultMapElement(resultMapNode);       } catch (IncompleteElementException e) {         // ignore, it will be retried       }     }   }    private ResultMap resultMapElement(XNode resultMapNode, List<ResultMapping> additionalResultMappings) throws Exception {     ErrorContext.instance().activity("processing " + resultMapNode.getValueBasedIdentifier());     //获取resultmap节点的id属性     String id = resultMapNode.getStringAttribute("id",         resultMapNode.getValueBasedIdentifier());     //获取resultmap节点的type属性     String type = resultMapNode.getStringAttribute("type",         resultMapNode.getStringAttribute("ofType",             resultMapNode.getStringAttribute("resultType",                 resultMapNode.getStringAttribute("javaType"))));     //获取resultmap节点的extends属性,描述继承关系     String extend = resultMapNode.getStringAttribute("extends");     //获取resultmap节点的autoMapping属性,是否开启自动映射     Boolean autoMapping = resultMapNode.getBooleanAttribute("autoMapping");     //从别名注册中心获取entity的class对象     Class<?> typeClass = resolveClass(type);     Discriminator discriminator = null;     //记录子节点中的映射结果集合     List<ResultMapping> resultMappings = new ArrayList<>();     resultMappings.addAll(additionalResultMappings);     //从xml文件中获取当前resultmap中的所有子节点,并开始遍历     List<XNode> resultChildren = resultMapNode.getChildren();     for (XNode resultChild : resultChildren) {       if ("constructor".equals(resultChild.getName())) {//处理<constructor>节点         processConstructorElement(resultChild, typeClass, resultMappings);       } else if ("discriminator".equals(resultChild.getName())) {//处理<discriminator>节点         discriminator = processDiscriminatorElement(resultChild, typeClass, resultMappings);       } else {//处理<id> <result> <association> <collection>节点         List<ResultFlag> flags = new ArrayList<>();         if ("id".equals(resultChild.getName())) {           flags.add(ResultFlag.ID);//如果是id节点,向flags中添加元素         }         //创建ResultMapping对象并加入resultMappings集合中         resultMappings.add(buildResultMappingFromContext(resultChild, typeClass, flags));       }     }     //实例化resultMap解析器     ResultMapResolver resultMapResolver = new ResultMapResolver(builderAssistant, id, typeClass, extend, discriminator, resultMappings, autoMapping);     try {       //通过resultMap解析器实例化resultMap并将其注册到configuration对象       return resultMapResolver.resolve();     } catch (IncompleteElementException  e) {       configuration.addIncompleteResultMap(resultMapResolver);       throw e;     }   } 

这个方法同样先是解析节点的属性,然后通过buildResultMappingFromContext方法创建ResultMapping对象并封装到ResultMapResolver中去,最后还是通过MapperBuilderAssistant实例化ResultMap对象并添加到ConfigurationresultMaps属性中:

  public ResultMap addResultMap(       String id,       Class<?> type,       String extend,       Discriminator discriminator,       List<ResultMapping> resultMappings,       Boolean autoMapping) { 	 //完善id,id的完整格式是"namespace.id"     id = applyCurrentNamespace(id, false);     //获得父类resultMap的完整id     extend = applyCurrentNamespace(extend, true);      //针对extend属性的处理     if (extend != null) {       if (!configuration.hasResultMap(extend)) {         throw new IncompleteElementException("Could not find a parent resultmap with id '" + extend + "'");       }       ResultMap resultMap = configuration.getResultMap(extend);       List<ResultMapping> extendedResultMappings = new ArrayList<>(resultMap.getResultMappings());       extendedResultMappings.removeAll(resultMappings);       // Remove parent constructor if this resultMap declares a constructor.       boolean declaresConstructor = false;       for (ResultMapping resultMapping : resultMappings) {         if (resultMapping.getFlags().contains(ResultFlag.CONSTRUCTOR)) {           declaresConstructor = true;           break;         }       }       if (declaresConstructor) {         Iterator<ResultMapping> extendedResultMappingsIter = extendedResultMappings.iterator();         while (extendedResultMappingsIter.hasNext()) {           if (extendedResultMappingsIter.next().getFlags().contains(ResultFlag.CONSTRUCTOR)) {             extendedResultMappingsIter.remove();           }         }       }       //添加需要被继承下来的resultMapping对象结合       resultMappings.addAll(extendedResultMappings);     }     //通过建造者模式实例化resultMap,并注册到configuration.resultMaps中     ResultMap resultMap = new ResultMap.Builder(configuration, id, type, resultMappings, autoMapping)         .discriminator(discriminator)         .build();     configuration.addResultMap(resultMap);     return resultMap;   } 

3. sqlElement

解析SQL节点,只是将其缓存到XMLMapperBuildersqlFragments属性中。

4. buildStatementFromContext

  private void buildStatementFromContext(List<XNode> list) {     if (configuration.getDatabaseId() != null) {       buildStatementFromContext(list, configuration.getDatabaseId());     }     buildStatementFromContext(list, null);   }    private void buildStatementFromContext(List<XNode> list, String requiredDatabaseId) {     for (XNode context : list) {       //创建XMLStatementBuilder 专门用于解析sql语句节点       final XMLStatementBuilder statementParser = new XMLStatementBuilder(configuration, builderAssistant, context, requiredDatabaseId);       try {     	//解析sql语句节点         statementParser.parseStatementNode();       } catch (IncompleteElementException e) {         configuration.addIncompleteStatement(statementParser);       }     }   } 

这个方法是重点,通过XMLStatementBuilder对象解析select、update、insert、delete节点:

  public void parseStatementNode() { 	//获取sql节点的id     String id = context.getStringAttribute("id");     String databaseId = context.getStringAttribute("databaseId");      if (!databaseIdMatchesCurrent(id, databaseId, this.requiredDatabaseId)) {       return;     }     /*获取sql节点的各种属性*/     Integer fetchSize = context.getIntAttribute("fetchSize");     Integer timeout = context.getIntAttribute("timeout");     String parameterMap = context.getStringAttribute("parameterMap");     String parameterType = context.getStringAttribute("parameterType");     Class<?> parameterTypeClass = resolveClass(parameterType);     String resultMap = context.getStringAttribute("resultMap");     String resultType = context.getStringAttribute("resultType");     String lang = context.getStringAttribute("lang");     LanguageDriver langDriver = getLanguageDriver(lang);      Class<?> resultTypeClass = resolveClass(resultType);     String resultSetType = context.getStringAttribute("resultSetType");     StatementType statementType = StatementType.valueOf(context.getStringAttribute("statementType", StatementType.PREPARED.toString()));     ResultSetType resultSetTypeEnum = resolveResultSetType(resultSetType);                //根据sql节点的名称获取SqlCommandType(INSERT, UPDATE, DELETE, SELECT)     String nodeName = context.getNode().getNodeName();     SqlCommandType sqlCommandType = SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));     boolean isSelect = sqlCommandType == SqlCommandType.SELECT;     boolean flushCache = context.getBooleanAttribute("flushCache", !isSelect);     boolean useCache = context.getBooleanAttribute("useCache", isSelect);     boolean resultOrdered = context.getBooleanAttribute("resultOrdered", false);      // Include Fragments before parsing     //在解析sql语句之前先解析<include>节点     XMLIncludeTransformer includeParser = new XMLIncludeTransformer(configuration, builderAssistant);     includeParser.applyIncludes(context.getNode());      // Parse selectKey after includes and remove them.     //在解析sql语句之前,处理<selectKey>子节点,并在xml节点中删除     processSelectKeyNodes(id, parameterTypeClass, langDriver);          // Parse the SQL (pre: <selectKey> and <include> were parsed and removed)     //解析sql语句是解析mapper.xml的核心,实例化sqlSource,使用sqlSource封装sql语句     SqlSource sqlSource = langDriver.createSqlSource(configuration, context, parameterTypeClass);     String resultSets = context.getStringAttribute("resultSets");//获取resultSets属性     String keyProperty = context.getStringAttribute("keyProperty");//获取主键信息keyProperty     String keyColumn = context.getStringAttribute("keyColumn");///获取主键信息keyColumn          //根据<selectKey>获取对应的SelectKeyGenerator的id     KeyGenerator keyGenerator;     String keyStatementId = id + SelectKeyGenerator.SELECT_KEY_SUFFIX;     keyStatementId = builderAssistant.applyCurrentNamespace(keyStatementId, true);               //获取keyGenerator对象,如果是insert类型的sql语句,会使用KeyGenerator接口获取数据库生产的id;     if (configuration.hasKeyGenerator(keyStatementId)) {       keyGenerator = configuration.getKeyGenerator(keyStatementId);     } else {       keyGenerator = context.getBooleanAttribute("useGeneratedKeys",           configuration.isUseGeneratedKeys() && SqlCommandType.INSERT.equals(sqlCommandType))           ? Jdbc3KeyGenerator.INSTANCE : NoKeyGenerator.INSTANCE;     }      //通过builderAssistant实例化MappedStatement,并注册至configuration对象     builderAssistant.addMappedStatement(id, sqlSource, statementType, sqlCommandType,         fetchSize, timeout, parameterMap, parameterTypeClass, resultMap, resultTypeClass,         resultSetTypeEnum, flushCache, useCache, resultOrdered,          keyGenerator, keyProperty, keyColumn, databaseId, langDriver, resultSets);   }    public MappedStatement addMappedStatement(       String id,       SqlSource sqlSource,       StatementType statementType,       SqlCommandType sqlCommandType,       Integer fetchSize,       Integer timeout,       String parameterMap,       Class<?> parameterType,       String resultMap,       Class<?> resultType,       ResultSetType resultSetType,       boolean flushCache,       boolean useCache,       boolean resultOrdered,       KeyGenerator keyGenerator,       String keyProperty,       String keyColumn,       String databaseId,       LanguageDriver lang,       String resultSets) {      if (unresolvedCacheRef) {       throw new IncompleteElementException("Cache-ref not yet resolved");     }      id = applyCurrentNamespace(id, false);     boolean isSelect = sqlCommandType == SqlCommandType.SELECT;      MappedStatement.Builder statementBuilder = new MappedStatement.Builder(configuration, id, sqlSource, sqlCommandType)         .resource(resource)         .fetchSize(fetchSize)         .timeout(timeout)         .statementType(statementType)         .keyGenerator(keyGenerator)         .keyProperty(keyProperty)         .keyColumn(keyColumn)         .databaseId(databaseId)         .lang(lang)         .resultOrdered(resultOrdered)         .resultSets(resultSets)         .resultMaps(getStatementResultMaps(resultMap, resultType, id))         .resultSetType(resultSetType)         .flushCacheRequired(valueOrDefault(flushCache, !isSelect))         .useCache(valueOrDefault(useCache, isSelect))         .cache(currentCache);      ParameterMap statementParameterMap = getStatementParameterMap(parameterMap, parameterType, id);     if (statementParameterMap != null) {       statementBuilder.parameterMap(statementParameterMap);     }      MappedStatement statement = statementBuilder.build();     configuration.addMappedStatement(statement);     return statement;   } 

同样的,最后根据sql语句和属性实例化MappedStatement对象,并添加到Configuration对象的mappedStatements属性中。

回到XMLMapperBuilder.parse方法中,在解析完xml之后又调用了bindMapperForNamespace方法:

  private void bindMapperForNamespace() { 	//获取命名空间     String namespace = builderAssistant.getCurrentNamespace();     if (namespace != null) {       Class<?> boundType = null;       try {     	//通过命名空间获取mapper接口的class对象         boundType = Resources.classForName(namespace);       } catch (ClassNotFoundException e) {         //ignore, bound type is not required       }       if (boundType != null) {         if (!configuration.hasMapper(boundType)) {//是否已经注册过该mapper接口?           // 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#loadXmlResource           //将命名空间添加至configuration.loadedResource集合中           configuration.addLoadedResource("namespace:" + namespace);           //将mapper接口添加到mapper注册中心           configuration.addMapper(boundType);         }       }     }   } 

这个方法中首先通过namespace拿到xml对应的Mapper接口类型,然后委托给Configuration类中的mapperRegistry注册动态的代理的工厂MapperProxyFactory

  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 {     	//实例化Mapper接口的代理工程类,并将信息添加至knownMappers         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.         //解析接口上的注解信息,并添加至configuration对象         MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);         parser.parse();         loadCompleted = true;       } finally {         if (!loadCompleted) {           knownMappers.remove(type);         }       }     }   } 

这个就是创建Mapper接口的动态代理对象的工厂类,所以Mapper的代理对象实际上并不是在启动的时候就创建好了,而是在方法调用时才会创建,为什么会这么设计呢?因为代理对象和SqlSession是一一对应的,而我们每一次调用Mapper的方法都是创建一个新的SqlSession,所以这里只是缓存了代理工厂对象。
代理工厂注册之后还通过MapperAnnotationBuilder类提供了对注解方式的支持,这里就不阐述了,结果就是将注解的值添加到Configuration中去。

总结

解析配置文件的流程虽然比较长,但逻辑一点都不复杂,主要就是获取xml配置的属性值,实例化不同的配置对象,并将这些配置都丢到Configuration对象中去,我们只需要重点
夜勿语资料部分资料来自网络,侵权毕设源码联系删除

区块链毕设网(www.qklbishe.com)全网最靠谱的原创区块链毕设代做网站
部分资料来自网络,侵权联系删除!
资源收费仅为搬运整理打赏费用,用户自愿支付 !
qklbishe.com区块链毕设代做网专注|以太坊fabric-计算机|java|毕业设计|代做平台 » 夜勿语资料

提供最优质的资源集合

立即查看 了解详情