欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java如何從Redis中批量讀取數(shù)據(jù)

 更新時(shí)間:2025年05月30日 15:49:23   作者:風(fēng)碎峰  
這篇文章主要介紹了Java如何從Redis中批量讀取數(shù)據(jù)的情況,具有很好的參考價(jià)值,希望對(duì)大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

一.背景概述

本周接到一個(gè)新的需求:從用戶(hù)dau日志文件中讀取用戶(hù)uid,然后到Redis中獲取對(duì)應(yīng)的用戶(hù)數(shù)據(jù)。用戶(hù)的uid存儲(chǔ)于login_day_20220913.txt文件,共1億2千多萬(wàn)條數(shù)據(jù),數(shù)量達(dá)1.4G。

要求:盡量在2小時(shí)內(nèi)獲得結(jié)果,在數(shù)據(jù)處理過(guò)程中,Redis服務(wù)器QPS盡量低,不超過(guò)某個(gè)閾值,不然會(huì)觸發(fā)監(jiān)控報(bào)警。數(shù)據(jù)從Redis從庫(kù)讀取,只提供一個(gè)端口。

二.分析與實(shí)現(xiàn)

由于之前做過(guò)相同數(shù)據(jù)量的統(tǒng)計(jì)需求,所以從一開(kāi)始就確定單線程完成此次數(shù)據(jù)處理也是可以的。實(shí)際上,對(duì)多線程和并發(fā)的使用需要慎之又慎,特別是在業(yè)務(wù)繁忙的系統(tǒng)或環(huán)境下。

接觸Redis的朋友都知道,Redis是支持批量讀取的,其中常用的兩個(gè)方法:mget()和hmget()。

本次處理的數(shù)據(jù)不是哈希結(jié)構(gòu),所以確定使用mget()。

此時(shí),我自然而然地問(wèn)了同事一個(gè)問(wèn)題,那就是mget批量處理數(shù)據(jù)的最佳參數(shù)范圍是多少?因?yàn)閙get()接受一個(gè)字符串?dāng)?shù)組參數(shù),也就是說(shuō)字符串?dāng)?shù)組的長(zhǎng)度最佳為多少?

同事并沒(méi)有給我明確的答案,只是說(shuō)他們?nèi)粘C颗翁幚?0000條,建議我自己可以嘗試一下,于是我打算試試50000條數(shù)據(jù)。

主要代碼如下:

package com.sina.weibo;

import com.sina.weibo.util.FileUtils;
import com.sina.weibo.util.ListUtil;
import org.apache.commons.lang3.time.StopWatch;
import redis.clients.jedis.Jedis;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.concurrent.TimeUnit;

public class Application {
    /** dau數(shù)據(jù)讀取路徑 */
    private static String dauDataPath = "/data1/sinawap/var/logs/wapcommon/place/user_position/dau/login_day_20220913.txt";

    /** 結(jié)果輸出路徑 */
    private static String outputPath = "/data1/bingqing5/importcampusdata/output/campus_data.txt";

    /** 已處理過(guò)的uid數(shù)據(jù)存儲(chǔ)路徑 */
    private static String processedUidDataPath = "/data1/bingqing5/importcampusdata/process/processed_uid.txt";

    public static void main(String[] args) {
        StopWatch stopWatch = new StopWatch();
        // 開(kāi)始時(shí)間
        stopWatch.start();
        System.out.println("================程序開(kāi)始===============");
        transfer(dauDataPath, processedUidDataPath, outputPath);
        System.out.println("================程序結(jié)束===============");
        // 結(jié)束時(shí)間
        stopWatch.stop();
        // 統(tǒng)計(jì)執(zhí)行時(shí)間(秒)
        System.out.println("執(zhí)行時(shí)長(zhǎng):" + stopWatch.getTime(TimeUnit.SECONDS) + " 秒.");
    }

    private static void transfer(String dauDataPath, String processedUidDataPath, String outputPath) {
        List<String> dauDataList = FileUtils.readInfoFromFile(dauDataPath);
        List<List<String>> bucket = ListUtil.splitList(dauDataList, 50000);
        Jedis jedis = new Jedis("rdsxxxxx.xxxx.xxxx.xxxx.com.cn",50000);
        List<String> processedUidDataList = FileUtils.readInfoFromFile(processedUidDataPath);
        LinkedHashSet<String> linkedHashSet = ListUtil.getLinkedHashSet(processedUidDataList);
        for (List<String> list : bucket) {
            List<String> jsonStrList = jedis.mget(list.toArray(new String[list.size()]));
            for (int i = 0; i < list.size(); i++) {
                if (!linkedHashSet.contains(list.get(i))) {
                    String uid = list.get(i);
                    FileUtils.appendInfoToFile(processedUidDataPath, uid);
                    String jsonStr = jsonStrList.get(i);
                    if (jsonStr == null || jsonStr == "") continue;
                    String content = uid + "\t" + jsonStr;
                    FileUtils.appendInfoToFile(outputPath, content);
                }
            }
            System.out.println(list.size());
        }
    }
}

