【MyBatis】學(xué)習(xí)紀(jì)要四:結(jié)合源碼教你參數(shù)處理

引言

今天我們來(lái)說(shuō) MyBatis 接收參數(shù)這一塊斯棒。我打算這樣說(shuō)給你聽(tīng)荣暮,我們先看一下MyBatis源碼是如何處理參數(shù)的罩驻,然后我們通過(guò)例子來(lái)教你惠遏。

Parameters.png

實(shí)際上抽高,我們這一節(jié)講的就是:Mapper.xml 如何獲取 Dao 中的參數(shù)呢透绩?

另外帚豪,如果你還沒(méi)有開(kāi)始學(xué)習(xí)MyBatis狸臣,覺(jué)得MyBatis還不錯(cuò),可以看 【MyBatis】學(xué)習(xí)紀(jì)要一:SpringBoot集成MyBatis完成增刪查改 這篇教程统翩,起步厂汗。

源碼分析

  • 第一步:我們先找到源碼娶桦。

我先將處理參數(shù)的類(lèi)復(fù)制到下面,然后我們?cè)僖黄饋?lái)分析栗涂。

/**
 *    Copyright 2009-2017 the original author or authors.
 *
 *    Licensed under the Apache License, Version 2.0 (the "License");
 *    you may not use this file except in compliance with the License.
 *    You may obtain a copy of the License at
 *
 *       http://www.apache.org/licenses/LICENSE-2.0
 *
 *    Unless required by applicable law or agreed to in writing, software
 *    distributed under the License is distributed on an "AS IS" BASIS,
 *    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 *    See the License for the specific language governing permissions and
 *    limitations under the License.
 */

package org.apache.ibatis.reflection;

import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.util.Collections;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

import org.apache.ibatis.annotations.Param;
import org.apache.ibatis.binding.MapperMethod.ParamMap;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;

public class ParamNameResolver {

  private static final String GENERIC_NAME_PREFIX = "param";

  /**
   * <p>
   * The key is the index and the value is the name of the parameter.<br />
   * The name is obtained from {@link Param} if specified. When {@link Param} is not specified,
   * the parameter index is used. Note that this index could be different from the actual index
   * when the method has special parameters (i.e. {@link RowBounds} or {@link ResultHandler}).
   * </p>
   * <ul>
   * <li>aMethod(@Param("M") int a, @Param("N") int b) -&gt; {{0, "M"}, {1, "N"}}</li>
   * <li>aMethod(int a, int b) -&gt; {{0, "0"}, {1, "1"}}</li>
   * <li>aMethod(int a, RowBounds rb, int b) -&gt; {{0, "0"}, {2, "1"}}</li>
   * </ul>
   */
  private final SortedMap<Integer, String> names;

  private boolean hasParamAnnotation;

  public ParamNameResolver(Configuration config, Method method) {
    final Class<?>[] paramTypes = method.getParameterTypes();
    final Annotation[][] paramAnnotations = method.getParameterAnnotations();
    final SortedMap<Integer, String> map = new TreeMap<Integer, String>();
    int paramCount = paramAnnotations.length;
    // get names from @Param annotations
    for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
      if (isSpecialParameter(paramTypes[paramIndex])) {
        // skip special parameters
        continue;
      }
      String name = null;
      for (Annotation annotation : paramAnnotations[paramIndex]) {
        if (annotation instanceof Param) {
          hasParamAnnotation = true;
          name = ((Param) annotation).value();
          break;
        }
      }
      if (name == null) {
        // @Param was not specified.
        if (config.isUseActualParamName()) {
          name = getActualParamName(method, paramIndex);
        }
        if (name == null) {
          // use the parameter index as the name ("0", "1", ...)
          // gcode issue #71
          name = String.valueOf(map.size());
        }
      }
      map.put(paramIndex, name);
    }
    names = Collections.unmodifiableSortedMap(map);
  }

  private String getActualParamName(Method method, int paramIndex) {
    if (Jdk.parameterExists) {
      return ParamNameUtil.getParamNames(method).get(paramIndex);
    }
    return null;
  }

  private static boolean isSpecialParameter(Class<?> clazz) {
    return RowBounds.class.isAssignableFrom(clazz) || ResultHandler.class.isAssignableFrom(clazz);
  }

  /**
   * Returns parameter names referenced by SQL providers.
   */
  public String[] getNames() {
    return names.values().toArray(new String[0]);
  }

  /**
   * <p>
   * A single non-special parameter is returned without a name.<br />
   * Multiple parameters are named using the naming rule.<br />
   * In addition to the default names, this method also adds the generic names (param1, param2,
   * ...).
   * </p>
   */
  public Object getNamedParams(Object[] args) {
    final int paramCount = names.size();
    if (args == null || paramCount == 0) {
      return null;
    } else if (!hasParamAnnotation && paramCount == 1) {
      return args[names.firstKey()];
    } else {
      final Map<String, Object> param = new ParamMap<Object>();
      int i = 0;
      for (Map.Entry<Integer, String> entry : names.entrySet()) {
        param.put(entry.getValue(), args[entry.getKey()]);
        // add generic param names (param1, param2, ...)
        final String genericParamName = GENERIC_NAME_PREFIX + String.valueOf(i + 1);
        // ensure not to overwrite parameter named with @Param
        if (!names.containsValue(genericParamName)) {
          param.put(genericParamName, args[entry.getKey()]);
        }
        i++;
      }
      return param;
    }
  }
}

  • 第二步:我們先來(lái)看構(gòu)成方法忿墅。
