使用AOP給springboot項目添加日誌

AOP(Aspect Oriented Programming),即面向切面編程,可以說是OOP(Object Oriented Programming,面向對象編程)的補充和完善。OOP允許開發者定義縱向的關係,但並不適合定義橫向的關係,例如日誌功能。一般來說,我們項目中的日誌幾乎會遍佈所有controller,不利於統一管理,這種散佈在各處的無關的代碼被稱爲橫切(cross cutting),在OOP設計中,它導致了大量代碼的重複,而不利於各個模塊的重用。

使用”橫切”技術,AOP把軟件系統分爲兩個部分:核心關注點和橫切關注點。業務處理的主要流程是核心關注點,與之關係不大的部分是橫切關注點。橫切關注點的一個特點是,他們經常發生在覈心關注點的多處,而各處基本相似,比如權限認證、日誌、事務。AOP的作用在於分離系統中的各種關注點,將核心關注點和橫切關注點分離開來。

接下來,我們看一下如何使用AOP給springboot項目添加日誌:

一.引入AOP的依賴包

<dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-aop</artifactId>
</dependency>

二.添加AOP的全局類

@Aspect
@Component
public class WebLogAspect {

    private Logger logger = LoggerFactory.getLogger(getClass());

    @Autowired
    private LogService logService;

    @Autowired
    private UserService userService;

    private String url;

    private String ip;

    private Integer port;

    private User user;

    @Pointcut("execution(public * com.ytint.bb6.controller..*.*(..))")
    public void webLog() {
    }

    @Before("webLog()")
    public void doBefore(JoinPoint joinPoint) {
        ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
        HttpServletRequest request = attributes.getRequest();
        // 記錄請求內容,這裏只記錄非GET請求的日誌信息
        if (!request.getMethod().equals("GET")) {
            logger.info("請求URL: " + request.getRequestURL().toString());
            url = request.getRequestURI();
            ip = request.getRemoteHost();
            port = request.getRemotePort();
            user = userService.getCurrentUser();
        }
    }

    @AfterReturning(returning = "ret", pointcut = "webLog()")
    public void doAfterReturning(Object ret) throws Throwable {
        // 處理完請求,返回內容
        Integer logType = LogType.OPERATION.getId();
        if (ret instanceof ResultInfo) {
            ResultInfo result = (ResultInfo) ret;
            String msg = result.getMsg();
            String module = result.getModule();
            Integer code = result.getCode();
            if (UtilValidate.isNotEmpty(msg)) {
                logger.info(msg);
                // 操作模塊返回200才存儲到日誌表
                if (code.equals(200)) {
                    logService.save(user, ip, port, msg, logType, module, url);
                }
            }
        }

    }

    @AfterThrowing(pointcut = "webLog()", throwing = "e")
    public void doException(JoinPoint jp, Throwable e) {
        if (e != null) {
            Logger logger = LoggerFactory.getLogger(jp.getSignature().getClass());
            logger.error("程序發生了異常:" + e.getMessage(), e);
        }
    }
}

Spring使用AspectJ註解來聲明通知方法

@After:會在目標方法返回或拋出異常後調用

@AfterReturning:會在目標方法返回後調用

@AfterThrowing:會在目標方法拋出異常後調用

@Around:會將目標方法封裝起來

@Before:會在目標方法調用之前執行

使用@Pointcut定義切面

@Pointcut後面括號裏的內容是切面表達式:

@Pointcut("execution(public * com.yt.controller..*.*(..))")

表達式含義如下:

1) execution(): 表達式主體;

2) public *:表示返回類型, *號表示所有的類型;

3) 包名:表示需要攔截的包名,後面的兩個句點表示當前包和當前包的所有子包,com.yt.controller包、子孫包下所有類的方法;

4) 第二個*號:表示類名,*號表示所有的類;

5) *(..):表示方法名,*號表示所有的方法,括號裏面表示方法的參數,..表示任何參數;

webLog()方法定義了一個可重用的切點,這樣我們就不必把同樣的切點表達式傻傻的寫好多遍了,只要在每次需要的時候引用它就可以了,webLog方法的實際內容並不重要,他本身只是一個標識,供@Pointcut註解依附。

在日誌中添加模塊信息

如果我們不只想獲取當前方法的URL,參數等基本信息,還想獲取它來源於某一個controller模塊該怎麼辦呢,我這裏採用了一個有點傻的方法,可以在返回的結果對象中添加一個模塊字段module,把對應的controller信息放在模塊裏,這樣輸出日誌時就可以知道當前請求是來源於哪個controller了。如果大家有更好的方式,歡迎交流~

本篇博客部分參考了以下兩篇博客,送上花花~~~

https://www.cnblogs.com/hongwz/p/5764917.html
https://www.cnblogs.com/30go/p/8443522.html

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章