三.發(fā)現(xiàn)問(wèn)題與屢次改進(jìn)

3.1.QPS過(guò)高而且波動(dòng)很大

上述代碼上線后沒(méi)多久,就被同事找來(lái),說(shuō)QPS過(guò)高,開(kāi)始的時(shí)候瞬間達(dá)到近100k,之后穩(wěn)定在70k~100k之間。因?yàn)閾?dān)心影響其他業(yè)務(wù),于是把jar包暫停,著手優(yōu)化。

于是,我多次修改如下代碼:

List<List<String>> bucket = ListUtil.splitList(dauDataList, 50000);

將50000,調(diào)整為10000,5000,1000,500,100等值逐一嘗試。

QPS確實(shí)逐步降下來(lái)了,但是即便是每次處理1000條,QPS也有40K左右。

3.2.程序中斷,拋異常

最終以每批次讀取500條數(shù)據(jù),將代碼上線。但是程序總是中斷報(bào)錯(cuò),拋出異常:

而這時(shí)候已處理的數(shù)據(jù)量達(dá)到幾千萬(wàn)條。

最初懷疑是因?yàn)閖edis對(duì)象沒(méi)有調(diào)用close方法,于是修改代碼如下:

package com.sina.weibo;

import com.sina.weibo.util.FileUtils;
import com.sina.weibo.util.ListUtil;
import org.apache.commons.lang3.time.StopWatch;
import redis.clients.jedis.Jedis;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.concurrent.TimeUnit;

public class Application {
    /** dau數(shù)據(jù)讀取路徑 */
    private static String dauDataPath = "/data1/sinawap/var/logs/wapcommon/place/user_position/dau/login_day_20220913.txt";

    /** 結(jié)果輸出路徑 */
    private static String outputPath = "/data1/bingqing5/importcampusdata/output/campus_data.txt";

    /** 已處理過(guò)的uid數(shù)據(jù)存儲(chǔ)路徑 */
    private static String processedUidDataPath = "/data1/bingqing5/importcampusdata/process/processed_uid.txt";

    public static void main(String[] args) {
        StopWatch stopWatch = new StopWatch();
        // 開(kāi)始時(shí)間
        stopWatch.start();
        System.out.println("================程序開(kāi)始===============");
        transfer(dauDataPath, processedUidDataPath, outputPath);
        System.out.println("================程序結(jié)束===============");
        // 結(jié)束時(shí)間
        stopWatch.stop();
        // 統(tǒng)計(jì)執(zhí)行時(shí)間(秒)
        System.out.println("執(zhí)行時(shí)長(zhǎng):" + stopWatch.getTime(TimeUnit.SECONDS) + " 秒.");
    }

    private static void transfer(String dauDataPath, String processedUidDataPath, String outputPath) {
        List<String> dauDataList = FileUtils.readInfoFromFile(dauDataPath);
        List<List<String>> bucket = ListUtil.splitList(dauDataList, 50000);
        List<String> processedUidDataList = FileUtils.readInfoFromFile(processedUidDataPath);
        LinkedHashSet<String> linkedHashSet = ListUtil.getLinkedHashSet(processedUidDataList);
        for (List<String> list : bucket) {
            Jedis jedis = new Jedis(rdsxxxxx.xxxx.xxxx.xxxx.com.cn", 50000);
            List<String> jsonStrList = jedis.mget(list.toArray(new String[list.size()]));
            for (int i = 0; i < list.size(); i++) {
                if (!linkedHashSet.contains(list.get(i))) {
                    String uid = list.get(i);
                    FileUtils.appendInfoToFile(processedUidDataPath, uid);
                    String jsonStr = jsonStrList.get(i);
                    if (jsonStr == null || jsonStr == "") continue;
                    String content = uid + "\t" + jsonStr;
                    FileUtils.appendInfoToFile(outputPath, content);
                }
            }
            jedis.close();
            System.out.println(list.size());
        }
    }
}

修改后跑程序依舊沒(méi)有任何改善,繼續(xù)修改,代碼如下:

package com.sina.weibo;

import com.sina.weibo.util.FileUtils;
import com.sina.weibo.util.ListUtil;
import org.apache.commons.lang3.time.StopWatch;
import redis.clients.jedis.Jedis;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.concurrent.TimeUnit;

public class A {
    /** dau數(shù)據(jù)讀取路徑 */
    private static String dauDataPath = "/data1/sinawap/var/logs/wapcommon/place/user_position/dau/login_day_20220913.txt";

