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

SpringBoot ThreadLocal實(shí)現(xiàn)公共字段自動(dòng)填充案例講解

 更新時(shí)間:2022年10月12日 11:39:39   作者:懶羊羊.java  
每一次在Controller層中封裝改動(dòng)數(shù)據(jù)的方法時(shí)都要重新設(shè)置一些共性字段,顯得十分冗余。為了解決此問題也是在項(xiàng)目中第一次利用到線程,總的來說還是讓我眼前一亮,也開闊了視野,對(duì)以后的開發(fā)具有深遠(yuǎn)的意義

一.字段自動(dòng)填充引入

先看一個(gè)現(xiàn)象,在之前寫好的表中,我們發(fā)現(xiàn)有很多字段重復(fù)出現(xiàn)

比如update_time、create_time、create_user…

這就導(dǎo)致需要在Controller層中每一次對(duì)表中數(shù)據(jù)進(jìn)行修改后調(diào)用一次.setCreateTime(LocalDateTime.now());或者setUpdateTime(LocalDateTime.now());等等“硬編碼問題又出現(xiàn)了”顯得格外麻煩

這些共性字段如何統(tǒng)一拿出來處理呢?MyBatisPlus給了我們解決方案,為了實(shí)現(xiàn)這一功能:

首先

我們需要在公共字段對(duì)應(yīng)的實(shí)體屬性上加上@TableField注解與指定填充策略,就像這樣:

    @TableField(fill = FieldFill.INSERT)
    private LocalDateTime createTime;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private LocalDateTime updateTime;
    @TableField(fill = FieldFill.INSERT)
    private Long createUser;
    @TableField(fill = FieldFill.INSERT_UPDATE)
    private Long updateUser;

不難看出,fill的類型是一個(gè)枚舉類

若是不需要處理則選擇DEAFULT,若是增添后需要修改字段的值選擇INSERT,若是涉及修改數(shù)據(jù)時(shí)需要修改字段的值則選擇UPDATE,若是插入和修改都需要設(shè)置字段的值則選擇INSERT_UPDATE就像updateTime和updateUser,只要涉及對(duì)數(shù)據(jù)的操作就要修改字段的值

類似于全局異常處理器,為了實(shí)現(xiàn)字段填充也需要定義一個(gè)元數(shù)據(jù)對(duì)象處理器

二.元數(shù)據(jù)對(duì)象處理器

在定義的類中實(shí)現(xiàn)MetaObjecthandler接口,并重寫接口中策略對(duì)應(yīng)的方法,就像這樣:

@Component
@Slf4j
public class MyMetaObjecthandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("公共字段自動(dòng)填充...");
        log.info(metaObject.toString());
    }
    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("公共字段自動(dòng)填充...");
        log.info(metaObject.toString());
    }
}

這里有點(diǎn)像繼承HttpServlet那個(gè)套路,重寫doGet()與doPost()方法,要實(shí)現(xiàn)什么功能就寫在對(duì)應(yīng)的方法里…

重寫完方法,不妨debug一下:

發(fā)現(xiàn)我實(shí)現(xiàn)接口后在方法里拿到了實(shí)體的數(shù)據(jù),并封裝到了形參里的metaObject對(duì)象中,接下來我就可以利用此對(duì)象來做公共字段的集中處理了!

所謂的自動(dòng)填充也正是因?yàn)樵擃悡碛蠤Component注解,在每一次的項(xiàng)目啟動(dòng)后就會(huì)被掃描到,加載到,而類中的方法又實(shí)現(xiàn)了功能,所以才可以做到自動(dòng)填充字段!

回到正題

我們要做的是把公共的字段做到統(tǒng)一填充,具體實(shí)現(xiàn)則是在重寫的方法里調(diào)用setValue()方法并傳進(jìn)去字段與參數(shù),就像這樣:

    @Override
    public void insertFill(MetaObject metaObject) {
        log.info("公共字段自動(dòng)填充...");
        log.info(metaObject.toString());
        metaObject.setValue("createTime", LocalDateTime.now());
        metaObject.setValue("updateTime",LocalDateTime.now());
    }
    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("公共字段自動(dòng)填充...");
        log.info(metaObject.toString());
        metaObject.setValue("updateTime",LocalDateTime.now());
    }

相比之前controller層中的employee.setCreateTime(LocalDateTime.now());/employee.setUpdateTime(LocalDateTime.now());來看還是比較好理解,無(wú)非就是“茴“字的另一種寫法

(但卻實(shí)現(xiàn)了一勞永逸)…

可能有人會(huì)問,懶羊羊你的updateUser與createUser字段怎么不處理呢?

確實(shí),還記得之前在controller層中我們是怎么處理的嗎?