public ParamNameResolver(Configuration config, Method method) {
    final Class<?>[] paramTypes = method.getParameterTypes();
    final Annotation[][] paramAnnotations = method.getParameterAnnotations();
    final SortedMap<Integer, String> map = new TreeMap<Integer, String>();
    int paramCount = paramAnnotations.length;
    // get names from @Param annotations
    for (int paramIndex = 0; paramIndex < paramCount; paramIndex++) {
      if (isSpecialParameter(paramTypes[paramIndex])) {
        // skip special parameters
        continue;
      }
      String name = null;
      for (Annotation annotation : paramAnnotations[paramIndex]) {
        if (annotation instanceof Param) {
          hasParamAnnotation = true;
          name = ((Param) annotation).value();
          break;
        }
      }
      if (name == null) {
        // @Param was not specified.
        if (config.isUseActualParamName()) {
          name = getActualParamName(method, paramIndex);
        }
        if (name == null) {
          // use the parameter index as the name ("0", "1", ...)
          // gcode issue #71
          name = String.valueOf(map.size());
        }
      }
      map.put(paramIndex, name);
    }
    names = Collections.unmodifiableSortedMap(map);
  }

這部分代碼還是挺好理解的疚脐,可以先大概看一篇棍弄,然后主要看注釋的幾處呼畸。

1役耕、獲取有 @Param 注解的值

2聪廉、每次解析一個(gè)參數(shù)板熊,并且保存到map中(key:索引察绷,value:name)
name:標(biāo)注了Param注解:注解的值
沒(méi)有標(biāo)注:
1.全局配置:useActualParamName(jdk1.8):name=參數(shù)名
2.name=map.size():相當(dāng)于當(dāng)前元素的索引

例如:
method(@Param("id") id, @Param("name") name, Integer age);
之后的Map干签,大概就這樣:{0=id, 1=name}

  • 第三步:我們具體分析
// args[1, "Tom", 20]

public Object getNamedParams(Object[] args) {
    int paramCount = this.names.size();
    
     // 參數(shù)為null,直接返回
    if (args != null && paramCount != 0) {

          // 如果只有一個(gè)元素拆撼,并且沒(méi)有 `@Param` 注解容劳,args[0]喘沿,單個(gè)參數(shù)直接返回
        if (!this.hasParamAnnotation && paramCount == 1) {
            return args[(Integer)this.names.firstKey()];
         
           // 多個(gè)參數(shù)或者有 `@Param` 標(biāo)注
        } else {
            Map<String, Object> param = new ParamMap();
            int i = 0;
               // 遍歷 names 集合{0=id, 1=name}
            for(Iterator var5 = this.names.entrySet().iterator(); var5.hasNext(); ++i) {
               
               // names集合的value作為key;
               // names集合的key又作為取值的參考args[0]:args[1, "Tom"]
               // eg:{id=args[0]:1, name=args[1]:Tome}
                Entry<Integer, String> entry = (Entry)var5.next();
                param.put(entry.getValue(), args[(Integer)entry.getKey()]);
               
                // 額外的將每一個(gè)參數(shù)也保存到map中竭贩,使用新的key:param1 ... paramN
                String genericParamName = "param" + String.valueOf(i + 1);
                if (!this.names.containsValue(genericParamName)) {
                    param.put(genericParamName, args[(Integer)entry.getKey()]);
                }
            }

            return param;
        }
    } else {
        return null;
    }
}