    /** 結(jié)果輸出路徑 */
    private static String outputPath = "/data1/bingqing5/importcampusdata/output/campus_data.txt";

    /** 已處理過(guò)的uid數(shù)據(jù)存儲(chǔ)路徑 */
    private static String processedUidDataPath = "/data1/bingqing5/importcampusdata/process/processed_uid.txt";

    public static void main(String[] args) {
        StopWatch stopWatch = new StopWatch();
        // 開(kāi)始時(shí)間
        stopWatch.start();
        System.out.println("================程序開(kāi)始===============");
        transfer(dauDataPath, processedUidDataPath, outputPath);
        System.out.println("================程序結(jié)束===============");
        // 結(jié)束時(shí)間
        stopWatch.stop();
        // 統(tǒng)計(jì)執(zhí)行時(shí)間(秒)
        System.out.println("執(zhí)行時(shí)長(zhǎng):" + stopWatch.getTime(TimeUnit.SECONDS) + " 秒.");
    }

    private static void transfer(String dauDataPath, String processedUidDataPath, String outputPath) {
        List<String> dauDataList = FileUtils.readInfoFromFile(dauDataPath);
        List<List<String>> bucket = ListUtil.splitList(dauDataList, 50000);
        List<String> processedUidDataList = FileUtils.readInfoFromFile(processedUidDataPath);
        LinkedHashSet<String> linkedHashSet = ListUtil.getLinkedHashSet(processedUidDataList);
        for (List<String> list : bucket) {
            Jedis jedis = new Jedis("rdsxxxxx.xxxx.xxxx.xxxx.com.cn", 50000);
            List<String> jsonStrList = jedis.mget(list.toArray(new String[list.size()]));
            for (int i = 0; i < list.size(); i++) {
                if (!linkedHashSet.contains(list.get(i))) {
                    String uid = list.get(i);
                    FileUtils.appendInfoToFile(processedUidDataPath, uid);
                    String jsonStr = jsonStrList.get(i);
                    if (jsonStr == null || jsonStr == "") continue;
                    String content = uid + "\t" + jsonStr;
                    FileUtils.appendInfoToFile(outputPath, content);
                }
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                jedis.close();
            }
            System.out.println(list.size());
        }
    }
}

上線以后,觀測(cè)發(fā)現(xiàn)QPS區(qū)域穩(wěn)定,但是程序會(huì)空跑,也就是從頭開(kāi)始將已處理的數(shù)據(jù)也要逐一讀取一次,很多時(shí)候都沒(méi)有跑到上次程序處理的地方就已經(jīng)被迫退出。

linkedHashSet本來(lái)是用來(lái)標(biāo)記上次程序運(yùn)行停止的地方,但是似乎并沒(méi)有完全發(fā)揮作用。

于是修改代碼,加入一個(gè)新的list集合,用于存放還沒(méi)有處理過(guò)的數(shù)據(jù),代碼如下:

package com.sina.weibo;

import com.sina.weibo.util.FileUtils;
import com.sina.weibo.util.ListUtil;
import org.apache.commons.lang3.time.StopWatch;
import redis.clients.jedis.Jedis;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author bingqing5 
 * @date 2022/09/14 15:00
 * @version 1.0
 */

public class Application {

    /** dau數(shù)據(jù)讀取路徑 */
    private static String dauDataPath = "/data1/sinawap/var/logs/wapcommon/place/user_position/dau/login_day_20220913.txt";

    /** 結(jié)果輸出路徑 */
    private static String outputPath = "/data1/bingqing5/importcampusdata/output/campus_data.txt";
    
    /** 已處理過(guò)的uid數(shù)據(jù)存儲(chǔ)路徑 */
    private static String processedUidDataPath = "/data1/bingqing5/importcampusdata/process/processed_uid.txt";

