1. 程式人生 > >Spring AOP實現執行緒安全的日誌輸出功能

Spring AOP實現執行緒安全的日誌輸出功能

在我們的工作中很多的時候都需要對介面的輸入和輸出進行監管,這樣可以方便我們對介面的除錯,也方面我們專案後期的維護。
那麼我們怎麼使用spring進行介面的輸入和輸出?
在之前我們可以直接用spring的攔截器來實現
問題:
1. 攔截器可以輕鬆抓取介面的引數,不能抓取介面的結果
2. 攔截器需要進行配置,不能實現靈活多變的使用
為什麼使用aop:
1.aop的底層是使用的代理模式,我們不經可以得到介面的引數還能得到介面的結果
2.aop的攔截比較靈活,更方便我們對日誌的管理
那麼我們aop是如何實現的呢?
1.首先我們需要定義一個介面

該介面主要定義一個輸入攔截方法和一個輸出攔截放
如:

 package com.oa.common.proxy;

import java.lang.reflect.Method;

/**
 * @name 日誌輸出類,保證執行緒安全
 * @author Yang
 * @date 2018-08-07
 * @version 1.0.2
 */
public interface LoggerInterceptorHandler {

    /**
     * 日誌輸入的實現類
     */
    static String LOGGER_ADDRESS="interceptor.address"
; /** * 日誌攔截前的處理 * * @param object * 當前的物件 * * @param method * 當前的方法 * * @param args * 引數 * * @throws Exception */ void interptBefore(Object object, Method method,Object args) throws Exception; /** * 日誌攔截後的方法 * * @param
object * 當前物件 * * @param method * 當前的方法 * * @param args * 引數 * * @param result * 返回的結果:可以為空 * */
void interceptPost(Object object,Method method,Object args,Object result); }

2.定義一個攔截器的註解
程式碼如:

package com.oa.common.annotion;

import java.lang.annotation.*;

/**
* @name 最新執行緒安全的攔截器
* @author Yang
* @date 2018-08-07
* @version 1.0.2
*
*/
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LoggerInterceptor {
}

**3.呼叫spring Aop的相關方法** 實現方法攔截的功能
***重點**程式碼如:
package com.oa.common.proxy;

import com.oa.common.annotion.LoggerInterceptor;
import com.oa.common.config.ConfigProperties;
import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.LinkedHashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.atomic.AtomicReference;

/**
 * @name  實現日誌輸出的代理類
 * @author Yang
 *
 */
@Aspect
@Component
public class CglibInterceptorProxy implements MethodInterceptor {


    private AtomicReference<LoggerInterceptorHandler> handlerAtomicReference= new AtomicReference<>();

    @Pointcut("@annotation(org.springframework.web.bind.annotation.ResponseBody)")
    public void loginauto(){

    }

    @Around("loginauto()")
    public Object around(ProceedingJoinPoint point){
        Object result= null;
        //獲取訪問的名稱
        String methodName= point.getSignature().getName();
        Class[] methods= ((MethodSignature)point.getSignature()).getParameterTypes();
        Object target= point.getTarget();
        Class<?> clazz= target.getClass();
        try {
            Method method=clazz.getMethod(methodName,methods);
            //判斷方法是否使用 LoggerInterceptor 註解
            if(!isContainer(method)){
                return point.proceed();
            }
            LoggerInterceptorHandler interceptorHandler= this.handlerAtomicReference.get();
            //判斷是否載入了日誌輸出類
            if (interceptorHandler==null){
                //初始化日誌輸出類
                String path=ConfigProperties.getValue(LoggerInterceptorHandler.LOGGER_ADDRESS);
                interceptorHandler= new ClassRefletInterceptHandlerFactory(path).instance();
                this.handlerAtomicReference.set(interceptorHandler);
            }
            RequestAttributes requestAttribute= RequestContextHolder.currentRequestAttributes();
            HttpServletRequest request= ((ServletRequestAttributes)requestAttribute).getRequest();
            Set<Object> allParams = new LinkedHashSet<>();
            //獲取query string 或 posted form data引數
            Map<String, String[]> paramMap = request.getParameterMap();
            Map<String,String> map= new HashMap<>();
            for(Map.Entry<String,String[]> entry :paramMap.entrySet()){
                map.put(entry.getKey(),entry.getValue()[0]);
            }
            if(map.size() > 0){
                allParams.add(map);
            }
            //輸出開始日誌
            interceptorHandler.interptBefore(target,method,allParams);
            //執行方法
            result= point.proceed();
            //輸出結果日誌
            interceptorHandler.interceptPost(target,method,allParams,result);
        } catch (Throwable throwable) {
            throwable.printStackTrace();
        }
        return result;
    }

