天天看點

SpringBoot2.0 基礎案例(11):配置AOP切面程式設計,解決日志記錄業務一、AOP切面程式設計二、與SpringBoot2.0整合三、源代碼位址

一、AOP切面程式設計

1、什麼是AOP程式設計

在軟體業,AOP為Aspect Oriented Programming的縮寫,意為:面向切面程式設計,通過預編譯方式和運作期動态代理實作程式功能的統一維護的一種技術。AOP是OOP(面向對象程式設計)的延續,是軟體開發中的一個熱點,也是Spring架構中的一個重要内容,是函數式程式設計的一種衍生範型。利用AOP可以對業務邏輯的各個部分進行隔離,進而使得業務邏輯各部分之間的耦合度降低,提高程式的可重用性,同時提高了開發的效率。

2、AOP程式設計特點

1)AOP采取橫向抽取機制,取代了傳統縱向繼承體系重複性代碼
2)經典應用:事務管理、性能監視、安全檢查、緩存 、日志等
3)aop底層将采用代理機制進行實作
4)接口 + 實作類 :spring采用 jdk 的動态代理Proxy
5)實作類:spring 采用 cglib位元組碼增強           

3、AOP中術語和圖解

1)target:目标類
   需要被代理的類。例如:UserService
2)Joinpoint:連接配接點
   所謂連接配接點是指那些可能被攔截到的方法。例如:所有的方法
3)PointCut:切入點
   已經被增強的連接配接點。例如:addUser()
4)advice:通知/增強
   增強代碼。例如:after、before
5)Weaving:織入
   指把增強advice應用到目标對象target來建立新的代理對象proxy的過程.
6)proxy 代理類
7) Aspect(切面): 是切入點pointcut和通知advice的結合
    一個線是一個特殊的面。
    一個切入點和一個通知,組成成一個特殊的面。           
SpringBoot2.0 基礎案例(11):配置AOP切面程式設計,解決日志記錄業務一、AOP切面程式設計二、與SpringBoot2.0整合三、源代碼位址

二、與SpringBoot2.0整合

1、核心依賴

<!-- AOP依賴 -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
</dependency>           

2、編寫日志記錄注解

package com.boot.aop.config;
import java.lang.annotation.*;
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface LogFilter {
    String value() default "" ;
}           

3、編寫日志記錄的切面代碼

這裡分為兩種情況處理,一種正常的請求日志,和系統異常的錯誤日志。

核心注解兩個。@Aspect和@Component。

package com.boot.aop.config;
import com.alibaba.fastjson.JSONObject;
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.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;

@Aspect
@Component
public class LogAspect {

    private static final Logger LOGGER = LoggerFactory.getLogger(LogAspect.class) ;

    @Pointcut("@annotation(com.boot.aop.config.LogFilter)")
    public void logPointCut (){

    }
    @Around("logPointCut()")
    public Object around (ProceedingJoinPoint point) throws Throwable {
        Object result = null ;
        try{
            // 執行方法
            result = point.proceed();
            // 儲存請求日志
            saveRequestLog(point);
        } catch (Exception e){
            // 儲存異常日志
            saveExceptionLog(point,e.getMessage());
        }
        return result;
    }
    private void saveExceptionLog (ProceedingJoinPoint point,String exeMsg){
        LOGGER.info("捕獲異常:"+exeMsg);
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        LOGGER.info("請求路徑:"+request.getRequestURL());
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        LOGGER.info("請求方法:"+method.getName());
        // 擷取方法上LogFilter注解
        LogFilter logFilter = method.getAnnotation(LogFilter.class);
        String value = logFilter.value() ;
        LOGGER.info("子產品描述:"+value);
        Object[] args = point.getArgs();
        LOGGER.info("請求參數:"+ JSONObject.toJSONString(args));
    }
    private void saveRequestLog (ProceedingJoinPoint point){
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
        LOGGER.info("請求路徑:"+request.getRequestURL());
        MethodSignature signature = (MethodSignature) point.getSignature();
        Method method = signature.getMethod();
        LOGGER.info("請求方法:"+method.getName());
        // 擷取方法上LogFilter注解
        LogFilter logFilter = method.getAnnotation(LogFilter.class);
        String value = logFilter.value() ;
        LOGGER.info("子產品描述:"+value);
        Object[] args = point.getArgs();
        LOGGER.info("請求參數:"+ JSONObject.toJSONString(args));
    }
}           

4、請求日志測試

@LogFilter("儲存請求日志")
@RequestMapping("/saveRequestLog")
public String saveRequestLog (@RequestParam("name") String name){
    return "success:"+name ;
}           

切面類資訊列印

/**
 * 請求路徑:http://localhost:8011/saveRequestLog
 * 請求方法:saveRequestLog
 * 子產品描述:儲存請求日志
 * 請求參數:["cicada"]
 */           

5、異常日志測試

@LogFilter("儲存異常日志")
@RequestMapping("/saveExceptionLog")
public String saveExceptionLog (@RequestParam("name") String name){
    int error = 100 / 0 ;
    System.out.println(error);
    return "success:"+name ;
}           
/**
 * 捕獲異常:/ by zero
 * 請求路徑:http://localhost:8011/saveExceptionLog
 * 請求方法:saveExceptionLog
 * 子產品描述:儲存異常日志
 * 請求參數:["cicada"]
 */           

三、源代碼位址

GitHub位址:知了一笑
https://github.com/cicadasmile
碼雲位址:知了一笑
https://gitee.com/cicadasmile           
SpringBoot2.0 基礎案例(11):配置AOP切面程式設計,解決日志記錄業務一、AOP切面程式設計二、與SpringBoot2.0整合三、源代碼位址