    public static void main(String[] args) {
        StopWatch stopWatch = new StopWatch();
        // 開(kāi)始時(shí)間
        stopWatch.start();
        System.out.println("================程序開(kāi)始===============");
//        transfer(dauDataPath, processedUidDataPath, outputPath);
        List<String> dauDataList = FileUtils.readInfoFromFile(dauDataPath);
//        List<List<String>> bucket = ListUtil.splitList(dauDataList, 50000);
//        Jedis jedis = new Jedis("rdsxxxxx.xxxx.xxxx.xxxx.com.cn", 50000);
        List<String> processedUidDataList = FileUtils.readInfoFromFile(processedUidDataPath);
        LinkedHashSet<String> linkedHashSet = ListUtil.getLinkedHashSet(processedUidDataList);
        List<String> uidList = new ArrayList<>();
        for (String uid : dauDataList) {
            if (linkedHashSet.contains(uid)) {
                continue;
            } else {
                uidList.add(uid);
            }
        }

        List<List<String>> bucket;
        if (uidList.size() != 0) {
            bucket = ListUtil.splitList(uidList, 10000);
        } else {
            bucket = new ArrayList<>();
        }

        for (List<String> list : bucket) {
            Jedis jedis = new Jedis("rdsxxxxx.xxxx.xxxx.xxxx.com.cn", 50000);
            List<String> jsonStrList = jedis.mget(list.toArray(new String[list.size()]));
            for (int i = 0; i < list.size(); i++) {
                if (!linkedHashSet.contains(list.get(i))) {
                    String uid = list.get(i);
                    FileUtils.appendInfoToFile(processedUidDataPath, uid);
                    String jsonStr = jsonStrList.get(i);
                    if (jsonStr == null || jsonStr == "") continue;
                    String content = uid + "\t" + jsonStr;
                    FileUtils.appendInfoToFile(outputPath, content);
                }
            }
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                jedis.close();
            }
            System.out.println(list.size());
        }
        System.out.println("================程序結(jié)束===============");
        // 結(jié)束時(shí)間
        stopWatch.stop();
        // 統(tǒng)計(jì)執(zhí)行時(shí)間(秒)
        System.out.println("執(zhí)行時(shí)長(zhǎng):" + stopWatch.getTime(TimeUnit.SECONDS) + " 秒.");
    }


}

終于這次修改后,上線代碼,代碼平穩(wěn)運(yùn)行。

此時(shí)查看QPS,發(fā)現(xiàn)10000的批讀取量,QPS文檔在25K以下,此前同樣的數(shù)據(jù)量,QPS能達(dá)到40K。

3.3.內(nèi)存消耗過(guò)大

在上次修改后,程序平穩(wěn)運(yùn)行,期間我查看了機(jī)器狀態(tài),發(fā)現(xiàn)我跑的jar包竟然消耗了32%左右的內(nèi)存,那臺(tái)機(jī)器也不過(guò)62G的總內(nèi)存。雖然不缺內(nèi)存資源,但是還是決定趁著程序在跑的期間,回顧一下代碼。

List<List<String>> bucket = ListUtil.splitList(dauDataList, 10000);

上面這行代碼是將所有的用戶(hù)uid數(shù)據(jù)按照10000的大小均等分割,每次遍歷,要重復(fù)創(chuàng)建同一類(lèi)Jedis對(duì)象,也會(huì)消耗大量?jī)?nèi)存。

另外,下面這段程序:

 List<String> uidList = new ArrayList<>();
        for (String uid : dauDataList) {
            if (linkedHashSet.contains(uid)) {
                continue;
            } else {
                uidList.add(uid);
            }
        }

已經(jīng)對(duì)處理過(guò)的數(shù)據(jù)做過(guò)篩選,在循環(huán)中再次做如下判斷:

if (!linkedHashSet.contains(list.get(i))) {
                   
}

也是多次一舉,會(huì)增加耗時(shí)。

綜合以上考慮,我做了修改,代碼如下:

package com.sina.weibo;

import com.sina.weibo.util.FileUtils;
import com.sina.weibo.util.ListUtil;
import org.apache.commons.lang3.time.StopWatch;
import redis.clients.jedis.Jedis;

import java.util.ArrayList;
import java.util.LinkedHashSet;
import java.util.List;
import java.util.concurrent.TimeUnit;

/**
 * @author bingqing5
 * @date 2022/09/14 15:00
 * @version 1.0
 */

public class Application {

    /** dau數(shù)據(jù)讀取路徑 */
    private static String dauDataPath = "/data1/sinawap/var/logs/wapcommon/place/user_position/dau/login_day_20220913.txt";