    /**
     * 判斷是否有日誌攔截器
     *
     * @param method
     * 當前方法
     * @return
     */
    private boolean isContainer(Method method){
        LoggerInterceptor interceptor= method.getAnnotation(LoggerInterceptor.class);
        return interceptor!=null;
    }

    @Override
    public Object invoke(MethodInvocation invocation) throws Throwable {
        return null;
    }
}

4.最後一步寫輸出日誌的實現類

package com.villager.proxy;

import com.alibaba.fastjson.JSONObject;
import com.oa.common.proxy.LoggerInterceptorHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.Method;

public class InterceptorHandler implements LoggerInterceptorHandler {

    private static final Logger logger= LoggerFactory.getLogger(InterceptorHandler.class);


    private String wrapJson(Object obj, int maxLength) {
        if (obj == null) {
            return "";
        }
        String json=JSONObject.toJSONString(obj);
        if (json == null) {
            return "";
        }
        if (json.length() > maxLength) {
            json = json.substring(0, maxLength - 1);
        }
        return json;
    }


    @Override
    public void interptBefore(Object object, Method method, Object args) throws Exception {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("\n當前物件類class={" + object.getClass() + "},呼叫方法method={" + method.getName() + "}");
        stringBuilder.append("-->{");
        stringBuilder.append("引數:"+this.wrapJson(args,400));
        stringBuilder.append("\n}");
        logger.warn(stringBuilder.toString());
        stringBuilder.setLength(0);
    }

    @Override
    public void interceptPost(Object object, Method method, Object args, Object result) {
        StringBuilder stringBuilder = new StringBuilder();
        stringBuilder.append("\n當前物件類class={" + object.getClass() + "},呼叫方法method={" + method.getName() + "}");
        stringBuilder.append("-->{");
        stringBuilder.append("\n    結果:" + this.wrapJson(result, 400));
        stringBuilder.append("\n}");
        logger.warn(stringBuilder.toString());
        stringBuilder.setLength(0);
    }
}

備註:
1.最後一步可以根據自己的需求進行調整。
2.該方法支援log4j 和 logback 等常用的日誌開源工具
3.在配置檔案裡面需配置日誌輸出實現類的地址

相關推薦

Spring AOP實現執行安全日誌輸出功能

在我們的工作中很多的時候都需要對介面的輸入和輸出進行監管,這樣可以方便我們對介面的除錯,也方面我們專案後期的維護。 那麼我們怎麼使用spring進行介面的輸入和輸出? 在之前我們可以直接用spring的攔截器來實現 問題: 1.

Qt中實現執行安全的單例模式

之前專案中用到單例模式,用的是執行緒不安全的,這次專案用到了多執行緒,所以想到實現一個執行緒安全的單例模式。經過查詢資料,發現Qt本身有自己的執行緒安全單例模式實現方式。 Q_GLOBAL_STATIC巨集 使用方法:MonitorWindow.h #ifndef MONITORW

設計模式之單例模式【內附物件例項化幾種方式、實現執行安全幾種方式】

繼續來複習常用的設計模式-單例模式,順便回憶一下執行緒安全的幾種實現方式。 一、什麼是單例模式 單例模式,簡單常用的一種設計模式,也很好的體現了程式碼控制物件在記憶體數量的一種方式,主要分2種實現方式: ①餓漢式,執行緒安全 ②懶漢式,執行緒不安全(新增鎖機制,可以實現執行緒安全)

c/c++ 多執行 利用條件變數實現執行安全的佇列

多執行緒 利用條件變數實現執行緒安全的佇列 背景:標準STL庫的佇列queue是執行緒不安全的。 利用條件變數(Condition variable)簡單實現一個執行緒安全的佇列。 程式碼: #include <queue> #include <memory> #include

ThreadLocal實現執行安全

Spring通過各種模板類降低了開發者使用各種資料持久技術的難度。這些模板類都是執行緒安全的,也就是說,多個DAO可以複用同一個模板例項而不會發生衝突。我們使用模板類訪問底層資料,根據持久化技術的不同,模板類需要繫結資料連線或會話的資源。但這些資源本身是非執行緒安全的,也就是說它們不能在同一時刻被多

懶漢式單例--雙重檢測鎖實現執行安全

