Mybatis源码解析(三)

开课吧开课吧锤锤2021-03-03 10:49

    Java是一门全球范围内使用最广泛的,面向对象的编程语言。Java语言具有功能强大和简单易用两个特征,它作为面向对象编程语言系列的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程。

Java

    2.3mappers的扫描与解析

    this.mapperElement(root.evalNode("mappers"));

    通过代码我们可以知道,这里主要是解析mappers标签的。我们先来看一下mappers标签里面是什么:

    <mappers>

    <mapperresource="UserMapper.xml"/>

    </mappers>

    所以他会去解析UserMapper.xml中的内容:

    <mappernamespace="com.cl.mybatis.learn.mapper.UserMapper">

    <selectid="selectById"resultType="com.cl.mybatis.learn.user.User">

    select*fromuserwhereid=#{id}

    </select>

    </mapper>

    我们看一下源代码:

    privatevoidmapperElement(XNodeparent)throwsException{

    if(parent!=null){

    for(XNodechild:parent.getChildren()){

    /**如果子节点是配置的package,那么进行包自动扫描处理*/

    if("package".equals(child.getName())){

    StringmapperPackage=child.getStringAttribute("name");

    configuration.addMappers(mapperPackage);

    }else{

    Stringresource=child.getStringAttribute("resource");

    Stringurl=child.getStringAttribute("url");

    StringmapperClass=child.getStringAttribute("class");

    /**如果子节点配置的是resource、url、mapperClass,本文我们使用的是resource*/

    if(resource!=null&&url==null&&mapperClass==null){

    ErrorContext.instance().resource(resource);

    InputStreaminputStream=Resources.getResourceAsStream(resource);

    XMLMapperBuildermapperParser=newXMLMapperBuilder(inputStream,configuration,resource,configuration.getSqlFragments());

    mapperParser.parse();

    }elseif(resource==null&&url!=null&&mapperClass==null){

    ErrorContext.instance().resource(url);

    InputStreaminputStream=Resources.getUrlAsStream(url);

    /**解析resource引入的另外一个xml文件*/

    XMLMapperBuildermapperParser=newXMLMapperBuilder(inputStream,configuration,url,configuration.getSqlFragments());

    mapperParser.parse();

    }elseif(resource==null&&url==null&&mapperClass!=null){

    Class<?>mapperInterface=Resources.classForName(mapperClass);

    configuration.addMapper(mapperInterface);

    }else{

    thrownewBuilderException("Amapperelementmayonlyspecifyaurl,resourceorclass,butnotmorethanone.");

    }

    }

    }

    }

    }

    下面我们具体看一下他是如何解析另一个xml文件的:

    publicvoidparse(){

    if(!configuration.isResourceLoaded(resource)){

    /**解析sql语句*/

    configurationElement(parser.evalNode("/mapper"));

    configuration.addLoadedResource(resource);

    /**解析名称空间,实际上就是对应绑定的接口类*/

    bindMapperForNamespace();

    }

    parsePendingResultMaps();

    parsePendingChacheRefs();

    parsePendingStatements();

    }

    下面我们来看一下configurationElement(parser.evalNode("/mapper"))到底做了什么:

    publicvoidparseStatementNode(){

    Stringid=context.getStringAttribute("id");

    StringdatabaseId=context.getStringAttribute("databaseId");

    if(!databaseIdMatchesCurrent(id,databaseId,this.requiredDatabaseId))return;

    IntegerfetchSize=context.getIntAttribute("fetchSize");

    Integertimeout=context.getIntAttribute("timeout");

    StringparameterMap=context.getStringAttribute("parameterMap");

    StringparameterType=context.getStringAttribute("parameterType");

    Class<?>parameterTypeClass=resolveClass(parameterType);

    StringresultMap=context.getStringAttribute("resultMap");

    StringresultType=context.getStringAttribute("resultType");

    Stringlang=context.getStringAttribute("lang");

    LanguageDriverlangDriver=getLanguageDriver(lang);

    Class<?>resultTypeClass=resolveClass(resultType);

    StringresultSetType=context.getStringAttribute("resultSetType");

    StatementTypestatementType=StatementType.valueOf(context.getStringAttribute("statementType",StatementType.PREPARED.toString()));

    ResultSetTyperesultSetTypeEnum=resolveResultSetType(resultSetType);

    StringnodeName=context.getNode().getNodeName();

    SqlCommandTypesqlCommandType=SqlCommandType.valueOf(nodeName.toUpperCase(Locale.ENGLISH));

    booleanisSelect=sqlCommandType==SqlCommandType.SELECT;

    booleanflushCache=context.getBooleanAttribute("flushCache",!isSelect);

    booleanuseCache=context.getBooleanAttribute("useCache",isSelect);

    booleanresultOrdered=context.getBooleanAttribute("resultOrdered",false);

    //IncludeFragmentsbeforeparsing

    XMLIncludeTransformerincludeParser=newXMLIncludeTransformer(configuration,builderAssistant);

    includeParser.applyIncludes(context.getNode());

    //ParseselectKeyafterincludesandremovethem.

    processSelectKeyNodes(id,parameterTypeClass,langDriver);

    //ParsetheSQL(pre:<selectKey>and<include>wereparsedandremoved)

    SqlSourcesqlSource=langDriver.createSqlSource(configuration,context,parameterTypeClass);

    StringresultSets=context.getStringAttribute("resultSets");

    StringkeyProperty=context.getStringAttribute("keyProperty");

    StringkeyColumn=context.getStringAttribute("keyColumn");

    KeyGeneratorkeyGenerator;

    StringkeyStatementId=id+SelectKeyGenerator.SELECT_KEY_SUFFIX;

    keyStatementId=builderAssistant.applyCurrentNamespace(keyStatementId,true);

    if(configuration.hasKeyGenerator(keyStatementId)){

    keyGenerator=configuration.getKeyGenerator(keyStatementId);

    }else{

    keyGenerator=context.getBooleanAttribute("useGeneratedKeys",

    configuration.isUseGeneratedKeys()&&SqlCommandType.INSERT.equals(sqlCommandType))

    ?newJdbc3KeyGenerator():newNoKeyGenerator();

    }

    builderAssistant.addMappedStatement(id,sqlSource,statementType,sqlCommandType,

    fetchSize,timeout,parameterMap,parameterTypeClass,resultMap,resultTypeClass,

    resultSetTypeEnum,flushCache,useCache,resultOrdered,

    keyGenerator,keyProperty,keyColumn,databaseId,langDriver,resultSets);

    }

    publicMappedStatementaddMappedStatement(

    Stringid,

    SqlSourcesqlSource,

    StatementTypestatementType,

    SqlCommandTypesqlCommandType,

    IntegerfetchSize,

    Integertimeout,

    StringparameterMap,

    Class<?>parameterType,

    StringresultMap,

    Class<?>resultType,

    ResultSetTyperesultSetType,

    booleanflushCache,

    booleanuseCache,

    booleanresultOrdered,

    KeyGeneratorkeyGenerator,

    StringkeyProperty,

    StringkeyColumn,

    StringdatabaseId,

    LanguageDriverlang,

    StringresultSets){

    if(unresolvedCacheRef)thrownewIncompleteElementException("Cache-refnotyetresolved");

    id=applyCurrentNamespace(id,false);

    booleanisSelect=sqlCommandType==SqlCommandType.SELECT;

    MappedStatement.BuilderstatementBuilder=newMappedStatement.Builder(configuration,id,sqlSource,sqlCommandType);

    statementBuilder.resource(resource);

    statementBuilder.fetchSize(fetchSize);

    statementBuilder.statementType(statementType);

    statementBuilder.keyGenerator(keyGenerator);

    statementBuilder.keyProperty(keyProperty);

    statementBuilder.keyColumn(keyColumn);

    statementBuilder.databaseId(databaseId);

    statementBuilder.lang(lang);

    statementBuilder.resultOrdered(resultOrdered);

    statementBuilder.resulSets(resultSets);

    setStatementTimeout(timeout,statementBuilder);

    setStatementParameterMap(parameterMap,parameterType,statementBuilder);

    setStatementResultMap(resultMap,resultType,resultSetType,statementBuilder);

    setStatementCache(isSelect,flushCache,useCache,currentCache,statementBuilder);

    MappedStatementstatement=statementBuilder.build();

    configuration.addMappedStatement(statement);

    returnstatement;

    }

    通过解析一个个的标签,最终将sql语句的所有信息封装成MappedStatement对象,然后存储在configuration对象中。

    那么bindMapperForNamespace();又做了什么呢?

    privatevoidbindMapperForNamespace(){

    Stringnamespace=builderAssistant.getCurrentNamespace();

    if(namespace!=null){

    Class<?>boundType=null;

    try{

    boundType=Resources.classForName(namespace);

    }catch(ClassNotFoundExceptione){

    //ignore,boundtypeisnotrequired

    }

    if(boundType!=null){

    if(!configuration.hasMapper(boundType)){

    //Springmaynotknowtherealresourcenamesowesetaflag

    //topreventloadingagainthisresourcefromthemapperinterface

    //lookatMapperAnnotationBuilder#loadXmlResource

    configuration.addLoadedResource("namespace:"+namespace);

    configuration.addMapper(boundType);

    }

    }

    }

    }

    实际上就是解析该sql对应的class,并把该class放到configuration中的mapperRegistry中。实际上mybatis的所有配置信息以及运行时的配置参数全部都保存在configuration对象中。

    所以整个流程可以用如下的时序图表示:

Java

    以上内容由开课吧老师左撇子小哥哥提供,更多Java教程尽在开课吧广场Java教程频道。

有用
分享