Mybatis源码分析: MapperMethod功能讲解

canmengqian

</div><!--end: blogTitle 博客的标题和副标题 -->
<div id="navigator">
新随笔
  • 联系
  • 订阅
  • 管理
  • 	<div class="blogStats">
    		
    		<span id="stats_post_count">随笔 - 
    

    32 
    文章 -

    评论 -
    2

    	</div><!--end: blogStats -->
    </div><!--end: navigator 博客导航栏 -->
    

    Mybatis源码分析: MapperMethod功能讲解(1)

            </h1>
            <div class="clear"></div>
            <div class="postBody">
    

    Mybatis源码分析: MapperMethod功能讲解(1)

          MapperMethod主要的功能是执行SQL的相关操作,在初始化时会实例化两个组件Sql命令(SqlCommand)和方法签名(MethodSignature)这两个组件会在后续进行详解,同时MapperMethod必须提供Mapper的接口路径,待执行的方法,配置Configuration作为入参。通过获取SqlCommand中的执行类型,MapperMethod才知道该Mapper接口将要执行什么样的操作。构造方法如下所示:

    1 public MapperMethod(Class<?> mapperInterface, Method method, Configuration config) {
    2 //初始化SqlCommand
    3 this.command = new SqlCommand(config, mapperInterface, method);
    4 //初始化方法签名
    5 this.method = new MethodSignature(config, mapperInterface, method);
    6 }

     

    Mybatis中定义了5种操作:SELECT,INSERT,UPDATE,DELETE,FLUSH。其中SELECT中包含了5中查询,结果类型处理器,返回NULL值,多条记录查询,单记录查询,游标查询,和map查询。相关操作和方法如下表对应。从表中可以看到,Sql执行方式本质上时通过sqlSession调用的,在SELECT操作中,虽然调用了MapperMethod中的方法,但本质上仍是通过Sqlsession下的select(),selectList(),selectCursor(),selectMap()等方法实现的。

    操作 方法名
    INSERT sqlSession.insert()
    UPDATE sqlSession.update()
    DELETE sqlSession.delete()
    FLUSH sqlSession.flushStatements()
    SELECT executeWithResultHandler(sqlSession, args)
    executeForMany(sqlSession, args)
    method.returnsMap()
    executeForCursor()
    sqlSession.selectOne(command.getName(), param)

     

    复制代码
     1 public Object execute(SqlSession sqlSession, Object[] args) {
     2 Object result;
     3 //判断命令类型
     4 switch (command.getType()) {
     5 case INSERT: {
     6 //将参数转为Sql参数
     7 Object param = method.convertArgsToSqlCommandParam(args);
     8 //获取插入结果
     9 result = rowCountResult(sqlSession.insert(command.getName(), param));
    10 break;
    11 }
    12 case UPDATE: {
    13 Object param = method.convertArgsToSqlCommandParam(args);
    14 result = rowCountResult(sqlSession.update(command.getName(), param));
    15 break;
    16 }
    17 case DELETE: {
    18 Object param = method.convertArgsToSqlCommandParam(args);
    19 result = rowCountResult(sqlSession.delete(command.getName(), param));
    20 break;
    21 }
    22 case SELECT:
    23 if (method.returnsVoid() && method.hasResultHandler()) {
    24 //带有结果处理器的
    25 executeWithResultHandler(sqlSession, args);
    26 result = null;
    27 } else if (method.returnsMany()) {
    28 //多条查询
    29 result = executeForMany(sqlSession, args);
    30 } else if (method.returnsMap()) {
    31 //返回map
    32 result = executeForMap(sqlSession, args);
    33 } else if (method.returnsCursor()) {
    34 //游标查询
    35 result = executeForCursor(sqlSession, args);
    36 } else {
    37 //单条查询
    38 Object param = method.convertArgsToSqlCommandParam(args);
    39 result = sqlSession.selectOne(command.getName(), param);
    40 }
    41 break;
    42 case FLUSH:
    43 //清除操作
    44 result = sqlSession.flushStatements();
    45 break;
    46 default:
    47 throw new BindingException("Unknown execution method for: " + command.getName());
    48 }
    49 if (result == null && method.getReturnType().isPrimitive() && !method.returnsVoid()) {
    50 throw new BindingException("Mapper method '" + command.getName() 
    51 + " attempted to return null from a method with a primitive return type (" + method.getReturnType() + ").");
    52 }
    53 return result;
    54 }
    复制代码

     


    参数解析器

         在上述代码中经常能看见这样一句代码 method.convertArgsToSqlCommandParam(args);,该方法主要的功能是获取@Param注解上的参数值,而实现方式便是通过参数解析器(ParamNameResolver),convertArgsToSqlCommandParam(args)方法实际上调用的是ParamNameResolver下的getNamedParams(args)方法。在分析该方法之前,先看看构造器都做了些什么操作。

    复制代码
     1 public ParamNameResolver(Configuration config, Method method) {
     2 final Class<?>[] paramTypes = method.getParameterTypes(); //获取所有参数类型
     3 final Annotation[][] paramAnnotations = method.getParameterAnnotations();//获取方法中的所有注解
     4 final SortedMap<Integer, String> map = new TreeMap<Integer, String>();
     5 //判断注解数组长度
     6 int paramCount = paramAnnotations.length;
     7 // get names from @Param annotations
     8 for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
     9 if (isSpecialParameter(paramTypes[paramIndex])) {
    10 // skip special parameters
    11 continue;
    12 }
    13 String name = null;
    14 for (Annotation annotation : paramAnnotations[paramIndex]) {
    15 //判断是否是Param标签的子类,也就是说@param中是否存在value值
    16 if (annotation instanceof Param) {
    17 hasParamAnnotation = true;
    18 //获取标签中的value值
    19 name = ((Param) annotation).value();
    20 break;
    21 }
    22 }
    23 if (name == null) {
    24 // @Param was not specified.
    25 //是否使用了真实值,也就是说没有设置value值
    26 if (config.isUseActualParamName()) {
    27 //获取方法中参数的名字
    28 name = getActualParamName(method, paramIndex);
    29 }
    30 if (name == null) {
    31 // use the parameter index as the name ("0", "1", ...)
    32 // gcode issue #71
    33 name = String.valueOf(map.size());
    34 }
    35 }
    36 map.put(paramIndex, name);
    37 }
    38 //设置所有参数名为不可修改的集合
    39 names = Collections.unmodifiableSortedMap(map);
    40 }
    复制代码

     


          构造器同样需要两个入参,配置类和方法名,ParamNameResolver下包含了两个属性字段GENERIC_NAME_PREFIX属性前缀和参数集合names,构造器会将Method中所有参数级别的注解全部解析出来方法有序参数集中,names中存储形式为<参数下标,参数名>,如果在注解上设置了参数名,则会直接获取注解的value值,如果没有使用@Param注解,则使用真实的参数名,注意,真实参数名其实是arg0,arg1....的形式展现的,在判断真实参数名时,Mybatis会检查JDK版本是否包含java.lang.reflect.Parameter类,不存在该类的化会抛出ClassNotFoundException异常。 完成初始化后,就可以调用getNamedParams(args)方法了,如下代码所示,该方法使用了类中的names属性,从有序集合中取出所有的<参数索引,参数名>键值对>,随后填充到另外一个集合中,以<参数名,参数下标索引,>形式呈现,同时会保留一份<param+下标索引,参数下标>的键值对

    复制代码
     1 public Object getNamedParams(Object[] args) {
     2 //判断参数个数
     3 final int paramCount = names.size();
     4 if (args == null || paramCount == 0) {
     5 return null;
     6 }
     7 //返回首个参数 
     8 else if (!hasParamAnnotation && paramCount == 1) {
     9 return args[names.firstKey()];
    10 } else {
    11 final Map<String, Object> param = new ParamMap<Object>();
    12 int i = 0;
    13 for (Map.Entry<Integer, String> entry : names.entrySet()) {
    14 //设置参数的值和键名
    15 param.put(entry.getValue(), args[entry.getKey()]);
    16 // add generic param names (param1, param2, ...)
    17 //增加参数名
    18 final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
    19 // ensure not to overwrite parameter named with @Param
    20 if (!names.containsValue(genericParamName)) {
    21 param.put(genericParamName, args[entry.getKey()]);
    22 }
    23 i++;
    24 }
    25 return param;
    26 }
    27 }
    复制代码

     

    需掌握的知识点

    • Method类的使用,常用的方法
    方法名 作用
    getName() 获取方法名
    isVarArgs() 如果该方法声明为采用可变数量的参数,则返回true; 否则返回false
    getModifiers() 获取权限修饰符
    getReturnType() 获取返回类型
    getExceptionTypes() 获取所有抛出的异常类型
    getGenericReturnType ()返回Type类型
    getParameterTypes() 获取所有参数的类型
    getParameterCount() 获取所有参数的个数
    getAnnotations() 获取方法级别的注解

     

    • 集合的使用

    Collections.unmodifiableSortedMap(map);可修饰为一个不可变的集合

    如何实现自定义注解,并对注解进行解析

    见实现自定义注解,并对注解进行解析 文章

    标签: mybatis, 源码
    <div id="blog_post_info">
    
    0
    0
    <div class="clear"></div>
    <div id="post_next_prev">
    
    <a href="https://www.cnblogs.com/zhengzuozhanglina/p/10927591.html" class="p_n_p_prefix">« </a> 上一篇:    <a href="https://www.cnblogs.com/zhengzuozhanglina/p/10927591.html" title="发布于 2019-05-26 21:08">windows下安装RabbitMQ</a>
    <br>
    <a href="https://www.cnblogs.com/zhengzuozhanglina/p/11219844.html" class="p_n_p_prefix">» </a> 下一篇:    <a href="https://www.cnblogs.com/zhengzuozhanglina/p/11219844.html" title="发布于 2019-07-20 23:52">反射类 Method类的使用</a>
    
    posted @ 2019-07-19 11:24  爱吃猫的鱼z 阅读( 66) 评论( 0) 编辑 收藏
    </div><!--end: topics 文章、评论容器-->
    
        <div id="google_ads_iframe_/1090369/C2_0__container__" style="border: 0pt none;"><iframe id="google_ads_iframe_/1090369/C2_0" title="3rd party ad content" name="google_ads_iframe_/1090369/C2_0" width="468" height="60" scrolling="no" marginwidth="0" marginheight="0" frameborder="0" srcdoc="" style="border: 0px; vertical-align: bottom;" data-google-container-id="2" data-load-complete="true"></iframe></div></div>
    </div>
    <div id="under_post_kb">
    
    <div id="sideBar">
    	<div id="sideBarMain">
    
    <div id="profile_block">
        昵称:
        <a href="https://home.cnblogs.com/u/zhengzuozhanglina/">
            爱吃猫的鱼z
        </a>
        <br>
        园龄:
        <a href="https://home.cnblogs.com/u/zhengzuozhanglina/" title="入园时间:2016-10-17">
            3年
        </a>
        <br>
        粉丝:
        <a href="https://home.cnblogs.com/u/zhengzuozhanglina/followers/">
            2
        </a>
        <br>
        关注:
        <a href="https://home.cnblogs.com/u/zhengzuozhanglina/followees/">
            2
        </a>
        <div id="p_b_follow">
    

    +加关注

    		<div id="blog-calendar" style="">
    
    < 2019年11月 >
    2728293031 1 2
    3 4 5 6 7 8 9
    10 11 12 13 14 15 16
    17 18 19 20 21 22 23
    24 25 26 27 28 29 30
    1 2 3 4 5 6 7
    		<div id="leftcontentcontainer">
    			<div id="blog-sidecolumn">
    

    我的随笔

  • 我的评论

  • 我的参与

  • 最新评论

  • 我的标签

      随笔分类

              </h3>
      
      
              <ul>
      
                          <li>
      
      C语言(2)
                          </li>
                          <li>
      
      Erlang
                          </li>
                          <li>
      
      java(2)
                          </li>
                          <li>
      
      Jedis(5)
                          </li>
                          <li>
      
      linux(2)
                          </li>
                          <li>
      
      mybatis源码分析(12)
                          </li>
                          <li>
      
      mysql
                          </li>
                          <li>
      
      Oracle
                          </li>
                          <li>
      
      RabbitMQ
                          </li>
                          <li>
      
      redis
                          </li>
                          <li>
      
      spring
                          </li>
                          <li>
      
      spring-boot
                          </li>
                          <li>
      
      springmvc
                          </li>
                          <li>
      
      SQL
                          </li>
                          <li>
      
      Struts2(1)
                          </li>
                          <li>
      
      UML
                          </li>
                          <li>
      
      vm虚拟机(1)
                          </li>
                          <li>
      
      XML
                          </li>
                          <li>
      
      大数据(2)
                          </li>
                          <li>
      
      多线程(1)
                          </li>
                          <li>
      
      设计模式
                          </li>
                          <li>
      
      数据结构与算法
                          </li>
      
              </ul>
      
      
          </div>
          <div id="sidebar_postarchive" class="catListPostArchive sidebar-block">
              <h3 class="catListTitle">
      

      随笔档案

              </h3>
      
      
              <ul>
      
                          <li>
      
      2019年9月(1)
                          </li>
                          <li>
      
      2019年8月(13)
                          </li>
                          <li>
      
      2019年7月(9)
                          </li>
                          <li>
      
      2019年5月(1)
                          </li>
                          <li>
      
      2018年10月(2)
                          </li>
                          <li>
      
      2018年9月(1)
                          </li>
                          <li>
      
      2016年12月(1)
                          </li>
                          <li>
      
      2016年11月(1)
                          </li>
                          <li>
      
      2016年10月(3)
                          </li>
      
              </ul>
      
      
          </div>
          <div id="sidebar_articlecategory" class="catListArticleCategory sidebar-block">
              <h3 class="catListTitle">
      

      文章分类

              </h3>
      
      
              <ul>
      
                          <li>
      
      C语言(3)
                          </li>
                          <li>
      
      Eclipse(1)
                          </li>
                          <li>
      
      Hodoop
                          </li>
                          <li>
      
      ibatis(2)
                          </li>
                          <li>
      
      Java.Thread
                          </li>
                          <li>
      
      JavaScript
                          </li>
                          <li>
      
      MyBatis
                          </li>
                          <li>
      
      Oracle
                          </li>
                          <li>
      
      Spring
                          </li>
                          <li>
      
      spring-boot
                          </li>
                          <li>
      
      SpringMVC
                          </li>
                          <li>
      
      SQL
                          </li>
                          <li>
      
      算法和数据结构
                          </li>
      
              </ul>
      
      
          </div>
      
      	</div><!--end: sideBarMain -->
      </div><!--end: sideBar 侧边栏容器 -->
      <div class="clear"></div>
      </div><!--end: main -->
      <div class="clear"></div>
      <div id="footer">
      	<!--done-->
      

      Copyright © 2019 爱吃猫的鱼z

      Powered by .NET Core 3.0.0 on Linux

      </div><!--end: footer -->
      

      canmengqian

      </div><!--end: blogTitle 博客的标题和副标题 -->
      <div id="navigator">
      
      新随笔
    • 联系
    • 订阅
    • 管理
    • 	<div class="blogStats">
      		
      		<span id="stats_post_count">随笔 - 
      

      32 
      文章 -

      评论 -
      2

      	</div><!--end: blogStats -->
      </div><!--end: navigator 博客导航栏 -->
      

      Mybatis源码分析:MapperMethod中内部静态类SqlCommand的作用

              </h1>
              <div class="clear"></div>
              <div class="postBody">
      

      MapperMethod中内部静态类SqlCommand的作用

       

         在MapperMethod初始化中,会首先初始化两个内部静态类,SqlCommand就是其中之一,SqlCommand的作用主要体现在MapperMethod类的execute()方法里,SqlCommand为其提供了查询类型和方法id两个信息,从而使用Sqlseesion执行不同的方法,那么SqlCommand是如何获取到查询类型和方法id的呢?其中又做了哪些操作呢?

        首先看看构造器中的代码执行顺序。构造器需要传入配置类,Mapper接口和Method类.

      1. 从Method类中获取方法名和该方法所在的类路径
      2. 根据mapper接口信息和配置类获取到XML对应的MapperStatement对象
      3. 判断映射语句对象是否为NULL,如果不为NULL,则从中获取到语句的id和待执行的Sql类型,如果为NULL,再次判断待执行方法上是否标注有Flush的注解,如果不满足条件,则会抛出BindingException异常。

        流程图如下所示:

           

      值得注意的是,在获取映射语句对象是通过调用resolveMappedStatement()方法实现的。见如下的讲述。

      复制代码
       1  public SqlCommand(Configuration configuration, Class<?> mapperInterface, Method method) {
       2      //获取方法名
       3         final String methodName = method.getName();
       4      //获取方法所在的类
       5         final Class<?> declaringClass = method.getDeclaringClass();
       6     //获取映射语句信息
       7       MappedStatement ms = resolveMappedStatement(mapperInterface, methodName, declaringClass,
       8           configuration);
       9       //如果映射语句对象是NULL,那么查看该方法上是否标注了FLUSH标签,如果存在,则设置查询类型为FLUSH,否则抛出BindingException异常表示接口找不到定义的方法。
      10       if (ms == null) {
      11         if (method.getAnnotation(Flush.class) != null) {
      12           name = null;
      13           type = SqlCommandType.FLUSH;
      14         } else {
      15           throw new BindingException("Invalid bound statement (not found): "
      16               + mapperInterface.getName() + "." + methodName);
      17         }
      18       }
      19       //如果映射语句对象不为空,则设置指定的查询类型,如果为UNKNOWN 类型,则直接抛出BindingException异常
      20       else {
      21         name = ms.getId();
      22         type = ms.getSqlCommandType();
      23         if (type == SqlCommandType.UNKNOWN) {
      24           throw new BindingException("Unknown execution method for: " + name);
      25         }
      26       }
      27     }
      复制代码

      resolveMappedStatement()方法

         该方法主要是获取映射语句对象,在xml配置中,像select,update,delete,insert 都需要提供id号和sql语句以及入参和出参信息,而MappedStatement就是xml到java对象的映射,一个<select//>标签就会对应一个MappedStatement对象,这个目前了解即可,在后续会专门进行介绍。现简要说明下代码的执行流程。

      1. 获取待执行语句的id号,id号形式为类路径.方法名

      2. 在配置类中是否能找到该语句的id号,如果能找到,则直接从配置类中返回MappedStatement实例,否则从父类接口中继续查找,如果找不到就返回NULL

      3. 如果入参接口路径就是方法所在的类路径,那么直接返回NULL

       

      复制代码
       1 private MappedStatement resolveMappedStatement(Class<?> mapperInterface, String methodName,
       2         Class<?> declaringClass, Configuration configuration) {
       3         //获取语句id 形式为接口名.方法名
       4       String statementId = mapperInterface.getName() + "." + methodName;
       5       //判断配置中是否存在该方法id
       6       if (configuration.hasStatement(statementId)) {
       7           //返回映射语句
       8         return configuration.getMappedStatement(statementId);
       9       }
      10       //如果接口信息就是所在类的话,直接返回NULL
      11       else if (mapperInterface.equals(declaringClass)) {
      12         return null;
      13       }
      14       //获取该类下的所有接口信息
      15       for (Class<?> superInterface : mapperInterface.getInterfaces()) {
      16           //从父接口中查找对应的方法ID,下列语句使用递归的方式进行查找
      17         if (declaringClass.isAssignableFrom(superInterface)) {
      18           MappedStatement ms = resolveMappedStatement(superInterface, methodName,
      19               declaringClass, configuration);
      20           if (ms != null) {
      21             return ms;
      22           }
      23         }
      24       }
      25       return null;
      26     }
      27   }
      复制代码

       

      <div id="blog_post_info">
      
      0
      0
      <div class="clear"></div>
      <div id="post_next_prev">
      
      <a href="https://www.cnblogs.com/zhengzuozhanglina/p/11219844.html" class="p_n_p_prefix">« </a> 上一篇:    <a href="https://www.cnblogs.com/zhengzuozhanglina/p/11219844.html" title="发布于 2019-07-20 23:52">反射类 Method类的使用</a>
      <br>
      <a href="https://www.cnblogs.com/zhengzuozhanglina/p/11223568.html" class="p_n_p_prefix">» </a> 下一篇:    <a href="https://www.cnblogs.com/zhengzuozhanglina/p/11223568.html" title="发布于 2019-07-22 00:47">Mybatis源码分析:MapperMethod中内部静态类MethodSignature的作用</a>
      
      posted @ 2019-07-21 15:14  爱吃猫的鱼z 阅读( 100) 评论( 0) 编辑 收藏
      </div><!--end: topics 文章、评论容器-->
      
      <div id="sideBar">
      	<div id="sideBarMain">
      
      <div id="profile_block">
          昵称:
          <a href="https://home.cnblogs.com/u/zhengzuozhanglina/">
              爱吃猫的鱼z
          </a>
          <br>
          园龄:
          <a href="https://home.cnblogs.com/u/zhengzuozhanglina/" title="入园时间:2016-10-17">
              3年
          </a>
          <br>
          粉丝:
          <a href="https://home.cnblogs.com/u/zhengzuozhanglina/followers/">
              2
          </a>
          <br>
          关注:
          <a href="https://home.cnblogs.com/u/zhengzuozhanglina/followees/">
              2
          </a>
          <div id="p_b_follow">
      

      +加关注

      		<div id="blog-calendar" style="">
      
      < 2019年11月 >
      2728293031 1 2
      3 4 5 6 7 8 9
      10 11 12 13 14 15 16
      17 18 19 20 21 22 23
      24 25 26 27 28 29 30
      1 2 3 4 5 6 7
      		<div id="leftcontentcontainer">
      			<div id="blog-sidecolumn">
      

      我的随笔

    • 我的评论

    • 我的参与

    • 最新评论

    • 我的标签

        随笔分类

                </h3>
        
        
                <ul>
        
                            <li>
        
        C语言(2)
                            </li>
                            <li>
        
        Erlang
                            </li>
                            <li>
        
        java(2)
                            </li>
                            <li>
        
        Jedis(5)
                            </li>
                            <li>
        
        linux(2)
                            </li>
                            <li>
        
        mybatis源码分析(12)
                            </li>
                            <li>
        
        mysql
                            </li>
                            <li>
        
        Oracle
                            </li>
                            <li>
        
        RabbitMQ
                            </li>
                            <li>
        
        redis
                            </li>
                            <li>
        
        spring
                            </li>
                            <li>
        
        spring-boot
                            </li>
                            <li>
        
        springmvc
                            </li>
                            <li>
        
        SQL
                            </li>
                            <li>
        
        Struts2(1)
                            </li>
                            <li>
        
        UML
                            </li>
                            <li>
        
        vm虚拟机(1)
                            </li>
                            <li>
        
        XML
                            </li>
                            <li>
        
        大数据(2)
                            </li>
                            <li>
        
        多线程(1)
                            </li>
                            <li>
        
        设计模式
                            </li>
                            <li>
        
        数据结构与算法
                            </li>
        
                </ul>
        
        
            </div>
            <div id="sidebar_postarchive" class="catListPostArchive sidebar-block">
                <h3 class="catListTitle">
        

        随笔档案

                </h3>
        
        
                <ul>
        
                            <li>
        
        2019年9月(1)
                            </li>
                            <li>
        
        2019年8月(13)
                            </li>
                            <li>
        
        2019年7月(9)
                            </li>
                            <li>
        
        2019年5月(1)
                            </li>
                            <li>
        
        2018年10月(2)
                            </li>
                            <li>
        
        2018年9月(1)
                            </li>
                            <li>
        
        2016年12月(1)
                            </li>
                            <li>
        
        2016年11月(1)
                            </li>
                            <li>
        
        2016年10月(3)
                            </li>
        
                </ul>
        
        
            </div>
            <div id="sidebar_articlecategory" class="catListArticleCategory sidebar-block">
                <h3 class="catListTitle">
        

        文章分类

                </h3>
        
        
                <ul>
        
                            <li>
        
        C语言(3)
                            </li>
                            <li>
        
        Eclipse(1)
                            </li>
                            <li>
        
        Hodoop
                            </li>
                            <li>
        
        ibatis(2)
                            </li>
                            <li>
        
        Java.Thread
                            </li>
                            <li>
        
        JavaScript
                            </li>
                            <li>
        
        MyBatis
                            </li>
                            <li>
        
        Oracle
                            </li>
                            <li>
        
        Spring
                            </li>
                            <li>
        
        spring-boot
                            </li>
                            <li>
        
        SpringMVC
                            </li>
                            <li>
        
        SQL
                            </li>
                            <li>
        
        算法和数据结构
                            </li>
        
                </ul>
        
        
            </div>
        
        	</div><!--end: sideBarMain -->
        </div><!--end: sideBar 侧边栏容器 -->
        <div class="clear"></div>
        </div><!--end: main -->
        <div class="clear"></div>
        <div id="footer">
        	<!--done-->
        

        Copyright © 2019 爱吃猫的鱼z

        Powered by .NET Core 3.0.0 on Linux

        </div><!--end: footer -->
        
      评论 1
      成就一亿技术人!
      拼手气红包6.0元
      还能输入1000个字符
       
      红包 添加红包
      表情包 插入表情
       条评论被折叠 查看
      添加红包

      请填写红包祝福语或标题

      红包个数最小为10个

      红包金额最低5元

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

      抵扣说明:

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

      余额充值