通過(guò)讀源碼中的注釋?zhuān)嘈拍愦蟾哦税裳劣 H绻欢矝](méi)關(guān)系,我們通過(guò)實(shí)例演示給你窄赋。

演示

這一步,我們先搭建起項(xiàng)目错敢,先看搭建之后的目錄結(jié)構(gòu)。如果你已經(jīng)搭建起來(lái)峰锁,可以調(diào)到下一步虹蒋。

module.png
structure.png

application.properties

spring.datasource.url=jdbc:mysql://localhost:3306/mybatis_demo
spring.datasource.username=root
spring.datasource.password=xfsy2018
spring.datasource.driver-class-name=com.mysql.jdbc.Driver

mybatis.type-aliases-package=com.fengwenyi.mybatis.demo1.domain
mybatis.mapper-locations=classpath:mapper/*.xml

User

package com.fengwenyi.mybatis.demo1.domain;

/**
 * @author Wenyi Feng
 */
public class User {

    private Integer id;
    private String name;
    private Integer age;
    // getter & setter
    // toString
}

UserDao

package com.fengwenyi.mybatis.demo1.dao;

import com.fengwenyi.mybatis.demo1.domain.User;
import org.apache.ibatis.annotations.Mapper;

/**
 * @author Wenyi Feng
 */
@Mapper
public interface UserDao {

    // 查詢(xún)
    User findById (Integer id);

}

UserMapper.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd" >
<mapper namespace="com.fengwenyi.mybatis.demo1.dao.UserDao" >
    <resultMap id="BaseResultMap" type="User" >
        <id column="id" property="id" jdbcType="INTEGER" />
        <result column="name" property="name" jdbcType="VARCHAR" />
        <result column="age" property="age" jdbcType="INTEGER" />
    </resultMap>

    <select id="findById" resultMap="BaseResultMap">
        SELECT
          id, name, age
        FROM
          user
        WHERE
          id = #{id}
    </select>

</mapper>

數(shù)據(jù)庫(kù)

DROP TABLE IF EXISTS `user`;
CREATE TABLE `user` (
  `id` bigint(20) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) DEFAULT NULL,
  `age` int(11) DEFAULT NULL,
  PRIMARY KEY (`id`)
) ENGINE=MyISAM AUTO_INCREMENT=8 DEFAULT CHARSET=utf8mb4;

UserService

package com.fengwenyi.mybatis.demo1.service;

import com.fengwenyi.mybatis.demo1.dao.UserDao;
import com.fengwenyi.mybatis.demo1.domain.User;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

/**
 * @author Wenyi Feng
 */
@Service
public class UserService {

    @Autowired
    private UserDao userDao;

    public User findById (Integer id) {
        if (id != null && id > 0) {
            return userDao.findById(id);
        }
        return null;
    }

}

看一下測(cè)試代碼

Demo1ApplicationTests

package com.fengwenyi.mybatis.demo1;

import com.fengwenyi.mybatis.demo1.domain.User;
import com.fengwenyi.mybatis.demo1.service.UserService;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class Demo1ApplicationTests {

    @Test
    public void contextLoads() {

    }

    @Autowired
    private UserService userService;

    @Test
    public void test1 () {
        User user = userService.findById(1);
        System.out.println(user.toString());
    }

}

運(yùn)行:

Demo1ApplicationTests > test1()

只有一個(gè)參數(shù)

上面例子就是一個(gè)參數(shù)標(biāo)準(zhǔn)實(shí)例哲银,我們抽樣一下荆责。

UserDao

    User findById (Integer id);

UserMapper.xml

    <select id="findById" resultMap="BaseResultMap">
        SELECT
          id, name, age
        FROM
          user
        WHERE
          id = #{id}
    </select>

一個(gè)參數(shù),不論你用什么都可以取到键耕,例如 id=#{ssss}玛迄。

多個(gè)參數(shù)

UserDao

    User findByNameAndAge (String name, Integer age);

這樣寫(xiě)怎么獲取參數(shù)呢?

你可能會(huì)這樣勒虾,我們寫(xiě)一個(gè)例子測(cè)試下

UserMapp.xml

    <select id="findByNameAndAge" resultMap="BaseResultMap">
        SELECT
          id, name, age
        FROM
          user
        WHERE
          name = #{name}
          AND
          age = #{age}
    </select>

