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

Springboot公共字段填充及ThreadLocal模塊改進(jìn)方案

 更新時(shí)間:2022年11月30日 09:15:06   作者:掘金LiBiGo  
這篇文章主要為大家介紹了Springboot公共字段填充及ThreadLocal模塊改進(jìn)方案詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪

1. 公共字段自動(dòng)填充

1.1 問題分析

在新增員工時(shí)需要設(shè)置創(chuàng)建時(shí)間、創(chuàng)建人、修改時(shí)間、修改人等字段,在編輯員工時(shí)需要設(shè)置修改時(shí)間、修改人等字段。這些字段屬于公共字段,也就是也就是在系統(tǒng)中很多表中都會(huì)有這些字段,如下:

 而針對(duì)于這些字段的賦值方式為:

A. 在新增數(shù)據(jù)時(shí), 將createTime、updateTime 設(shè)置為當(dāng)前時(shí)間, createUser、updateUser設(shè)置為當(dāng)前登錄用戶ID。

B. 在更新數(shù)據(jù)時(shí), 將updateTime 設(shè)置為當(dāng)前時(shí)間, updateUser設(shè)置為當(dāng)前登錄用戶ID。

目前,在項(xiàng)目中處理這些字段都是在每一個(gè)業(yè)務(wù)方法中進(jìn)行賦值操作,如下

如果都按照上述的操作方式來處理這些公共字段, 需要在每一個(gè)業(yè)務(wù)方法中進(jìn)行操作, 編碼相對(duì)冗余、繁瑣。

改進(jìn):使用Mybatis Plus提供的公共字段自動(dòng)填充功能來簡(jiǎn)化開發(fā)。

1.2 基本功能實(shí)現(xiàn)

1.2.1 思路分析

Mybatis Plus公共字段自動(dòng)填充,即在插入或者更新的時(shí)候?yàn)橹付ㄗ侄钨x予指定的值,好處統(tǒng)一對(duì)這些字段進(jìn)行處理,避免了重復(fù)代碼。在上述的問題分析中,有四個(gè)公共字段需要在新增/更新中進(jìn)行賦值操作, 具體情況如下:

字段名賦值時(shí)機(jī)說明
createTime插入(INSERT)當(dāng)前時(shí)間
updateTime插入(INSERT) , 更新(UPDATE)當(dāng)前時(shí)間
createUser插入(INSERT)當(dāng)前登錄用戶ID
updateUser插入(INSERT) , 更新(UPDATE)

實(shí)現(xiàn)步驟:

1、在實(shí)體類的屬性上加入@TableField注解,指定自動(dòng)填充的策略。

2、按照框架要求編寫元數(shù)據(jù)對(duì)象處理器,在此類中統(tǒng)一為公共字段賦值,此類需要實(shí)現(xiàn)MetaObjectHandler接口。

1.3 代碼實(shí)現(xiàn)

1). 實(shí)體類的屬性上加入@TableField注解,指定自動(dòng)填充的策略。

在員工Employee實(shí)體類的公共字段屬性上, 加上注解, 指定填充策略。

package com.itheima.reggie.entity;
import com.baomidou.mybatisplus.annotation.FieldFill;
import com.baomidou.mybatisplus.annotation.TableField;
import lombok.Data;
import java.io.Serializable;
import java.time.LocalDateTime;
/**@Description: 員工實(shí)體類 該實(shí)體類主要用于和員工表 employee 進(jìn)行映射。
 * @version v1.0
 * @author LiBiGo
 * @date 2022/8/12 11:05
 */