為了確定User是誰(shuí),我們從Session里拿到了操作的對(duì)象id,并調(diào)用set方法修改了對(duì)象的字段值

按照上面的經(jīng)驗(yàn),為了能夠動(dòng)態(tài)的拿到id,我們應(yīng)該這樣設(shè)置:

可是,在此方法中metaObject對(duì)象好像不能利用Session里的empID,那要如何獲得對(duì)象的標(biāo)識(shí)呢?

不就是一個(gè)標(biāo)識(shí)么,我用線程id可以嗎?

三.Threadlocal的使用

在這之前,需要明白一點(diǎn),每當(dāng)客戶端發(fā)送一次HTTP請(qǐng)求,對(duì)應(yīng)在服務(wù)器端會(huì)分配一個(gè)新的線程來處理。項(xiàng)目設(shè)計(jì)到現(xiàn)在為止,以點(diǎn)擊一次保存按鈕作為一次請(qǐng)求,它會(huì)觸發(fā)過濾器、調(diào)用Controller層、MetaObjectHandler層的方法

我們通過Thread的內(nèi)部方法:long id = Thread.currentThread().getId();來獲得當(dāng)前線程的id,以日志的形式log.info("當(dāng)前線程id:{}",id);輸出到控制臺(tái):

可見,三者的線程id相同,說明他們?cè)谕粋€(gè)線程中,這就保證了一致性,也正是因?yàn)檫@個(gè)特性,所以可用來當(dāng)作表中的字段id使用來作為標(biāo)識(shí)

在此基礎(chǔ)上,大致方向已經(jīng)敲定是利用線程的特性,具體要如何實(shí)現(xiàn)呢?那就不得不需要了解一下ThreadLocal這個(gè)概念了:

1.ThreadLocal并不是一個(gè)Thread,而是Thread的局部變量。

2.當(dāng)使用ThreadLocal維護(hù)變量時(shí),ThreadLocal為每個(gè)使用該變量的線程提供獨(dú)立的變量副本,所以每一個(gè)線程都可以獨(dú)立地改變自己的副本,而不會(huì)影響其它線程所對(duì)應(yīng)的副本。

3.ThreadLocal為每個(gè)線程提供單獨(dú)一份存儲(chǔ)空間,具有線程隔離的效果,只有在線程內(nèi)才能獲取到對(duì)應(yīng)的值,線程外則不能訪問

常用方法

public void set(T value)設(shè)置當(dāng)前線程局部變量的值
public T get()返回當(dāng)前線程所對(duì)應(yīng)的線程局部變量的值

了解到ThreadLocal特性,我們就可以結(jié)合“同一線程”這一特性來獲得那個(gè)對(duì)象唯一的SessionId。

?? 在前面的登錄功能中我們?cè)O(shè)置了一個(gè)filter,在filter中我們已經(jīng)拿到過了一次SessionId,寫到這里解決方案不就出來了嘛——把filter中的SessionId當(dāng)作參數(shù)傳給ThreadLocal的set方法,然后在MetaObjectHandler實(shí)現(xiàn)類(元數(shù)據(jù)對(duì)象處理器)中調(diào)用ThreadLocal的get()方法得到SessionId

就像這樣:

1.為了規(guī)范,我們封裝一個(gè)類,功能是調(diào)用ThreadLocal的方法

/**
 * 基于ThreadLocal封裝工具類,用于保護(hù)和獲取當(dāng)前登錄id
 */
public class BaseContext {
    private static ThreadLocal<Long> threadLocal=new ThreadLocal();
    public static void setCurrentId(Long currentId){
        threadLocal.set(currentId);
    }
    public static Long getCurrentId(){
        return threadLocal.get();
    }
}

2.在filter中做到SessionId的遷移

3.在MetaObjectHandler實(shí)現(xiàn)類中利用SessionId完成公共字段填充設(shè)置

    @Override
    public void updateFill(MetaObject metaObject) {
        log.info("公共字段自動(dòng)填充...");
        log.info(metaObject.toString());
        long id = Thread.currentThread().getId();
        log.info("當(dāng)前線程id:{}",id);
        metaObject.setValue("updateTime",LocalDateTime.now());
        metaObject.setValue("createUser",BaseContext.getCurrentId());
        metaObject.setValue("updateUser",BaseContext.getCurrentId());
    }

這樣,就可以一勞永逸咯~

又是一個(gè)小技巧?。?/p>

到此這篇關(guān)于SpringBoot ThreadLocal實(shí)現(xiàn)公共字段自動(dòng)填充案例講解的文章就介紹到這了,更多相關(guān)SpringBoot自動(dòng)填充內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

最新評(píng)論