Java 進階 & 實現(xiàn)讀取Excel中的Http請求及檢查點袱巨、關(guān)聯(lián)API (一)

一阁谆、項目場景

1、excel中存儲各個http請求的 類型愉老、url,參數(shù)场绿,頭部信息,檢查點嫉入,關(guān)聯(lián)參數(shù)


image.png

2焰盗、從excel中讀取并發(fā)送各類http請求,并校驗檢查點
3咒林、將各請求中關(guān)聯(lián)的參數(shù)熬拒,引入對應(yīng)需要關(guān)聯(lián)的請求鏈接中

二、實現(xiàn)以上需求垫竞,需要使用到框架
   <!--用于讀取excel-->
        <dependency>
            <groupId>com.github.crab2died</groupId>
            <artifactId>Excel4J</artifactId>
            <version>2.1.2</version>
        </dependency>
        <!--用于讀取文件-->
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.6</version>
        </dependency>
        <!--用于httpclient發(fā)送get,post請求-->
        <dependency>
            <groupId>org.apache.httpcomponents</groupId>
            <artifactId>httpclient</artifactId>
            <version>4.5.4</version>
        </dependency>
        <!--用于表達式判斷-->
        <dependency>
            <groupId>com.googlecode.aviator</groupId>
            <artifactId>aviator</artifactId>
            <version>3.2.0</version>
        </dependency>
        <!--用于獲取json數(shù)據(jù)-->
        <dependency>
            <groupId>com.jayway.jsonpath</groupId>
            <artifactId>json-path</artifactId>
            <version>2.4.0</version>
        </dependency>
三澎粟、需要用到的Utils工具類

1、將字符串轉(zhuǎn)化成Map存儲欢瞪。
如:excel中的頭部信息活烙、檢查點有多個時,需要拆分

import java.util.HashMap;
import java.util.Map;
public class MapUtil {
    public static Map StringToMap(String str, String regx){

        Map<String,Object> map = null;
        if(str!=null) {
            String[] strs = str.split(regx);
            map = new HashMap<String,Object>();
            for (String s : strs) {
                String[] ss = s.split("=");
                map.put(ss[0], ss[1]);
            }
        }
        return  map;
    }
    public static Map StringToMap(String str){
        return StringToMap(str,";");
    }
}

2遣鼓、將參數(shù)存儲到Map中啸盏。
如:excel中的關(guān)聯(lián)參數(shù),若有多個時需要拆分譬正,且需要得到具體關(guān)聯(lián)的值宫补。

import com.jayway.jsonpath.JsonPath;
import java.util.HashMap;
import java.util.Map;

public class SaveParamsUtil {
    private  static  Map<String,Object> paramsMap = new HashMap<String,Object>();
    public static void saveParamsToMap(String json,String params){
        Map<String,Object> map = MapUtil.StringToMap(params);
        if(map!=null) {
            for (Map.Entry<String, Object> entry : map.entrySet()) {
                String key = entry.getKey();
                String val = entry.getValue().toString();
//這里需要用到JsonPath表達式獲取具體json中的值
                Object obj = JsonPath.read(json, val);
                paramsMap.put(key, obj.toString());
            }
        }
    }

    public static Object get(String key){
        return  paramsMap.get(key);
    }
}

3、正則表達式匹配工具類
如:excel中第三個請求url中用到關(guān)聯(lián)的參數(shù)

import com.sc.bean.TestCase;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class PatternUtil {
    private static Pattern patternRule = Pattern.compile("\\$\\{(.*?)\\}");//${id}
    public static void matcherParams(TestCase testCase){
        Matcher matcher = patternRule.matcher(testCase.getUrl());
        while (matcher.find()){
            System.out.println(matcher.group());
            String key = matcher.group(1);
            String value = SaveParamsUtil.get(key).toString();
            String url = testCase.getUrl().replace(matcher.group(),value);
            testCase.setUrl(url);
            System.out.println("newurl--->"+testCase.getUrl());
        }
    }
}

4曾我、實際json返回的結(jié)果與excel中檢查點是否一致工具類
如:excel中 $.code的實際值與期望值 1的比較