// 在實(shí)體類上添加@Data注解,可以省去代碼中大量的 get()、 set()、 toString() 等方法,提高代碼的簡(jiǎn)潔:
@Data
public class Employee implements Serializable {
    private static final long serialVersionUID = 1L;
    private Long id;
    private String username;
    private String name;
    private String password;
    private String phone;
    private String sex;
    // map-underscore-to-camel-case: true
    // 在映射實(shí)體或者屬性時(shí),將數(shù)據(jù)庫(kù)中表名和字段名中的下劃線去掉,按照駝峰命名法映射
    private String idNumber; //身份證 因?yàn)樵谂渲梦募性O(shè)置駝峰命名,所以與數(shù)據(jù)庫(kù)中的不太一樣,數(shù)據(jù)庫(kù)中為id_number
    private Integer status;
    @TableField(fill = FieldFill.INSERT) // 插入時(shí)填充該屬性值
    private LocalDateTime createTime; // 同上 駝峰命名法
    @TableField(fill = FieldFill.INSERT_UPDATE) //插入、更新時(shí)填充該屬性值
    private LocalDateTime updateTime;   // 同上 駝峰命名法
    @TableField(fill = FieldFill.INSERT)
    private Long createUser;
    @TableField(fill = FieldFill.INSERT_UPDATE) //插入和更新時(shí)填充
    private Long updateUser;
}

2). 按照框架要求編寫元數(shù)據(jù)對(duì)象處理器,在此類中統(tǒng)一為公共字段賦值,此類需要實(shí)現(xiàn)MetaObjectHandler接口。

package com.itheima.reggie.common;
import com.baomidou.mybatisplus.core.handlers.MetaObjectHandler;
import lombok.extern.slf4j.Slf4j;
import org.apache.ibatis.reflection.MetaObject;
import org.springframework.stereotype.Component;
import java.time.LocalDateTime;
/**
 * Description: 自定義元數(shù)據(jù)對(duì)象處理器|統(tǒng)一為公共字段賦值,此類需要實(shí)現(xiàn)MetaObjectHandler接口。
 * @author w
 * @version 1.0
 * @date 2022/8/15 12:05
 */
@Component
@Slf4j
public class MyMetaObjecthandler implements MetaObjectHandler {
    @Override
    public void insertFill(MetaObject metaObject) {
        /**@Description: 插入操作自動(dòng)填充
         * @version v1.0
         * @author LiBiGo
         * @date 2022/8/15 12:14
         */
        log.info("公共字段自動(dòng)填充insert...");
        log.info(metaObject.toString());
        metaObject.setValue("createTime", LocalDateTime.now());
        metaObject.setValue("updateTime", LocalDateTime.now());
        metaObject.setValue("createUser", BaseContext.getCurrentId()); // 使用了Thread 看后續(xù)
        metaObject.setValue("updateUser", BaseContext.getCurrentId());
    }
    @Override
    public void updateFill(MetaObject metaObject) {
        /**@Description: 更新操作自動(dòng)填充
         * @version v1.0
         * @author LiBiGo
         * @date 2022/8/15 12:14
         */
        log.info("公共字段自動(dòng)填充update...");
        log.info(metaObject.toString());
        long id = Thread.currentThread().getId();
        log.info("線程ID:{}",id);
        metaObject.setValue("updateTime", LocalDateTime.now());
        metaObject.setValue("updateUser", BaseContext.getCurrentId());
    }
}

2 使用ThreadLocal對(duì)公共字段填充功能進(jìn)行完善

2.1 思路分析

2.1.1 提出設(shè)想

前面已經(jīng)完成公共字段自動(dòng)填充功能的代碼開發(fā),但是還有一個(gè)問題沒有解決,就是在自動(dòng)填充createUser和updateUser時(shí)設(shè)置的用戶id是固定值,現(xiàn)在需要改造成動(dòng)態(tài)獲取當(dāng)前登錄用戶的id。

提出設(shè)想:用戶登錄成功后將用戶id存入了HttpSession中,現(xiàn)在從HttpSession中獲取不就行了?

存在問題:在MyMetaObjectHandler類中是不能直接獲得HttpSession對(duì)象的,所以需要通過其他方式來獲取登錄用戶id。

2.1.2 分析問題

在修改員工信息時(shí), 業(yè)務(wù)的執(zhí)行流程如下圖:

客戶端發(fā)送的每次http請(qǐng)求,對(duì)應(yīng)的在服務(wù)端都會(huì)分配一個(gè)新的線程來處理,在處理過程中涉及到下面類中的方法都屬于相同的一個(gè)線程:

1). LoginCheckFilter的doFilter方法

2). EmployeeController的update方法

3). MyMetaObjectHandler的updateFill方法

我們可以在上述類的方法中加入如下代碼(獲取當(dāng)前線程ID,并輸出):

long id = Thread.currentThread().getId();
log.info("線程id為:{}",id);

執(zhí)行編輯員工功能進(jìn)行驗(yàn)證,通過觀察控制臺(tái)輸出可以發(fā)現(xiàn),一次請(qǐng)求對(duì)應(yīng)的線程id是相同的:

經(jīng)過上述的分析之后,發(fā)現(xiàn)可以使用JDK提供的一個(gè)ThreadLocal類來解決此問題。

2.2 ThreadLocal

2.2.1 ThreadLocal基本概述

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

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

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

2.2.2 ThreadLocal常用方法

A. public void set(T value) : 設(shè)置當(dāng)前線程的線程局部變量的值

B. public T get() : 返回當(dāng)前線程所對(duì)應(yīng)的線程局部變量的值

C. public void remove() : 刪除當(dāng)前線程所對(duì)應(yīng)的線程局部變量的值

我們可以在LoginCheckFilter的doFilter方法中獲取當(dāng)前登錄用戶id,并調(diào)用ThreadLocal的set方法來設(shè)置當(dāng)前線程的線程局部變量的值(用戶id),然后在MyMetaObjectHandler的updateFill方法中調(diào)用ThreadLocal的get方法來獲得當(dāng)前線程所對(duì)應(yīng)的線程局部變量的值(用戶id)。 如果在后續(xù)的操作中, 我們需要在Controller / Service中要使用當(dāng)前登錄用戶的ID, 可以直接從ThreadLocal直接獲取。

2.3 操作步驟

1). 編寫B(tài)aseContext工具類,基于ThreadLocal封裝的工具類

2). 在LoginCheckFilter的doFilter方法中調(diào)用BaseContext來設(shè)置當(dāng)前登錄用戶的id

3). 在MyMetaObjectHandler的方法中調(diào)用BaseContext獲取登錄用戶的id

2.4 代碼實(shí)現(xiàn)

1). BaseContext工具類

package com.itheima.reggie.common;
/**
 * Description: 基于ThreadLocal封裝工具類,用戶保存和獲取當(dāng)前登陸用戶ID
 * @date 2022/8/15 12:40
 */
public class BaseContext {
    private static ThreadLocal<Long> threadLocal = new ThreadLocal<>();
    /**
     * 設(shè)置值
     * @param id
     */
    public static void setCurrentId(Long id){
        threadLocal.set(id);
    }
    /**
     * 獲取值
     * @return
     */
    public static Long getCurrentId(){
        return threadLocal.get();
    }
}

2).LoginCheckFilter中存放當(dāng)前登錄用戶到ThreadLocal

在doFilter方法中, 判定用戶是否登錄, 如果用戶登錄, 在放行之前, 獲取HttpSession中的登錄用戶信息, 調(diào)用BaseContext的setCurrentId方法將當(dāng)前登錄用戶ID存入ThreadLocal。

package com.itheima.reggie.filter;
import com.alibaba.fastjson.JSON;
import com.itheima.reggie.common.BaseContext;
import com.itheima.reggie.common.R;
import lombok.extern.slf4j.Slf4j;
import org.springframework.util.AntPathMatcher;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
 * Description: 檢查用戶是否已經(jīng)完成登陸
 */