    /** 結(jié)果輸出路徑 */
//    private static String outputPath = "/data1/bingqing5/redis_test/output/campus_data.txt";
    private static String outputPath = "/data1/bingqing/redis_test/output/campus_data.txt";

    /** 已處理過(guò)的uid數(shù)據(jù)存儲(chǔ)路徑 */
//    private static String processedUidDataPath = "/data1/bingqing5/redis_test/process/processed_uid.txt";
    private static String processedUidDataPath = "/data1/bingqing/redis_test/process/processed_uid.txt";

    public static void main(String[] args) {
        StopWatch stopWatch = new StopWatch();
        // 開(kāi)始時(shí)間
        stopWatch.start();
        System.out.println("================程序開(kāi)始===============");
        transfer(dauDataPath, processedUidDataPath, outputPath);
        System.out.println("================程序結(jié)束===============");
        // 結(jié)束時(shí)間
        stopWatch.stop();
        // 統(tǒng)計(jì)執(zhí)行時(shí)間(秒)
        System.out.println("執(zhí)行時(shí)長(zhǎng):" + stopWatch.getTime(TimeUnit.SECONDS) + " 秒.");
    }

    private static void transfer(String dauDataPath, String processedUidDataPath, String outputPath) {
        List<String> dauDataList = FileUtils.readInfoFromFile(dauDataPath);
        Jedis jedis = new Jedis("rdsxxxxx.xxxx.xxxx.xxxx.com.cn", 50000);
        List<String> processedUidDataList = FileUtils.readInfoFromFile(processedUidDataPath);
        LinkedHashSet<String> linkedHashSet = ListUtil.getLinkedHashSet(processedUidDataList);
        List<String> uidList = new ArrayList<>();
        for (String uid : dauDataList) {
            if (linkedHashSet.contains(uid)) {
                continue;
            } else {
                uidList.add(uid);
            }
        }
        List<List<String>> bucket;
        if (uidList.size() != 0) {
            bucket = ListUtil.splitList(uidList, 50000);
        } else {
            bucket = new ArrayList<>();
        }

        for (List<String> list : bucket) {
            List<String> jsonStrList = jedis.mget(list.toArray(new String[list.size()]));
            for (int i = 0; i < list.size(); i++) {
                String uid = list.get(i);
                FileUtils.appendInfoToFile(processedUidDataPath, uid);
                String jsonStr = jsonStrList.get(i);
                if (jsonStr == null || jsonStr == "") continue;
                String content = uid + "\t" + jsonStr;
                FileUtils.appendInfoToFile(outputPath, content);
            }

            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                jedis.close();
            }
            System.out.println(list.size());
        }
    }
}

修改代碼以后,替換掉原先運(yùn)行的jar包,接著運(yùn)行。發(fā)現(xiàn)內(nèi)存消耗明顯降低,穩(wěn)定占總內(nèi)存的20%。

然后嘗試修改了mget參數(shù)量,修改為50000條,再次運(yùn)行程序發(fā)現(xiàn)QPS穩(wěn)定在40K左右。

總結(jié)

本篇算是筆者剛接觸Redis不久的一篇隨手記。通過(guò)本次需求的開(kāi)發(fā)經(jīng)歷,讓我對(duì)Redis有了直觀的了解,同時(shí)也理解了代碼優(yōu)化在實(shí)際生產(chǎn)工作和開(kāi)發(fā)中的潛在價(jià)值。

關(guān)于Redis,在快速直接從Redis讀取數(shù)據(jù)的場(chǎng)景中,尤其是數(shù)據(jù)量大的時(shí)候,為了防止QPS過(guò)高,最好在處理一批次數(shù)據(jù)后空出一定的時(shí)間間隔,比如可以讓線程暫時(shí)休眠一定時(shí)間間隔,再進(jìn)行下批次讀取和處理。