import com.googlecode.aviator.AviatorEvaluator;
import com.jayway.jsonpath.JsonPath;
import java.util.HashMap;
import java.util.Map;

public class CheckPointUtil {
    public static Boolean checkPoint(String json,String checkParam){
        if(checkParam!=null && !"".equals(checkParam) && !"null".equals(checkParam)){
            Map<String,Object> map = new HashMap<String,Object>();
            String[] cParams =  checkParam.split(";");
            for(String params:cParams){
                String[] pars = params.split("=|>|<|>=|<=|==");
                checkParam = checkParam.replace(pars[0],"data");
                Object obj = JsonPath.read(json,pars[0]);
                if(obj instanceof  String){
                    checkParam =  checkParam.replace(pars[1],StrToAviatorString(pars[1]));
                    checkParam = checkParam.replace("=","==");
                }
                map.put("data",obj);
                Boolean bool = (Boolean) AviatorEvaluator.execute(checkParam,map);
                return  bool;
            }
        }
        return true;
    }
    public static String StrToAviatorString(String str){
        return  "'"+str+"'";
    }
}

5、HttpClient發(fā)送get健民、post請求工具類

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.commons.io.FileUtils;
import org.apache.http.HttpEntity;
import org.apache.http.HttpHost;
import org.apache.http.NameValuePair;
import org.apache.http.client.ClientProtocolException;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.entity.UrlEncodedFormEntity;
import org.apache.http.client.methods.CloseableHttpResponse;
import org.apache.http.client.methods.HttpGet;
import org.apache.http.client.methods.HttpPost;
import org.apache.http.entity.StringEntity;
import org.apache.http.entity.mime.MultipartEntityBuilder;
import org.apache.http.impl.client.CloseableHttpClient;
import org.apache.http.impl.client.HttpClients;
import org.apache.http.impl.conn.PoolingHttpClientConnectionManager;
import org.apache.http.message.BasicNameValuePair;
import org.apache.http.protocol.HTTP;
import org.apache.http.util.EntityUtils;

public class HttpUtils {

    private static CloseableHttpClient httpclient;

    static {
        PoolingHttpClientConnectionManager manager = new PoolingHttpClientConnectionManager();
        manager.setMaxTotal(200); //連接池最大并發(fā)連接數(shù)
        manager.setDefaultMaxPerRoute(200);//單路由最大并發(fā)數(shù),路由是對maxTotal的細(xì)分
        httpclient = HttpClients.custom().setConnectionManager(manager).build();
    }

    /* ConnectionRequestTimeout httpclient使用連接池來管理連接抒巢,這個時間就是從連接池獲取連接的超時時間,可以想象下數(shù)據(jù)庫連接池
       ConnectTimeout 建立連接最大時間
       SocketTimeout 數(shù)據(jù)傳輸過程中數(shù)據(jù)包之間間隔的最大時間
       HttpHost 代理
     */
    private static RequestConfig config = RequestConfig.copy(RequestConfig.DEFAULT)
             .setSocketTimeout(10000)
             .setConnectTimeout(5000)
             .setConnectionRequestTimeout(100).build();
            //.setProxy(new HttpHost("127.0.0.1", 8888, "http")).build();

    public static String doGet(String url) throws HttpClientException {
        return doGet(url, null);
    }

    public static String doGet(String url, Map<String, Object> header) throws HttpClientException {
        String ret = "";
        HttpGet get = new HttpGet(url);
        get.setConfig(config);
        get.addHeader(HTTP.CONTENT_ENCODING, "UTF-8");
        CloseableHttpResponse closeableHttpResponse = null;
        try {
            if (header != null) {
                for (Map.Entry<String, Object> entry : header.entrySet()) {
                    get.setHeader(entry.getKey(), entry.getValue().toString());
                }
            }
            closeableHttpResponse = httpclient.execute(get);
            if (closeableHttpResponse.getStatusLine().getStatusCode() == 200) {
                ret = EntityUtils.toString(closeableHttpResponse.getEntity(), "UTF-8");
            } else {
                throw new HttpClientException(
                        "System level error, Code=[" + closeableHttpResponse.getStatusLine().getStatusCode() + "].");
            }
        } catch (ClientProtocolException e) {
            throw new HttpClientException("HttpClient error," + e.getMessage());
        } catch (IOException e) {
            throw new HttpClientException("IO error," + e.getMessage());
        } finally {
            if (closeableHttpResponse != null) {
                try {
                    closeableHttpResponse.close();
                } catch (IOException e) {
                }
            }
        }
        return ret;
    }

