性欧美巨大乳,五月婷婷开心中文字幕,天天躁日日躁白天躁晚上躁,天天狠天天透天干天天怕∴

手機版 | 網(wǎng)站導(dǎo)航
東方科技網(wǎng) 綜合 >

要聞:3.自定義注解實現(xiàn)系統(tǒng)日志記載

博客園 | 2023-04-09 14:43:04


(資料圖)

前言

今天來分享一下我昨天的成果,昨天計劃復(fù)現(xiàn)若依系統(tǒng)的系統(tǒng)日志記載功能,若依的系統(tǒng)日志記載的主要實現(xiàn)使用過自定義注解配合切面類來實現(xiàn)的,這里會把標注@Log的方法在用戶調(diào)用完后,將方法的一部分信息記錄在數(shù)據(jù)庫的指定數(shù)據(jù)表中。因此我們需要java的spring開發(fā)四層結(jié)構(gòu):domain層、mapper層、service層、controller層。到這里項目就大概完成了,注意的是若依中自定義的工具類。本文的項目代碼鏈接:WomPlus: 結(jié)合若依項目對原始工單項目內(nèi)容進行增強 (gitee.com),若依項目鏈接:GitHub - yangzongzhuan/RuoYi-fast: (RuoYi)官方倉庫 基于SpringBoot的權(quán)限管理系統(tǒng) 易讀易懂、界面簡潔美觀。 核心技術(shù)采用Spring、MyBatis、Shiro沒有任何其它重度依賴。直接運行即可用

1.系統(tǒng)日志記載開發(fā)流程五步走

朋友們可以根據(jù)自己的項目來調(diào)節(jié)數(shù)據(jù)表結(jié)構(gòu),domain類、mapper接口以及Mapper.xml、Service接口及其實現(xiàn)類,我這里是根據(jù)自己項目需求來編寫的。