@WebFilter(filterName = "LoginCheckFilter",urlPatterns = "/*")  //設(shè)置攔截器,設(shè)置攔截的網(wǎng)頁區(qū)域
@Slf4j
public class LoginCheckFilter implements Filter {
    // 路徑匹配器,支持通配符,因?yàn)橄旅娴男蛄惺褂昧送ㄅ浞?
    // AntPathMatcher匹配規(guī)則  ? 匹配一個(gè)字符    * 匹配0個(gè)或多個(gè)字符   ** 匹配0個(gè)或多個(gè)目錄/字符
    public static final AntPathMatcher PATH_MATCHER = new AntPathMatcher();
    @Override
    public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
        HttpServletRequest request = (HttpServletRequest)servletRequest;
        HttpServletResponse response = (HttpServletResponse)servletResponse;
        //  A. 獲取本次請(qǐng)求的URI
        String requestURI = request.getRequestURI();
        log.info("攔截的請(qǐng)求:{}",requestURI);
        // 定義不攔截的序列,只攔截頁面數(shù)據(jù)請(qǐng)求,不攔截頁面格式
        String[] urls = new String[]{
                "/employee/login",
                "/employee/logout",
                "/backend/**",
                "/front/**"
        };
        //  B. 判斷本次請(qǐng)求, 是否需要登錄, 才可以訪問
        boolean check = check(urls, requestURI);
        //  C. 如果不需要,則直接放行
        if(check){
            log.info("本次請(qǐng)求{}不需要處理",requestURI);
            filterChain.doFilter(request,response);
            return;
        }
        //  D. 判斷登錄狀態(tài),如果已登錄,則直接放行
        if(request.getSession().getAttribute("employee")!=null){
            log.info("用戶已登錄,用戶id為:{}",request.getSession().getAttribute("employee"));
            Long empId = (Long) request.getSession().getAttribute("employee"); // 獲取當(dāng)前用戶登陸id
            BaseContext.setCurrentId(empId); //設(shè)置線程共享
            filterChain.doFilter(request,response); // 補(bǔ)課 不清楚????????????
            long id = Thread.currentThread().getId();
            log.info("檢測(cè)是否登陸線程ID:{}",id);
            return;
        }
        //  E. 如果未登錄, 則返回未登錄結(jié)果
        response.getWriter().write(JSON.toJSONString(R.error("NOTLOGIN")));
        return;
    }
    public boolean check(String[] urls,String requestURI){
        /**@Description: 路徑匹配,檢查本次請(qǐng)求是否需要放行
         * @author LiBiGo
         * @date 2022/8/12 16:50
         */
        for (String url : urls) {
            boolean match = PATH_MATCHER.match(url, requestURI); // 使用通配符對(duì)象,配對(duì)資源
            if(match){
                return true;
            }
        }
        return false;
    }
}

3). MyMetaObjectHandler中從ThreadLocal中獲取

將之前在代碼中添加動(dòng)態(tài)調(diào)用BaseContext中的getCurrentId方法獲取當(dāng)前登錄用戶ID

3 功能測(cè)試

編寫完了元數(shù)據(jù)對(duì)象處理器之后,我們就可以將之前在新增和修改方法中手動(dòng)賦值的代碼刪除或注釋掉。

完善了元數(shù)據(jù)對(duì)象處理器重新啟動(dòng)項(xiàng)目,完成登錄操作后,在員工管理模塊中,測(cè)試增加/更新員工信息功能,直接查詢數(shù)據(jù)庫(kù)數(shù)據(jù)變更,看看我們?cè)谛略?修改數(shù)據(jù)時(shí),這些公共字段數(shù)據(jù)是否能夠完成自動(dòng)填充, 并且看看填充的create_user 及 update_user字段值是不是本地登錄用戶的ID。

以上就是Springboot公共字段填充及ThreadLocal模塊改進(jìn)方案的詳細(xì)內(nèi)容,更多關(guān)于Springboot 公共字段填充的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!