    public static String doPostString(String url, String params, String regx) throws HttpClientException {
        Map<String, Object> paramsMp =null;
        if(params!=null) {
            paramsMp = new HashMap<String, Object>();
            String[] strp = params.split(regx);
            for (int i = 0; i < strp.length; i++) {
                String singleparms = strp[i];
                String[] key_values = singleparms.split("=");
                paramsMp.put(key_values[0], key_values[1]);
            }
        }
        return doPost(url, paramsMp);
    }
    
    public static String doPost(String url, Map<String, Object> params) throws HttpClientException {
        return doPost(url, params, null);
    }

    public static String doPost(String url, Map<String, Object> params, Map<String, Object> header)
            throws HttpClientException {
        String ret = "";
        HttpPost post = new HttpPost(url);
        post.setConfig(config);
        post.addHeader(HTTP.CONTENT_ENCODING, "UTF-8");
        CloseableHttpResponse closeableHttpResponse = null;
        HttpEntity postEntity = null;
        try {
            if (params != null) {
                List<NameValuePair> list = new ArrayList<NameValuePair>();
                for (Map.Entry<String, Object> entry : params.entrySet()) {
                    list.add(new BasicNameValuePair(entry.getKey(), entry.getValue().toString()));
                }
                postEntity = new UrlEncodedFormEntity(list,"UTF-8");
                post.setEntity(postEntity);
            }

            if (header != null) {
                for (Map.Entry<String, Object> entry : header.entrySet()) {
                    post.setHeader(entry.getKey(), entry.getValue().toString());
                }
            }
            closeableHttpResponse = httpclient.execute(post);
            if (closeableHttpResponse.getStatusLine().getStatusCode() == 200) {
                ret = EntityUtils.toString(closeableHttpResponse.getEntity(), "UTF-8");
            } else {
                throw new HttpClientException(
                        "System level error, Code=[" + closeableHttpResponse.getStatusLine().getStatusCode() + "].");
            }
        } catch (ClientProtocolException e) {
            throw new HttpClientException("HttpClient error," + e.getMessage());
        } catch (IOException e) {
            throw new HttpClientException("IO error," + e.getMessage());
        } finally {
            if (postEntity != null) {
                try {
                    EntityUtils.consume(postEntity);
                } catch (IOException e) {
                }
            }
            if (closeableHttpResponse != null) {
                try {
                    closeableHttpResponse.close();
                } catch (IOException e) {
                }
            }
        }
        return ret;
    }

    public static String doPostJson(String url, String jsonParam) throws HttpClientException {
        return doPostJson(url, jsonParam, null);
    }

    public static String doPostJson(String url, String jsonParam, Map<String, Object> header)
            throws HttpClientException {
        String ret = "";
        HttpPost post = new HttpPost(url);
        post.setConfig(config);
        post.addHeader(HTTP.CONTENT_ENCODING, "UTF-8");
        CloseableHttpResponse closeableHttpResponse = null;
        StringEntity postEntity = null;
        try {
            if (jsonParam != null) {
                postEntity = new StringEntity(jsonParam, "utf-8"); 
                postEntity.setContentEncoding("UTF-8");
                postEntity.setContentType("application/json");
                post.setEntity(postEntity);
            }

            if (header != null) {
                for (Map.Entry<String, Object> entry : header.entrySet()) {
                    post.setHeader(entry.getKey(), entry.getValue().toString());
                }
            }
            closeableHttpResponse = httpclient.execute(post);
            if (closeableHttpResponse.getStatusLine().getStatusCode() == 200) {
                ret = EntityUtils.toString(closeableHttpResponse.getEntity(), "UTF-8");
            } else {
                throw new HttpClientException(
                        "System level error, Code=[" + closeableHttpResponse.getStatusLine().getStatusCode() + "].");
            }
        } catch (ClientProtocolException e) {
            throw new HttpClientException("HttpClient error," + e.getMessage());
        } catch (IOException e) {
            throw new HttpClientException("IO error," + e.getMessage());
        } finally {
            if (postEntity != null) {
                try {
                    EntityUtils.consume(postEntity);
                } catch (IOException e) {
                }
            }
            if (closeableHttpResponse != null) {
                try {
                    closeableHttpResponse.close();
                } catch (IOException e) {
                }
            }
        }
        return ret;
    }
}
定義自己的異常類:
public class HttpClientException extends Exception{
    private static final long serialVersionUID = 2206587260745140347L;
    public HttpClientException(String msg) {
        super(msg);
    }
}
四秉犹、excel中的各參數(shù)轉(zhuǎn)化成javabean
import com.github.crab2died.annotation.ExcelField;