Football2.java /** * 懶漢式單例 * 用的時候再建立一個物件,執行緒不安全 * @author Administrator * */ class FootBall2 { private static int count; privat

python 實現執行安全的單例模式

單例模式是一種常見的設計模式,該模式的主要目的是確保某一個類只有一個例項存在。當你希望在整個系統中,某個類只能出現一個例項時,單例物件就能派上用場。 比如,伺服器的配置資訊寫在一個檔案中online.conf中,客戶端通過一個 Config 的類來讀取配置檔案的內容。如果在

自定義註解+Spring AOP實現記錄使用者操作日誌

一、背景     專案中需要對使用者的各種操作做詳細的操作日誌記錄,需要記錄使用者操作的操作模組、具體操作以及操作的資料記錄ID等。     若寫一個方法去儲存操作,則需要每次手動去呼叫。由於是非業務性的操作,並且大量的重複操作,Spring AOP就能很好的解決這個問題。

非同步程式設計學習之路(二)-通過Synchronize實現執行安全的多執行

本文是非同步程式設計學習之路(二)-通過Synchronize實現執行緒安全的多執行緒,若要關注前文,請點選傳送門: 非同步程式設計學習之路(一)-通過Thread實現簡單多執行緒(執行緒不安全) 上篇我們通過Thread實現了幾種執行緒不安全的多執行緒寫法,導致最後的結果與預期的值不一樣。

程式碼方式配置Log4j並實現執行日誌管理 第五部分

文章目錄 一 第三方程式不應僅限於會用 二 同時按日期和檔案大小備份 三 調整ThreadLogger使用複寫的Appender 四 簡單測試下 五 結語 一 第三方程式不應僅限於會用   關於日誌

程式碼方式配置Log4j並實現執行日誌管理 第四部分

文章目錄 一 非同步輸出模式 二 增加非同步輸出模式開關 三 重構日誌輸出介面 四 非同步處理執行緒 五 總結及一些其他的建議 一 非同步輸出模式   目前尚剩餘兩個需求,一個是實現日誌的非同步輸出

程式碼方式配置Log4j並實現執行日誌管理 第三部分

文章目錄 一 對第二部分的一些補充 二 如何實現執行緒級日誌物件管理 三 實現ThreadLogger 四 重構LogUtil 一 對第二部分的一些補充   第二部分用很簡單的樣例來描述了Logger物件的

程式碼方式配置Log4j並實現執行日誌管理 第二部分

文章目錄 一 設計類結構 二 成員設計 三 方法設計 四 實現Logger物件例項化方法 一 設計類結構   第一部分說了兩件事兒: 如何根據配置檔案分析有用的資訊以便對底層結構進行挖掘

程式碼方式配置Log4j並實現執行日誌管理 第一部分

文章目錄 一 為什麼寫略顯過時的東西 二 需求 三 程式設計能力如何提升 四 我是如何分析Log4j的 一 為什麼寫略顯過時的東西   講道理,現在還說Log4j有點過時了,因為自從Log4j2問世,全世界

Java多執行之—Synchronized方式和CAS方式實現執行安全效能對比

效能比較猜想 1.大膽假設 在設計試驗方法之前,針對Synchronized和CAS兩種方式的特點,我們先來思考一下兩種方式效率如何? 首先,我們在回顧一下兩種方式是如何保證執行緒安全的。Synchronized方式通過大家應該很熟悉,他的行為非常悲觀,只要有一個執行緒進

單例模式靜態內部類實現執行安全

模擬單例的類 package singleton.test; public class MyObject { private MyObject() { // TODO Auto-generated constructor stu

靜態內部類、靜態變數的載入次數-理解靜態內部類實現執行安全的單例模式

百度眾說紛紜的情況下就不如自己寫例子測試理論,話不多說,上程式碼: public class Sta { public static long date=System.currentTimeMillis();//1 public i

Go語言實現執行安全訪問佇列

這個例子用Go語言的包"container/list"實現一個執行緒安全訪問的佇列。其中不少細節耐人尋味,做出它則是花費了不少精力,找不到樣例啊! Go語言的許多優點自不必說,然而從C/C++程式角度看,語言的語法也好,程式的實現方式也好,看起來總有點怪怪的感覺。 在這個程

C++實現執行安全的佇列

  C++標準庫已經提供了std::queue這一佇列容器,但不是執行緒安全的。std::queue這個容器已經提供了pop(),push(),empty()等這些讀寫操作容器的函式,只要在這些函式上面加個鎖,就可以使其執行緒安全。   在C++原有容器上面進

C++實現執行安全的的雙緩衝例項

業務場景:多執行緒的業務系統不斷寫入資料到記憶體中,同步執行緒不斷讀取資料並同步redis 設計方案: 由於業務非常適合map的結構,因此採用了雙層map typedef std::map<int,int> EventCountMap; typedef st