相關(guān)文章

  • 通俗易懂學(xué)習(xí)java并發(fā)工具類-Semaphore,Exchanger

    通俗易懂學(xué)習(xí)java并發(fā)工具類-Semaphore,Exchanger

    這篇文章主要介紹了java并發(fā)工具類-Semaphore,Exchanger,java并發(fā)工具類有很多,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,下面小編帶大家來一起學(xué)習(xí)一下吧
    2019-06-06
  • Java實(shí)現(xiàn)批量合并Excel工作表

    Java實(shí)現(xiàn)批量合并Excel工作表

    這篇文章主要為大家詳細(xì)介紹了如何使用Java快速實(shí)現(xiàn)批量Excel工作表的合并,文中的示例代碼代碼講解詳細(xì),有需要的小伙伴可以跟隨小編一起學(xué)習(xí)一下
    2023-10-10
  • Spring Boot參數(shù)校驗(yàn)及分組校驗(yàn)的使用教程

    Spring Boot參數(shù)校驗(yàn)及分組校驗(yàn)的使用教程

    在日常的開發(fā)中,參數(shù)校驗(yàn)是非常重要的一個(gè)環(huán)節(jié),嚴(yán)格參數(shù)校驗(yàn)會(huì)減少很多出bug的概率,增加接口的安全性,下面這篇文章主要給大家介紹了關(guān)于Spring Boot參數(shù)校驗(yàn)及分組校驗(yàn)使用的相關(guān)資料,需要的朋友可以參考下
    2021-08-08
  • Java異常處理Guava?Throwables類使用實(shí)例解析

    Java異常處理Guava?Throwables類使用實(shí)例解析

    這篇文章主要為大家介紹了Java異常處理神器Guava?Throwables類使用深入詳解,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪
    2023-12-12
  • Java面試題沖刺第四天--數(shù)據(jù)庫(kù)

    Java面試題沖刺第四天--數(shù)據(jù)庫(kù)

    這篇文章主要為大家分享了最有價(jià)值的三道數(shù)據(jù)庫(kù)面試題,涵蓋內(nèi)容全面,包括數(shù)據(jù)結(jié)構(gòu)和算法相關(guān)的題目、經(jīng)典面試編程題等,感興趣的小伙伴們可以參考一下
    2021-07-07
  • 基于SpringCloud手寫一個(gè)簡(jiǎn)易版Sentinel

    基于SpringCloud手寫一個(gè)簡(jiǎn)易版Sentinel

    SpringCloud Alibaba Sentinel是當(dāng)前最為流行一種熔斷降級(jí)框架,簡(jiǎn)單易用的方式可以快速幫助我們實(shí)現(xiàn)服務(wù)的限流和降級(jí),保證服務(wù)的穩(wěn)定性。
    2021-05-05
  • Spring Boot前后端分離開發(fā)模式中的跨域問題及解決方法

    Spring Boot前后端分離開發(fā)模式中的跨域問題及解決方法

    本文介紹了解決Spring Boot前端Vue跨域問題的實(shí)戰(zhàn)經(jīng)驗(yàn),并提供了后端和前端的配置示例,通過配置后端和前端,我們可以輕松解決跨域問題,實(shí)現(xiàn)正常的前后端交互,需要的朋友可以參考下
    2023-09-09
  • 在Java中操作Zookeeper的示例代碼詳解

    在Java中操作Zookeeper的示例代碼詳解

    這篇文章主要介紹了在Java中操作Zookeeper的示例代碼詳解,本文通過實(shí)例代碼給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下
    2020-07-07
  • Java中List轉(zhuǎn)字符串的5種方法解析

    Java中List轉(zhuǎn)字符串的5種方法解析

    在Java中將一個(gè)List轉(zhuǎn)換為字符串有多種方法,下面這篇文章主要給大家介紹了關(guān)于Java中List轉(zhuǎn)字符串的5種方法,文中通過代碼介紹的非常詳細(xì),需要的朋友可以參考下
    2023-11-11
  • MyBatis Mapper.xml入?yún)ist使用in函數(shù)問題

    MyBatis Mapper.xml入?yún)ist使用in函數(shù)問題

    文章主要講述了在使用MyBatis的Mapper.xml文件時(shí),如何正確地在in函數(shù)中使用List作為入?yún)?作者強(qiáng)調(diào)了完整拷貝<if>...</if>格式的重要性,并指出稍微的改動(dòng)就會(huì)導(dǎo)致錯(cuò)誤
    2025-02-02

最新評(píng)論