public class TestCase {
    
    @ExcelField(title = "類型")
    private String type;
    
    @ExcelField(title = "地址")
    private String url;
    
    @ExcelField(title = "參數(shù)")
    private String params;
    
    @ExcelField(title = "頭部",order=2)
    private String header;
    
    @ExcelField(title = "測試結(jié)果", order=1)
    private String result;
    
    @ExcelField(title = "檢查點")
    private String checkP;
    
    @ExcelField(title = "關(guān)聯(lián)")
    private String correlation;


    public String getCheckP() {
        return checkP;
    }

    public void setCheckP(String checkP) {
        this.checkP = checkP;
    }

    public String getCorrelation() {
        return correlation;
    }

    public void setCorrelation(String correlation) {
        this.correlation = correlation;
    }

    public String getResult() {
        return result;
    }

    public void setResult(String result) {
        this.result = result;
    }

    public String getType() {
        return type;
    }

    public void setType(String type) {
        this.type = type;
    }

    public String getUrl() {
        return url;
    }

    public void setUrl(String url) {
        this.url = url;
    }

    public String getParams() {
        return params;
    }

    public void setParams(String params) {
        this.params = params;
    }

    public String getHeader() {
        return header;
    }

    public void setHeader(String header) {
        this.header = header;
    }

    @Override
    public String toString() {
        return "TestCase{" +
                "type='" + type + '\'' +
                ", url='" + url + '\'' +
                ", params='" + params + '\'' +
                ", header='" + header + '\'' +
                ", result='" + result + '\'' +
                ", checkP='" + checkP + '\'' +
                ", correlation='" + correlation + '\'' +
                '}';
    }
}
五蛉谜、測試類