1.1 根據(jù)自己項目創(chuàng)建數(shù)據(jù)表wo_operate_log
USE `wom_plus`DROP TABLE IF EXISTS `wo_operate_log`CREATE TABLE `wo_opertae_log`(    `operate_id` BIGINT(20) NOT NULL AUTO_INCREMENT COMMENT "日志主鍵",    `title` VARCHAR(50) DEFAULT "" COMMENT "模塊標題",    `business_type` INT(2) DEFAULT 0 COMMENT "業(yè)務(wù)類型(0其它 1新增 2修改 3刪除)",    `method` VARCHAR(100) DEFAULT "" COMMENT "方法名稱",    `request_method` VARCHAR(10) DEFAULT "" COMMENT "請求方式",    `operator_type` INT(1) DEFAULT 0 COMMENT "操作類別(0其它 1后臺用戶 2手機端用戶)",    `operate_name` VARCHAR(50) DEFAULT "" COMMENT "操作人員",    `operate_url` VARCHAR(255) DEFAULT "" COMMENT "請求URL",    `operate_ip` VARCHAR(128) DEFAULT "" COMMENT "主機地址",    `operate_location` VARCHAR(255) DEFAULT "" COMMENT "操作地點",    `operate_param` VARCHAR(2000) DEFAULT "" COMMENT "請求參數(shù)",    `json_result` VARCHAR(2000) DEFAULT "" COMMENT "返回參數(shù)",    `status` INT(1) DEFAULT 0 COMMENT "操作狀態(tài)(0正常 1異常)",    `error_msg` VARCHAR(2000)   DEFAULT ""COMMENT "錯誤消息",    `operate_time` DATETIME COMMENT "操作時間",    `cost_time` BIGINT(20) DEFAULT 0 COMMENT "消耗時間",    PRIMARY KEY (operate_id),    KEY idx_sys_oper_log_bt (`business_type`),    KEY idx_sys_oper_log_s  (`status`),    KEY idx_sys_oper_log_ot (`operate_time`))ENGINE=INNODB COMMENT="操作日志記錄" DEFAULT CHARSET="utf8";
wo_operate_log1.2 根據(jù)自己項目編寫Operate實體類
/** * 操作日志記錄表 oper_log *  * @author ruoyi */public class OperateLog extends BaseEntity{    private static final long serialVersionUID = 1L;    /** 日志主鍵 */    @Excel(name = "操作序號", cellType = Excel.ColumnType.NUMERIC)    private Long operateId;    /** 操作模塊 */    @Excel(name = "操作模塊")    private String title;    /** 業(yè)務(wù)類型 */    @Excel(name = "業(yè)務(wù)類型", readConverterExp = "0=其它,1=新增,2=修改,3=刪除,4=授權(quán),5=導(dǎo)出,6=導(dǎo)入,7=強退,8=生成代碼,9=清空數(shù)據(jù)")    private Integer businessType;        /** 業(yè)務(wù)類型數(shù)組 */    private Integer[] businessTypes;    /** 請求方法 */    @Excel(name = "請求方法")    private String method;    /** 請求方式 */    @Excel(name = "請求方式")    private String requestMethod;    /** 操作人類別 */    @Excel(name = "操作類別", readConverterExp = "0=其它,1=后臺用戶,2=手機端用戶")    private Integer operatorType;    /** 操作人員 */    @Excel(name = "操作人員")    private String operateName;//    /** 部門名稱 *///    @Excel(name = "部門名稱")//    private String deptName;    /** 請求url */    @Excel(name = "請求地址")    private String operateUrl;    /** 操作地址 */    @Excel(name = "操作地址")    private String operateIp;    /** 操作地點 */    @Excel(name = "操作地點")    private String operateLocation;    /** 請求參數(shù) */    @Excel(name = "請求參數(shù)")    private String operateParam;    /** 返回參數(shù) */    @Excel(name = "返回參數(shù)")    private String jsonResult;    /** 狀態(tài)0正常 1異常 */    @Excel(name = "狀態(tài)", readConverterExp = "0=正常,1=異常")    private Integer status;    /** 錯誤消息 */    @Excel(name = "錯誤消息")    private String errorMsg;    /** 操作時間 */    @Excel(name = "操作時間", width = 30, dateFormat = "yyyy-MM-dd HH:mm:ss")    private Date operTime;    /** 消耗時間 */    @Excel(name = "消耗時間", suffix = "毫秒")    private Long costTime;    public static long getSerialVersionUID() {        return serialVersionUID;    }    public Long getOperateId() {        return operateId;    }    public void setOperateId(Long operateId) {        this.operateId = operateId;    }    public String getTitle() {        return title;    }    public void setTitle(String title) {        this.title = title;    }    public Integer getBusinessType() {        return businessType;    }    public void setBusinessType(Integer businessType) {        this.businessType = businessType;    }    public Integer[] getBusinessTypes() {        return businessTypes;    }    public void setBusinessTypes(Integer[] businessTypes) {        this.businessTypes = businessTypes;    }    public String getMethod() {        return method;    }    public void setMethod(String method) {        this.method = method;    }    public String getRequestMethod() {        return requestMethod;    }    public void setRequestMethod(String requestMethod) {        this.requestMethod = requestMethod;    }    public Integer getOperatorType() {        return operatorType;    }    public void setOperatorType(Integer operatorType) {        this.operatorType = operatorType;    }    public String getOperateName() {        return operateName;    }    public void setOperateName(String operateName) {        this.operateName = operateName;    }    public String getOperateUrl() {        return operateUrl;    }    public void setOperateUrl(String operateUrl) {        this.operateUrl = operateUrl;    }    public String getOperateIp() {        return operateIp;    }    public void setOperateIp(String operateIp) {        this.operateIp = operateIp;    }    public String getOperateLocation() {        return operateLocation;    }    public void setOperateLocation(String operateLocation) {        this.operateLocation = operateLocation;    }    public String getOperateParam() {        return operateParam;    }    public void setOperateParam(String operateParam) {        this.operateParam = operateParam;    }    public String getJsonResult() {        return jsonResult;    }    public void setJsonResult(String jsonResult) {        this.jsonResult = jsonResult;    }    public Integer getStatus() {        return status;    }    public void setStatus(Integer status) {        this.status = status;    }    public String getErrorMsg() {        return errorMsg;    }    public void setErrorMsg(String errorMsg) {        this.errorMsg = errorMsg;    }    public Date getOperateTime() {        return operTime;    }    public void setOperateTime(Date operateTime) {        this.operTime = operateTime;    }    public Long getCostTime() {        return costTime;    }    public void setCostTime(Long costTime) {        this.costTime = costTime;    }    @Override    public String toString() {        return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)            .append("operateId", getOperateId())            .append("title", getTitle())            .append("businessType", getBusinessType())            .append("businessTypes", getBusinessTypes())            .append("method", getMethod())            .append("requestMethod", getRequestMethod())            .append("operatorType", getOperatorType())            .append("operateName", getOperateName())            .append("operateUrl", getOperateUrl())            .append("operateIp", getOperateIp())            .append("operateLocation", getOperateLocation())            .append("operateParam", getOperateParam())            .append("status", getStatus())            .append("errorMsg", getErrorMsg())            .append("operateTime", getOperateTime())            .append("costTime", getCostTime())            .toString();    }}
OperateLog1.3 根據(jù)自己項目編寫OperateMapper和OperateMapper.xml
public interface OperateLogMapper {    /**     * 新增操作日志     * @param :operateLog 操作日志對象     */    public void insertOperateLog(OperateLog operateLog);    /**     * 查詢系統(tǒng)操作日志集合     * @param :operateLog 操作日志對象     * @return 操作日志集合     */    public List selectOperateLogList(OperateLog operateLog);    /**     * 批量刪除系統(tǒng)操作日志     * @param ids 需要刪除的數(shù)據(jù)     * @return 結(jié)果     */    public int deleteOperateLogByIds(String[] ids);    /**     * 查詢操作日志詳細     * @param :operateId 操作ID     * @return 操作日志對象     */    public OperateLog selectOperateLogById(Long operateId);    /**     * 清空操作日志     */    public void cleanOperateLog();}                                                                                                                                                    select operate_id, title, business_type, method, request_method, operator_type, operate_name, operate_url, operate_ip, operate_location, operate_param, json_result, status, error_msg, operate_time, cost_time        from wom_plus.wo_operate_log                  insert into wom_plus.wo_operate_log(title, business_type, method, request_method, operator_type, operate_name, operate_url, operate_ip, operate_location, operate_param, json_result, status, error_msg, cost_time, operate_time)        values (#{title}, #{businessType}, #{method}, #{requestMethod}, #{operatorType}, #{operateName}, #{operateUrl}, #{operateIp}, #{operateLocation}, #{operateParam}, #{jsonResult}, #{status}, #{errorMsg}, #{costTime}, sysdate())                   delete from wom_plus.wo_operate_log where operate_id in                    #{operateId}                                truncate table wo_operate_log    
Mapper接口及其SQL1.4 根據(jù)自己項目編寫IOperateService和OperateServiceImpl
public interface IOperateLogService {    /**     * 新增操作日志     *     * @param :operateLog 操作日志對象     */    public void insertOperateLog(OperateLog operateLog);    /**     * 查詢系統(tǒng)操作日志集合     *     * @param :operateLog 操作日志對象     * @return 操作日志集合     */    public List selectOperateLogList(OperateLog operateLog);    /**     * 批量刪除系統(tǒng)操作日志     *     * @param ids 需要刪除的數(shù)據(jù)     * @return 結(jié)果     */    public int deleteOperateLogByIds(String ids);    /**     * 查詢操作日志詳細     *     * @param operateId 操作ID     * @return 操作日志對象     */    public OperateLog selectOperateLogById(Long operateId);    /**     * 清空操作日志     */    public void cleanOperateLog();}@Servicepublic class OperateLogServiceImpl implements IOperateLogService {    @Autowired(required = false)    private OperateLogMapper operateLogMapper;    @Override    public void insertOperateLog(OperateLog operateLog) {        operateLogMapper.insertOperateLog(operateLog);    }    @Override    public List selectOperateLogList(OperateLog operateLog) {        return operateLogMapper.selectOperateLogList(operateLog);    }    @Override    public int deleteOperateLogByIds(String ids) {        return deleteOperateLogByIds(ids);    }    @Override    public OperateLog selectOperateLogById(Long operateId) {        return operateLogMapper.selectOperateLogById(operateId);    }    @Override    public void cleanOperateLog() {        operateLogMapper.cleanOperateLog();    }}
IoperateService及其實現(xiàn)類1.5 Controller方法上的@Log
@Log(title = "用戶管理", businessType = BusinessType.EXPORT)@PostMapping("/export")@ResponseBodypublic AjaxResult export(@RequestParam(value = "name", required = false) String username){    List list = userDetailsService.getUserListByUsername(username);    ExcelUtil util = new ExcelUtil<>(SysUser.class);    return util.exportExcel(list, "用戶數(shù)據(jù)");}
2.自定義系統(tǒng)日志記載注解及其切面實現(xiàn)

java中注解與AOP的結(jié)合,方便了廣大java程序員對應(yīng)用的開發(fā),能夠?qū)υ蟹椒ǖ脑鰪姕p少很多代碼,原來的我們?nèi)绻诿總€方法上面進行日志記載,那么需要每個方法都調(diào)用日志記載的方法,而現(xiàn)在我們只需要在需要日志加載的方法上面加上@ Log就完美快速簡單地解決了上述繁雜問題。