關(guān)于代碼優(yōu)化,盡量創(chuàng)建可重復(fù)使用的對(duì)象,非必要不添加同類(lèi)對(duì)象,避免大量創(chuàng)建對(duì)象帶來(lái)的資源消耗,本次經(jīng)歷也算是很鮮明的體會(huì)到這點(diǎn)。

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • JAVA中IP和整數(shù)相互轉(zhuǎn)化的方法

    JAVA中IP和整數(shù)相互轉(zhuǎn)化的方法

    這篇文章主要介紹了JAVA中IP和整數(shù)相互轉(zhuǎn)化的方法,涉及java數(shù)值轉(zhuǎn)換的相關(guān)技巧,需要的朋友可以參考下
    2015-05-05
  • mybatis插入后返回主鍵id的3種方式圖解

    mybatis插入后返回主鍵id的3種方式圖解

    這篇文章主要給大家介紹了關(guān)于mybatis插入后返回主鍵id的3種方式,很多時(shí)候,在向數(shù)據(jù)庫(kù)插入數(shù)據(jù)時(shí),需要保留插入數(shù)據(jù)的,以便進(jìn)行后續(xù)的操作或者將存入其他表作為外鍵,需要的朋友可以參考下
    2023-08-08
  • 基于SpringBoot實(shí)現(xiàn)QQ郵箱驗(yàn)證碼注冊(cè)功能

    基于SpringBoot實(shí)現(xiàn)QQ郵箱驗(yàn)證碼注冊(cè)功能

    QQ 郵箱是由騰訊公司推出的一款免費(fèi)郵箱服務(wù),它提供了完整的郵件發(fā)送和接收功能,并且還支持多種郵件格式和附件類(lèi)型,QQ 郵箱還具有強(qiáng)大的反垃圾郵件功能,可以有效地過(guò)濾垃圾郵件,并保護(hù)用戶(hù)隱私和安全,所以本文給大家介紹了基于SpringBoot實(shí)現(xiàn)QQ郵箱驗(yàn)證碼注冊(cè)功能
    2024-11-11
  • 如何解決@value獲取不到y(tǒng)aml數(shù)組的問(wèn)題

    如何解決@value獲取不到y(tǒng)aml數(shù)組的問(wèn)題

    文章介紹了在使用YAML配置文件時(shí),通過(guò)@Value注解獲取整數(shù)和數(shù)組列表的配置方法,并提供了兩種解決方案:一種適用于非嵌套列表,另一種適用于嵌套列表等復(fù)雜配置
    2024-11-11
  • 深度剖析java中JDK動(dòng)態(tài)代理機(jī)制

    深度剖析java中JDK動(dòng)態(tài)代理機(jī)制

    本篇文章主要介紹了深度剖析java中JDK動(dòng)態(tài)代理機(jī)制 ,動(dòng)態(tài)代理避免了開(kāi)發(fā)人員編寫(xiě)各個(gè)繁鎖的靜態(tài)代理類(lèi),只需簡(jiǎn)單地指定一組接口及目標(biāo)類(lèi)對(duì)象就能動(dòng)態(tài)的獲得代理對(duì)象。
    2017-04-04
  • 基于springboot微信公眾號(hào)開(kāi)發(fā)(微信自動(dòng)回復(fù))

    基于springboot微信公眾號(hào)開(kāi)發(fā)(微信自動(dòng)回復(fù))

    這篇文章主要介紹了基于springboot微信公眾號(hào)開(kāi)發(fā)(微信自動(dòng)回復(fù)),文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧
    2019-11-11
  • java中利用Dom4j解析和生成XML文檔

    java中利用Dom4j解析和生成XML文檔

    本篇文章主要介紹了java中利用Dom4j解析和生成XML文檔,dom4j是一套非常優(yōu)秀的Java開(kāi)源api,主要用于讀寫(xiě)xml文檔,具有性能優(yōu)異、功能強(qiáng)大、和非常方便使用的特點(diǎn)。有興趣的可以了解一下。
    2016-11-11
  • Java運(yùn)算符>、>>、>>>三者的區(qū)別

    Java運(yùn)算符>、>>、>>>三者的區(qū)別

    這篇文章主要介紹了Java運(yùn)算符>、>>、>>>三者的區(qū)別,做了一個(gè)簡(jiǎn)單的對(duì)比,并用實(shí)例說(shuō)明,需要的朋友可以參考下
    2014-06-06
  • SpringBoot深入講解單元測(cè)試與熱部署應(yīng)用

    SpringBoot深入講解單元測(cè)試與熱部署應(yīng)用

    這篇文章介紹了SpringBoot單元測(cè)試與熱部署,文中通過(guò)示例代碼介紹的非常詳細(xì)。對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2022-06-06
  • Java 日期轉(zhuǎn)換詳解及實(shí)例代碼

    Java 日期轉(zhuǎn)換詳解及實(shí)例代碼

    這篇文章主要介紹了Java 日期轉(zhuǎn)換詳解及實(shí)例代碼的相關(guān)資料,需要的朋友可以參考下
    2016-11-11

最新評(píng)論