1、通過github.Excel4J包中的ExcelUtils 讀取excel崇堵,返回List
遍歷List集合型诚,集合中的每一個對象都是 TestCase bean
2、遍歷List的過程中鸳劳,判斷讀取excel中type請求是get還是post
如果是get,就調(diào)用已封裝好的HttpUtils中的doGet方法狰贯,需要傳get請求需要的url,頭部信息
如果是post,就調(diào)用已封裝好的HttpUtils中的doPost方法,需要傳get請求需要的url,參數(shù)涵紊,頭部信息
因為在Excel中參數(shù)傍妒、頭部信息都是以字符串的形式存儲的,所以需要將參數(shù)摸柄,頭部信息字符串轉(zhuǎn)化成Map對象才行颤练。
所以,這里封裝了字符串轉(zhuǎn)Map的工具類MapUtil驱负,方法名為:StringToMap,這里需要傳一個分割符的參數(shù)嗦玖,為了方便后續(xù)使用,重載StringToMap方法跃脊,傳遞了默認(rèn)的分割符“;”宇挫。
3、將get/post的請求返回結(jié)果匾乓,存儲在某個變量中responseJson捞稿。
4、檢查responseJson返回的code的值拼缝,是否與excel中檢查點中code的值是否一致娱局。
excel中“.code=1”,先要得到excel中檢查點的值咧七,如果是多個衰齐,需要分割一下,結(jié)果為一個數(shù)組1继阻。 因為數(shù)組1中的每個元素耻涛,=號左側(cè)是Jsonpath表達式,=號右側(cè)是具體的值瘟檩,所以需要繼續(xù)分隔抹缕,結(jié)果為一個數(shù)組2 分隔完成后,數(shù)組2中墨辛,第一個元素即為jsonpath表達式卓研,這里需要將整個參數(shù)字符串中的第一個元素替化成 data 用JsonPath.read(responseJson,數(shù)組2的第一個元素),返回一個具體的值 判斷上述返回的值是否為String類型睹簇,如果是奏赘,則需要將數(shù)組2的第二個值轉(zhuǎn)化成字符串,且字符串相比較要用==太惠,所以需要將參數(shù)中=替換成== 將data作為key,JsonPath.read返回的值作為 value,放入Map中 使用AviatorEvaluator.exec(params,map)磨淌,返回結(jié)果為bool類型。 5凿渊、關(guān)聯(lián): 首先梁只,需要查看遍歷的List集合的結(jié)果中缚柳,是否存在需要接受關(guān)聯(lián)參數(shù)的請求。 這里要用到Pattern正規(guī)匹配{(.*?)}作為一個匹配的規(guī)則敛纲,這里同jmeter中正則表達式匹配
在excel的url中通過上述匹配規(guī)則循環(huán)查看是否有需要匹配的url
如果有匹配喂击,則返回一個group,這里group得到的是${id}
通過group(n)得到具體要關(guān)聯(lián)的參數(shù)為id
將excel中關(guān)聯(lián)的參數(shù)id的值已轉(zhuǎn)存到map中
這里直接可調(diào)用該map淤翔,key為group(n)翰绊,即可得到對應(yīng)的id的value
將url中的參數(shù)替換成該id對應(yīng)的value,得到一個新的url
重新為TestCase設(shè)置url,后續(xù)http發(fā)送請求時旁壮,即可得到一個完整url

import com.github.crab2died.ExcelUtils;
import com.sc.bean.TestCase;
import com.sc.http.HttpUtils;
import com.sc.util.CheckPointUtil;
import com.sc.util.MapUtil;
import com.sc.util.PatternUtil;
import com.sc.util.SaveParamsUtil;
import java.io.File;
import java.util.List;