2.1 自定義@Log
/** * 自定義操作日志記錄注解 *  * @author ruoyi * */@Target({ ElementType.PARAMETER, ElementType.METHOD })//作用于方法和參數(shù)上面@Retention(RetentionPolicy.RUNTIME)@Documentedpublic @interface Log{    /**     * 模塊     */    public String title() default "";    /**     * 功能     */    public BusinessType businessType() default BusinessType.OTHER;    /**     * 操作人類別     */    public OperatorType operatorType() default OperatorType.MANAGE;    /**     * 是否保存請求的參數(shù)     */    public boolean isSaveRequestData() default true;    /**     * 是否保存響應(yīng)的參數(shù)     */    public boolean isSaveResponseData() default true;    /**     * 排除指定的請求參數(shù)     */    public String[] excludeParamNames() default {};}
@Log2.2 LogAspect(重點)

該類就是根據(jù)動態(tài)代理來實現(xiàn)的,在Spring中稱之為AOP,面向切面編程,可以很好地實現(xiàn)對軟件中已有方法在安全、日志、監(jiān)控等方面的增強。

@Component@Aspect/** * 操作日志記錄處理 */public class LogAspect {    private static final Logger log = LoggerFactory.getLogger(LogAspect.class);    /** 排除敏感屬性字段 */    public static final String[] EXCLUDE_PROPERTIES = { "password", "oldPassword", "newPassword", "confirmPassword" };    /** 計算操作消耗時間 */    private static final ThreadLocal TIME_THREADLOCAL = new NamedThreadLocal("Cost Time");    /**     * 處理請求前執(zhí)行     */    @Before(value = "@annotation(controllerLog)")    //該方法傳入一個注解類型參數(shù),改參數(shù)被@Before注解中的@annotation作用,    // 表示只要該注解作用在哪個方法上,就在該方法上生效    public void before(JoinPoint joinPoint, Log controllerLog){        TIME_THREADLOCAL.set(System.currentTimeMillis());    }    /**     * 處理完請求后執(zhí)行     * @param joinPoint 切點     */    @AfterReturning(pointcut = "@annotation(controllerLog)", returning = "jsonResult")    public void doAfterReturning(JoinPoint joinPoint, Log controllerLog, Object jsonResult)    {        handleLog(joinPoint, controllerLog, null, jsonResult);    }    /**     * 攔截異常操作     * @param joinPoint 切點     * @param e 異常     */    @AfterThrowing(value = "@annotation(controllerLog)", throwing = "e")    public void doAfterThrowing(JoinPoint joinPoint, Log controllerLog, Exception e)    {        handleLog(joinPoint, controllerLog, e, null);    }    protected void handleLog(final JoinPoint joinPoint, Log controllerLog,                             final Exception e, Object jsonResult){        try        {            //本項目沒有使用shiro框架,所以無法根據(jù)登錄獲取用戶信息,以后再完善//            // 獲取當(dāng)前的用戶//            SysUser currentUser = ShiroUtils.getSysUser();            // *========數(shù)據(jù)庫日志=========*//            OperateLog operateLog = new OperateLog();            //ordinal可以返回當(dāng)前枚舉所在的序列,利用這個函數(shù),可以自增長的獲取我們定義的Excel的cell位置,            // 然后進行寫入數(shù)據(jù)操作            operateLog.setStatus(BusinessStatus.SUCCESS.ordinal());            // 請求的地址//            String ip = ShiroUtils.getIp();//這里暫時不使用Shiro相關(guān)類            String ip = IPUtils.getIpAddr(ServletUtils.getRequest());            operateLog.setOperateIp(ip);            operateLog.setOperateUrl(StringUtils.substring(ServletUtils.getRequest().getRequestURI(), 0, 255));            //后面完善//            if (currentUser != null)//            {//                operateLog.setOperateName(currentUser.getUsername());//            }            operateLog.setOperateName("xiaoku");            if (e != null)            {                operateLog.setStatus(BusinessStatus.FAIL.ordinal());                operateLog.setErrorMsg(StringUtils.substring(e.getMessage(), 0, 2000));            }            // 設(shè)置方法名稱            String className = joinPoint.getTarget().getClass().getName();            String methodName = joinPoint.getSignature().getName();            operateLog.setMethod(className + "." + methodName + "()");            // 設(shè)置請求方式            operateLog.setRequestMethod(ServletUtils.getRequest().getMethod());            // 處理設(shè)置注解上的參數(shù)            getControllerMethodDescription(joinPoint, controllerLog, operateLog, jsonResult);            // 設(shè)置消耗時間            operateLog.setCostTime(System.currentTimeMillis() - TIME_THREADLOCAL.get());            //我這里不使用異步任務(wù),因此在這里執(zhí)行插入            //這里通過SpringUtils.getBean(Classclz)來獲取所需對象            // 遠程查詢操作地點            operateLog.setOperateLocation(AddressUtils.getRealAddressByIP(operateLog.getOperateIp()));            SpringUtils.getBean(IOperateLogService.class).insertOperateLog(operateLog);            //暫時不需要//            // 保存數(shù)據(jù)庫//            AsyncManager.me().execute(AsyncFactory.recordOper(operLog));        }        catch (Exception exp)        {            // 記錄本地異常日志            log.error("異常信息:{}", exp.getMessage());            exp.printStackTrace();        }        finally        {            TIME_THREADLOCAL.remove();        }    }    /**     * 獲取注解中對方法的描述信息 用于Controller層注解     * @param log 日志     * @param :operateLog 操作日志     * @throws Exception     */    public void getControllerMethodDescription(JoinPoint joinPoint, Log log, OperateLog operLog, Object jsonResult) throws Exception    {        // 設(shè)置action動作        operLog.setBusinessType(log.businessType().ordinal());        // 設(shè)置標題        operLog.setTitle(log.title());        // 設(shè)置操作人類別        operLog.setOperatorType(log.operatorType().ordinal());        // 是否需要保存request,參數(shù)和值        if (log.isSaveRequestData())        {            // 獲取參數(shù)的信息,傳入到數(shù)據(jù)庫中。            setRequestValue(joinPoint, operLog, log.excludeParamNames());        }        // 是否需要保存response,參數(shù)和值        if (log.isSaveResponseData() && StringUtils.isNotNull(jsonResult))        {            operLog.setJsonResult(StringUtils.substring(JSONObject.toJSONString(jsonResult), 0, 2000));        }    }    /**     * 獲取請求的參數(shù),放到log中     *     * @param : operateLog     * @param : request     */    private void setRequestValue(JoinPoint joinPoint, OperateLog operLog, String[] excludeParamNames)    {        Map map = ServletUtils.getRequest().getParameterMap();        if (StringUtils.isNotEmpty(map))        {            String params = JSONObject.toJSONString(map, excludePropertyPreFilter(excludeParamNames));            operLog.setOperateParam(StringUtils.substring(params, 0, 2000));        }        else        {            Object args = joinPoint.getArgs();            if (StringUtils.isNotNull(args))            {                String params = argsArrayToString(joinPoint.getArgs(), excludeParamNames);                operLog.setOperateParam(StringUtils.substring(params, 0, 2000));            }        }    }    /**     * 忽略敏感屬性     */    public PropertyPreFilters.MySimplePropertyPreFilter excludePropertyPreFilter(String[] excludeParamNames)    {        return new PropertyPreFilters().addFilter().addExcludes(ArrayUtils.addAll(EXCLUDE_PROPERTIES, excludeParamNames));    }    /**     * 參數(shù)拼裝     */    private String argsArrayToString(Object[] paramsArray, String[] excludeParamNames)    {        String params = "";        if (paramsArray != null && paramsArray.length > 0)        {            for (Object o : paramsArray)            {                if (StringUtils.isNotNull(o) && !isFilterObject(o))                {                    try                    {                        Object jsonObj = JSONObject.toJSONString(o, excludePropertyPreFilter(excludeParamNames));                        params += jsonObj.toString() + " ";                    }                    catch (Exception e)                    {                    }                }            }        }        return params.trim();    }    /**     * 判斷是否需要過濾的對象。     * @param o 對象信息。     * @return 如果是需要過濾的對象,則返回true;否則返回false。     */    @SuppressWarnings("rawtypes")    public boolean isFilterObject(final Object o)    {        Class clazz = o.getClass();        if (clazz.isArray())        {            return clazz.getComponentType().isAssignableFrom(MultipartFile.class);        }        else if (Collection.class.isAssignableFrom(clazz))        {            Collection collection = (Collection) o;            for (Object value : collection)            {                return value instanceof MultipartFile;            }        }        else if (Map.class.isAssignableFrom(clazz))        {            Map map = (Map) o;            for (Object value : map.entrySet())            {                Map.Entry entry = (Map.Entry) value;                return entry.getValue() instanceof MultipartFile;            }        }        return o instanceof MultipartFile || o instanceof HttpServletRequest || o instanceof HttpServletResponse                || o instanceof BindingResult;    }}
LogAspect

上述代碼中的前置通知@Before和后置通知@After是用來記錄標注有@Log注解的方法運行所花費的時間,環(huán)繞返回注解@AroundReturning是返回系統(tǒng)日志記載信息,環(huán)繞異常注解@AroundThrowing是返回這部分代碼出現(xiàn)異常是返回的異常信息。主要的日志系統(tǒng)信息實現(xiàn)是在handleLog()方法中,根據(jù)切點獲取方法名稱、請求參數(shù)、等等信息。

2.3 若依中的自定義工具類3.項目運行結(jié)果

第一張圖的運行結(jié)果是使用@Log注解標注在以Excel形式導(dǎo)出數(shù)據(jù)的export()方法上面,第二張圖片是系統(tǒng)日志記載表wo_operate_log記載的執(zhí)行標有@Log注解的方法的日志記載信息。我這里只截取了后面的內(nèi)容。

關(guān)鍵詞:

上一篇:

下一篇:

相關(guān)推薦

媒體焦點