springboot 中使用Aspect實現切面Log日誌

解釋

AOP通常意思是: 面向切面編程,通過預編譯方式和運行期動態代理實現程序功能的統一維護的一種技術.AOP是OOP的延續,是軟件開發中的一個熱點,也是Spring框架中的一個重要內容,是函數式編程的一種衍生範型。利用AOP可以對業務邏輯的各個部分進行隔離,從而使得業務邏輯各部分之間的耦合度降低,提高程序的可重用性,同時提高了開發的效率。
常用的業務環境: 配置事務、日誌、權限驗證、用戶請求時做一些特殊處理
註解解釋:

  • @Target:用於描述註解的使用範圍(即:被描述的註解可以用在什麼地方)(元註解);
    取值(ElementType)有

    作用
    CONSTRUCTOR 用於描述構造器
    FIELD 用於描述域
    LOCAL_VARIABLE 用於描述局部變量
    METHOD 用於描述方法
    PACKAGE 用於描述包
    PARAMETER 用於描述參數
    TYPE 用於描述類、接口(包括註解類型) 或enum聲明
  • @Retention:表示需要在什麼級別保存該註釋信息,用於描述註解的生命週期(即:被描述的註解在什麼範圍內有效)(元註解)
    取值(RetentionPoicy)有:

    鍵名 作用
    SOURCE 在源文件中有效(即源文件保留)
    CLASS 在class文件中有效(即class保留)
    RUNTIME 在運行時有效(即運行時保留)

    **注意:**RetentionPolicy的屬性值是RUTIME,這樣註解處理器可以通過反射,獲取到該註解的屬性值,從而去做一些運行時的邏輯處理

  • @Aspect:作用是把當前類標識爲一個切面供容器讀取

  • @Pointcut:Pointcut是植入Advice的觸發條件。每個Pointcut的定義包括2部分,一是表達式,二是方法簽名。方法簽名必須是 public及void型。可以將Pointcut中的方法看作是一個被Advice引用的助記符,因爲表達式不直觀,因此我們可以通過方法簽名的方式爲 此表達式命名。因此Pointcut中的方法只需要方法簽名,而不需要在方法體內編寫實際代碼。

  • @Around:環繞增強,相當於MethodInterceptor

  • @AfterReturning:後置增強,相當於AfterReturningAdvice,方法正常退出時執行

  • @Before:標識一個前置增強方法,相當於BeforeAdvice的功能,相似功能的還有

  • @AfterThrowing:異常拋出增強,相當於ThrowsAdvice

  • @Component 註解 把切面類加入到IOC容器中

  • @Aspect 註解 使之成爲切面類

代碼實現切面用戶操作Log日誌

1. 日誌實體類和service

實體:採用lombok的方式

@Data
public class SysLog  implements Serializable {
	private static final long serialVersionUID = -6309732882044872298L;
	@TableId(value="id", type = IdType.ID_WORKER)
	private Integer id;
	private String userName;
	private String operation;
	private Integer time;
	private String method;
	private String params;
	private String ip;
	private Date createTime;
}

service接口和實現類(dao層再次就不貼出來了,該項目使用了mybatisplus非常簡單):

//接口
public interface ISysLogService extends IService<SysLog> {
}
//實現類
@Service
public class SysLogServiceImpl extends ServiceImpl<SysLogMapper, SysLog> implements ISysLogService {
}

2. 定義一個方法級別的Log

@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface Log {
	String value() default "";
}

3. 聲名切面,完成日誌保存

@Aspect
@Component
public class LogAspect {
	@Autowired
	ISysLogService iSysLogService;

	@Pointcut("@annotation(com.zhongsy.study.common.Log)")
	public void pointcut() { }
	@Around("pointcut()")
	public Object around(ProceedingJoinPoint point) {
		Object result = null;
		long beginTime = System.currentTimeMillis();
		try {
			// 執行方法
			result = point.proceed();
		} catch (Throwable e) {
			e.printStackTrace();
		}
		// 執行時長(毫秒)
		long time = System.currentTimeMillis() - beginTime;
		// 保存日誌
		saveLog(point, time);
		return result;
	}
	private void saveLog(ProceedingJoinPoint joinPoint, long time) {
		MethodSignature signature = (MethodSignature) joinPoint.getSignature();
		Method method = signature.getMethod();
		SysLog sysLog = new SysLog();
		Log logAnnotation = method.getAnnotation(Log.class);
		if (logAnnotation != null) {
			// 註解上的描述
			sysLog.setOperation(logAnnotation.value());
		}
		// 請求的方法名
		String className = joinPoint.getTarget().getClass().getName();
		String methodName = signature.getName();
		sysLog.setMethod(className + "." + methodName + "()");
		// 請求的方法參數值
		Object[] args = joinPoint.getArgs();
		// 請求的方法參數名稱
		LocalVariableTableParameterNameDiscoverer u = new LocalVariableTableParameterNameDiscoverer();
		String[] paramNames = u.getParameterNames(method);
		if (args != null && paramNames != null) {
			String params = "";
			for (int i = 0; i < args.length; i++) {
				params += "  " + paramNames[i] + ": " + args[i];
			}
			sysLog.setParams(params);
		}
		// 獲取request
//		HttpServletRequest request = HttpContextUtils.getHttpServletRequest();
		// 設置IP地址
//		sysLog.setIp(IPUtils.getIpAddr(request));
		sysLog.setIp("127.0.0.1");
		// 模擬一個用戶名
		sysLog.setUserName("mrbird");
		sysLog.setTime((int) time);
		sysLog.setCreateTime(new Date());
		// 保存系統日誌
		iSysLogService.save(sysLog);
	}
}

注意: 因爲我這裏是測試,因此獲取ip的方法我沒有編寫,具體採用的時候需要具體的編寫。

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