public class ApiTest {
    public static void main(String[] args) throws Exception {
        String path = System.getProperty("user.dir")+ File.separator+"data"+File.separator+"apitest.xlsx";
//通過github.Excel4J包中的ExcelUtils 讀取excel监嗜,返回List
        List<TestCase> list = ExcelUtils.getInstance().readExcel2Objects(path,TestCase.class);
        if(list!=null){
            for(TestCase testCase : list){
                PatternUtil.matcherParams(testCase);
                String responseJson =null;
                if("get".equals(testCase.getType())){
                    responseJson = HttpUtils.doGet(testCase.getUrl(), MapUtil.StringToMap(testCase.getHeader()));
                }else if("post".equals(testCase.getType())){
                    responseJson = HttpUtils.doPost(testCase.getUrl(), MapUtil.StringToMap(testCase.getParams(),"&"),MapUtil.StringToMap(testCase.getHeader()));
                }
               Boolean checkFlag =  CheckPointUtil.checkPoint(responseJson,testCase.getCheckP());
                System.out.println("checkFlag="+checkFlag);
                if(checkFlag){
                    SaveParamsUtil.saveParamsToMap(responseJson,testCase.getCorrelation());
                }
                System.out.println(responseJson);
            }
        }

    }
}
六、輸出結(jié)果
checkFlag=true
{"msg":"登錄成功","uid":"E2BBEDC09FAA4406B8D85C96DF6281CF","code":"1"}
checkFlag=true
{"msg":"登錄成功","uid":"E2BBEDC09FAA4406B8D85C96DF6281CF","code":"1"}
${id}
newurl--->http://www.baidu.com?pid=E2BBEDC09FAA4406B8D85C96DF6281CF
checkFlag=true
<!DOCTYPE html>
<!--STATUS OK--><html> <head><meta http-equiv=content-type content=text/html;charset=utf-8><meta http-equiv=X-UA-Compatible content=IE=Edge><meta ......
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末抡谐,一起剝皮案震驚了整個濱河市裁奇,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌麦撵,老刑警劉巖刽肠,帶你破解...
    沈念sama閱讀 217,185評論 6 503
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異免胃,居然都是意外死亡音五,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,652評論 3 393
  • 文/潘曉璐 我一進店門羔沙,熙熙樓的掌柜王于貴愁眉苦臉地迎上來躺涝,“玉大人,你說我怎么就攤上這事扼雏〖崾龋” “怎么了?”我有些...
    開封第一講書人閱讀 163,524評論 0 353
  • 文/不壞的土叔 我叫張陵诗充,是天一觀的道長苍蔬。 經(jīng)常有香客問我,道長蝴蜓,這世上最難降的妖魔是什么银室? 我笑而不...
    開封第一講書人閱讀 58,339評論 1 293
  • 正文 為了忘掉前任,我火速辦了婚禮励翼,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘辜荠。我一直安慰自己汽抚,他們只是感情好,可當(dāng)我...
    茶點故事閱讀 67,387評論 6 391
  • 文/花漫 我一把揭開白布伯病。 她就那樣靜靜地躺著造烁,像睡著了一般否过。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上惭蟋,一...
    開封第一講書人閱讀 51,287評論 1 301
  • 那天苗桂,我揣著相機與錄音,去河邊找鬼告组。 笑死煤伟,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的木缝。 我是一名探鬼主播便锨,決...
    沈念sama閱讀 40,130評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼我碟!你這毒婦竟也來了放案?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,985評論 0 275
  • 序言:老撾萬榮一對情侶失蹤矫俺,失蹤者是張志新(化名)和其女友劉穎吱殉,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體厘托,經(jīng)...
    沈念sama閱讀 45,420評論 1 313
  • 正文 獨居荒郊野嶺守林人離奇死亡友雳,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 37,617評論 3 334
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了催烘。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片沥阱。...
    茶點故事閱讀 39,779評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖伊群,靈堂內(nèi)的尸體忽然破棺而出考杉,到底是詐尸還是另有隱情,我是刑警寧澤舰始,帶...
    沈念sama閱讀 35,477評論 5 345
  • 正文 年R本政府宣布崇棠,位于F島的核電站,受9級特大地震影響丸卷,放射性物質(zhì)發(fā)生泄漏枕稀。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 41,088評論 3 328
  • 文/蒙蒙 一谜嫉、第九天 我趴在偏房一處隱蔽的房頂上張望萎坷。 院中可真熱鬧,春花似錦沐兰、人聲如沸哆档。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,716評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽瓜浸。三九已至澳淑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間插佛,已是汗流浹背杠巡。 一陣腳步聲響...
    開封第一講書人閱讀 32,857評論 1 269
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留雇寇,地道東北人氢拥。 一個月前我還...
    沈念sama閱讀 47,876評論 2 370
  • 正文 我出身青樓,卻偏偏與公主長得像谢床,于是被迫代替她去往敵國和親兄一。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點故事閱讀 44,700評論 2 354

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

  • Spring Cloud為開發(fā)人員提供了快速構(gòu)建分布式系統(tǒng)中一些常見模式的工具(例如配置管理识腿,服務(wù)發(fā)現(xiàn)出革,斷路器,智...
    卡卡羅2017閱讀 134,654評論 18 139
  • //Clojure入門教程: Clojure – Functional Programming for the J...
    葡萄喃喃囈語閱讀 3,660評論 0 7
  • 從三月份找實習(xí)到現(xiàn)在渡讼,面了一些公司骂束,掛了不少,但最終還是拿到小米成箫、百度展箱、阿里、京東蹬昌、新浪混驰、CVTE、樂視家的研發(fā)崗...
    時芥藍閱讀 42,243評論 11 349
  • 翻譯約定 primary data: 主數(shù)據(jù)resource identifier object 資源標(biāo)識符對象r...
    sladeliu閱讀 2,388評論 0 2
  • 5月11日早6:00 自從進了傳媒皂贩,我就沒有醒過這么早栖榨,而且是大腦異常清醒。 ………… 我慢騰騰的從床上爬下來明刷,走...
    許安戈閱讀 571評論 2 6