運(yùn)行會(huì)報(bào)錯(cuò):

Caused by:
org.apache.ibatis.binding.BindingException:
Parameter 'name' not found.
Available parameters are [arg1, arg0, param1, param2]

這樣寫(xiě),不對(duì)愕宋,那我們?cè)撛趺磳?xiě)呢?

    <select id="findByNameAndAge" resultMap="BaseResultMap">
        SELECT
          id, name, age
        FROM
          user
        WHERE
          name = #{param1}
          AND
          age = #{param2}
    </select>

原因邻寿,開(kāi)始我們就說(shuō)了。

@Param

我們改善一下

UserDao

    User findByNameAndAge2 (@Param("name") String name, @Param("age") Integer age);

UserMapper.xml

    <select id="findByNameAndAge2" resultMap="BaseResultMap">
        SELECT
          id, name, age
        FROM
          user
        WHERE
          name = #{name}
          AND
          age = #{age}
    </select>

POJO

UserDao

    User findByPojo (User user);

UserMapper.xml

    <select id="findByPojo" resultMap="BaseResultMap">
        SELECT
          id, name, age
        FROM
          user
        WHERE
          name = #{name}
          AND
          age = #{age}
    </select>

Map

看源碼我們已經(jīng)知道,MyBatis會(huì)將我們傳遞的參數(shù)封裝成Map段磨,那我們直接傳Map會(huì)怎么樣呢薇溃?

UserDao

    User findByMap (Map<String, Object> map);

UserMapper.xml

    <select id="findByMap" resultMap="BaseResultMap">
        SELECT
          id, name, age
        FROM
          user
        WHERE
          name = #{name}
          AND
          age = #{age}
    </select>

List

下面這兩種是特殊的,但我還是說(shuō)給你聽(tīng)

    User findByList (List<Object> list);
    <select id="findByList" resultMap="BaseResultMap">
        SELECT
          id, name, age
        FROM
          user
        WHERE
          name = #{list[0]}
          AND
          age = #{list[1]}
    </select>

Array

數(shù)組怎么處理

    User findByArray (Object [] array);

    <select id="findByArray" resultMap="BaseResultMap">
        SELECT
          id, name, age
        FROM
          user
        WHERE
          name = #{array[0]}
          AND
          age = #{array[1]}
    </select>

$ AND #

取值我們都會(huì)用 #{}

在我們習(xí)慣用的 ${}特姐,你們覺(jué)得這個(gè)很特別嗎?有沒(méi)有想過(guò)為什么呢捷枯?

  • #

UserDao

    User testGetValue1 (String name);

UserMapper.xml

    <select id="testGetValue1" resultMap="BaseResultMap">
        SELECT
          id, name, age
        FROM
          user
        WHERE
          name = #{name}
    </select>

看下運(yùn)行日志:

==> Preparing: SELECT id, name, age FROM user WHERE name = ?
==> Parameters: AAA(String)
<== Columns: id, name, age
<== Row: 1, AAA, 20
<== Total: 1

我們繼續(xù)看

  • $

UserDao

    User testGetValue2 (@Param("name") String name);

UserMapper.xml

    <select id="testGetValue2" resultMap="BaseResultMap">
        SELECT
          id, name, age
        FROM
          user
        WHERE
          name = '${name}'
    </select>

看下sql日志:

==> Preparing: SELECT id, name, age FROM user WHERE name = 'AAA'
==> Parameters:
<== Columns: id, name, age
<== Row: 1, AAA, 20
<== Total: 1

對(duì)比看下攀痊,你也許會(huì)發(fā)現(xiàn)各自的用法苟径。

值得注意的是,這里只是 $ 的測(cè)試示例蹬碧,在實(shí)際中,不推薦這么寫(xiě)罗心。

$的用法

既然有 ${}渤闷,那應(yīng)該怎么用呢?我們?cè)倏匆粋€(gè)例子

UserDao

    User testGetValue3 (@Param("name") String name);

UserMapper.xml

    <select id="testGetValue3" resultMap="BaseResultMap">
        SELECT
          id, name, age
        FROM
          ${name}
        WHERE
          name = "AAA"
    </select>

看下sql日志:

==> Preparing: SELECT id, name, age FROM user WHERE name = "AAA"
==> Parameters:
<== Columns: id, name, age
<== Row: 1, AAA, 20
<== Total: 1

總結(jié)

到這里就算差不過(guò)結(jié)束了肩碟。總結(jié)一下吧:

參數(shù)多時(shí)髓抑,封裝成Map,為了不混亂密末,我們可以使用 @Param 來(lái)指定封裝時(shí)使用的key严里,使用 #{key} 就可以取出Map中的值

測(cè)試源碼

MyBatis系列測(cè)試源碼·參數(shù)

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末座柱,一起剝皮案震驚了整個(gè)濱河市戏锹,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖馋吗,帶你破解...
    沈念sama閱讀 217,277評(píng)論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場(chǎng)離奇詭異堰怨,居然都是意外死亡灿巧,警方通過(guò)查閱死者的電腦和手機(jī)饿肺,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,689評(píng)論 3 393
  • 文/潘曉璐 我一進(jìn)店門(mén)零院,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)撰茎,“玉大人,你說(shuō)我怎么就攤上這事」畋兀” “怎么了?”我有些...
    開(kāi)封第一講書(shū)人閱讀 163,624評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵,是天一觀(guān)的道長(zhǎng)忠聚。 經(jīng)常有香客問(wèn)我,道長(zhǎng)赂毯,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,356評(píng)論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮,結(jié)果婚禮上燕耿,老公的妹妹穿的比我還像新娘。我一直安慰自己堵第,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,402評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布凄诞。 她就那樣靜靜地躺著伪朽,像睡著了一般朴肺。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上鞍盗,一...
    開(kāi)封第一講書(shū)人閱讀 51,292評(píng)論 1 301
  • 那天庐舟,我揣著相機(jī)與錄音,去河邊找鬼。 笑死摊求,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播踪旷,決...
    沈念sama閱讀 40,135評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼气破,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼狗超!你這毒婦竟也來(lái)了?” 一聲冷哼從身側(cè)響起渗稍,我...
    開(kāi)封第一講書(shū)人閱讀 38,992評(píng)論 0 275
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤拱燃,失蹤者是張志新(化名)和其女友劉穎,沒(méi)想到半個(gè)月后哮缺,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體尝苇,經(jīng)...
    沈念sama閱讀 45,429評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡押袍,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,636評(píng)論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片铁蹈。...
    茶點(diǎn)故事閱讀 39,785評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖览徒,靈堂內(nèi)的尸體忽然破棺而出措嵌,到底是詐尸還是另有隱情枫慷,我是刑警寧澤涕俗,帶...
    沈念sama閱讀 35,492評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站,受9級(jí)特大地震影響讨永,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,092評(píng)論 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望冰更。 院中可真熱鬧囚痴,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,723評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)介返。三九已至刃宵,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背键闺。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 32,858評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工挎塌, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留待锈,地道東北人。 一個(gè)月前我還...
    沈念sama閱讀 47,891評(píng)論 2 370
  • 正文 我出身青樓套啤,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親涝影。 傳聞我的和親對(duì)象是個(gè)殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,713評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 1. Java基礎(chǔ)部分 基礎(chǔ)部分的順序:基本語(yǔ)法,類(lèi)相關(guān)的語(yǔ)法舆瘪,內(nèi)部類(lèi)的語(yǔ)法,繼承相關(guān)的語(yǔ)法,異常的語(yǔ)法只嚣,線(xiàn)程的語(yǔ)...
    子非魚(yú)_t_閱讀 31,625評(píng)論 18 399
  • MyBatis是一個(gè)可以自定義SQL障般、存儲(chǔ)過(guò)程和高級(jí)映射的持久層框架。MyBatis 摒除了大部分的JDBC代碼贯钩、...
    七寸知架構(gòu)閱讀 6,707評(píng)論 6 56
  • Spring Cloud為開(kāi)發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見(jiàn)模式的工具(例如配置管理勺三,服務(wù)發(fā)現(xiàn),斷路器商源,智...
    卡卡羅2017閱讀 134,654評(píng)論 18 139
  • 只要說(shuō)到跨域庄吼,就必須聊到JSONP,就必須講一下JSONP的實(shí)現(xiàn)原理渐行,以及在項(xiàng)目中哪個(gè)需求使用了JSONP衰倦,簡(jiǎn)單講...
    Mr夜空閱讀 647評(píng)論 0 2
  • 之前豌习,很努力的告訴自己,為了把寫(xiě)作這門(mén)手藝練好,不論別的吸占,首先要做日更一篇。 而這種雞湯式的激勵(lì)方式,于我大概堅(jiān)持...
    漿糊郎中閱讀 119評(píng)論 4 2