溫馨提示×

您好,登錄后才能下訂單哦!

密碼登錄×
登錄注冊(cè)×
其他方式登錄
點(diǎn)擊 登錄注冊(cè) 即表示同意《億速云用戶(hù)服務(wù)條款》

Spring AOP如何實(shí)現(xiàn)簡(jiǎn)單的日志切面

發(fā)布時(shí)間:2021-10-27 09:20:45 來(lái)源:億速云 閱讀:133 作者:柒染 欄目:大數(shù)據(jù)

本篇文章給大家分享的是有關(guān)Spring AOP如何實(shí)現(xiàn)簡(jiǎn)單的日志切面,小編覺(jué)得挺實(shí)用的,因此分享給大家學(xué)習(xí),希望大家閱讀完這篇文章后可以有所收獲,話(huà)不多說(shuō),跟著小編一起來(lái)看看吧。

AOP

1.什么是 AOP ?

AOP 的全稱(chēng)為 Aspect Oriented Programming,譯為面向切面編程,是通過(guò)預(yù)編譯方式和運(yùn)行期動(dòng)態(tài)代理實(shí)現(xiàn)核心業(yè)務(wù)邏輯之外的橫切行為的統(tǒng)一維護(hù)的一種技術(shù)。AOP 是面向?qū)ο缶幊蹋∣OP)的補(bǔ)充和擴(kuò)展。 利用 AOP 可以對(duì)業(yè)務(wù)邏輯各部分進(jìn)行隔離,從而達(dá)到降低模塊之間的耦合度,并將那些影響多個(gè)類(lèi)的公共行為封裝到一個(gè)可重用模塊,從而到達(dá)提高程序的復(fù)用性,同時(shí)提高了開(kāi)發(fā)效率,提高了系統(tǒng)的可操作性和可維護(hù)性。

2.為什么要用 AOP ?

在實(shí)際的 Web 項(xiàng)目開(kāi)發(fā)中,我們常常需要對(duì)各個(gè)層面實(shí)現(xiàn)日志記錄,性能統(tǒng)計(jì),安全控制,事務(wù)處理,異常處理等等功能。如果我們對(duì)每個(gè)層面的每個(gè)類(lèi)都獨(dú)立編寫(xiě)這部分代碼,那久而久之代碼將變得很難維護(hù),所以我們把這些功能從業(yè)務(wù)邏輯代碼中分離出來(lái),聚合在一起維護(hù),而且我們能靈活地選擇何處需要使用這些代碼。

3.AOP 的核心概念

名詞概念理解
通知(Advice)攔截到連接點(diǎn)之后所要執(zhí)行的代碼,通知分為前置、后置、異常、最終、環(huán)繞通知五類(lèi)我們要實(shí)現(xiàn)的功能,如日志記錄,性能統(tǒng)計(jì),安全控制,事務(wù)處理,異常處理等等,說(shuō)明什么時(shí)候要干什么
連接點(diǎn)(Joint Point)被攔截到的點(diǎn),如被攔截的方法、對(duì)類(lèi)成員的訪(fǎng)問(wèn)以及異常處理程序塊的執(zhí)行等等,自身還能嵌套其他的 Joint PointSpring 允許你用通知的地方,方法有關(guān)的前前后后(包括拋出異常)
切入點(diǎn)(Pointcut)對(duì)連接點(diǎn)進(jìn)行攔截的定義指定通知到哪個(gè)方法,說(shuō)明在哪干
切面(Aspect)切面類(lèi)的定義,里面包含了切入點(diǎn)(Pointcut)和通知(Advice)的定義切面就是通知和切入點(diǎn)的結(jié)合
目標(biāo)對(duì)象(Target Object)切入點(diǎn)選擇的對(duì)象,也就是需要被通知的對(duì)象;由于 Spring AOP 通過(guò)代理模式實(shí)現(xiàn),所以該對(duì)象永遠(yuǎn)是被代理對(duì)象業(yè)務(wù)邏輯本身
織入(Weaving)把切面應(yīng)用到目標(biāo)對(duì)象從而創(chuàng)建出 AOP 代理對(duì)象的過(guò)程??椚肟梢栽诰幾g期、類(lèi)裝載期、運(yùn)行期進(jìn)行,而 Spring 采用在運(yùn)行期完成切點(diǎn)定義了哪些連接點(diǎn)會(huì)得到通知
引入(Introduction )可以在運(yùn)行期為類(lèi)動(dòng)態(tài)添加方法和字段,Spring 允許引入新的接口到所有目標(biāo)對(duì)象引入就是在一個(gè)接口/類(lèi)的基礎(chǔ)上引入新的接口增強(qiáng)功能
AOP 代理(AOP Proxy )Spring AOP 可以使用 JDK 動(dòng)態(tài)代理或者 CGLIB 代理,前者基于接口,后者基于類(lèi)通過(guò)代理來(lái)對(duì)目標(biāo)對(duì)象應(yīng)用切面

Spring AOP

1.簡(jiǎn)介

AOP 是 Spring 框架中的一個(gè)核心內(nèi)容。在 Spring 中,AOP 代理可以用 JDK 動(dòng)態(tài)代理或者 CGLIB 代理 CglibAopProxy 實(shí)現(xiàn)。Spring 中 AOP 代理由 Spring 的 IOC 容器負(fù)責(zé)生成和管理,其依賴(lài)關(guān)系也由 IOC 容器負(fù)責(zé)管理。

2.相關(guān)注解

注解說(shuō)明
@Aspect將一個(gè) java 類(lèi)定義為切面類(lèi)
@Pointcut定義一個(gè)切入點(diǎn),可以是一個(gè)規(guī)則表達(dá)式,比如下例中某個(gè) package 下的所有函數(shù),也可以是一個(gè)注解等
@Before在切入點(diǎn)開(kāi)始處切入內(nèi)容
@After在切入點(diǎn)結(jié)尾處切入內(nèi)容
@AfterReturning在切入點(diǎn) return 內(nèi)容之后處理邏輯
@Around在切入點(diǎn)前后切入內(nèi)容,并自己控制何時(shí)執(zhí)行切入點(diǎn)自身的內(nèi)容
@AfterThrowing用來(lái)處理當(dāng)切入內(nèi)容部分拋出異常之后的處理邏輯
@Order(100)AOP 切面執(zhí)行順序, @Before 數(shù)值越小越先執(zhí)行,@After 和 @AfterReturning 數(shù)值越大越先執(zhí)行
其中 @Before、@After、@AfterReturning、@Around、@AfterThrowing 都屬于通知(Advice)。

利用 AOP 實(shí)現(xiàn) Web 日志處理

1.構(gòu)建項(xiàng)目

2.添加依賴(lài)

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

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
		<!-- 熱部署模塊 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-devtools</artifactId>
			<optional>true</optional> <!-- 這個(gè)需要為 true 熱部署才有效 -->
		</dependency>
		<!-- Spring AOP -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>
	</dependencies>

3.Web 日志注解

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface ControllerWebLog {
	 String name();//所調(diào)用接口的名稱(chēng)
     boolean intoDb() default false;//標(biāo)識(shí)該條操作日志是否需要持久化存儲(chǔ)
}

4.實(shí)現(xiàn)切面邏輯

@Aspect
@Component
@Order(100)
public class WebLogAspect {

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

	private ThreadLocal<Map<String, Object>> threadLocal = new ThreadLocal<Map<String, Object>>();

	/**
	 * 橫切點(diǎn)
	 */
	@Pointcut("execution(public * cn.zwqh.springboot.controller..*.*(..))")
	public void webLog() {
	}
	/**
	 * 接收請(qǐng)求,并記錄數(shù)據(jù)
	 * @param joinPoint
	 * @param controllerWebLog
	 */
	@Before(value = "webLog()&& @annotation(controllerWebLog)")
	public void doBefore(JoinPoint joinPoint, ControllerWebLog controllerWebLog) {
		// 接收到請(qǐng)求
		RequestAttributes ra = RequestContextHolder.getRequestAttributes();
		ServletRequestAttributes sra = (ServletRequestAttributes) ra;
		HttpServletRequest request = sra.getRequest();
		// 記錄請(qǐng)求內(nèi)容,threadInfo存儲(chǔ)所有內(nèi)容
		Map<String, Object> threadInfo = new HashMap<>();
		logger.info("URL : " + request.getRequestURL());
		threadInfo.put("url", request.getRequestURL());
		logger.info("URI : " + request.getRequestURI());
		threadInfo.put("uri", request.getRequestURI());
		logger.info("HTTP_METHOD : " + request.getMethod());
		threadInfo.put("httpMethod", request.getMethod());
		logger.info("REMOTE_ADDR : " + request.getRemoteAddr());
		threadInfo.put("ip", request.getRemoteAddr());
		logger.info("CLASS_METHOD : " + joinPoint.getSignature().getDeclaringTypeName() + "."
				+ joinPoint.getSignature().getName());
		threadInfo.put("classMethod",
				joinPoint.getSignature().getDeclaringTypeName() + "." + joinPoint.getSignature().getName());
		logger.info("ARGS : " + Arrays.toString(joinPoint.getArgs()));
		threadInfo.put("args", Arrays.toString(joinPoint.getArgs()));
		logger.info("USER_AGENT"+request.getHeader("User-Agent"));
		threadInfo.put("userAgent", request.getHeader("User-Agent"));
		logger.info("執(zhí)行方法:" + controllerWebLog.name());
		threadInfo.put("methodName", controllerWebLog.name());
		threadLocal.set(threadInfo);
	}
	/**
	 * 執(zhí)行成功后處理
	 * @param controllerWebLog
	 * @param ret
	 * @throws Throwable
	 */
	@AfterReturning(value = "webLog()&& @annotation(controllerWebLog)", returning = "ret")
	public void doAfterReturning(ControllerWebLog controllerWebLog, Object ret) throws Throwable {
		Map<String, Object> threadInfo = threadLocal.get();
		threadInfo.put("result", ret);
		if (controllerWebLog.intoDb()) {
			//插入數(shù)據(jù)庫(kù)操作
			//insertResult(threadInfo);
		}
		// 處理完請(qǐng)求,返回內(nèi)容
		logger.info("RESPONSE : " + ret);
	}
	/**
	 * 獲取執(zhí)行時(shí)間
	 * @param proceedingJoinPoint
	 * @return
	 * @throws Throwable
	 */
	@Around(value = "webLog()")
	public Object doAround(ProceedingJoinPoint proceedingJoinPoint) throws Throwable {
		long startTime = System.currentTimeMillis();
		Object ob = proceedingJoinPoint.proceed();
		Map<String, Object> threadInfo = threadLocal.get();
		Long takeTime = System.currentTimeMillis() - startTime;
		threadInfo.put("takeTime", takeTime);
		logger.info("耗時(shí):" + takeTime);
		threadLocal.set(threadInfo);
		return ob;
	}
	/**
	 * 異常處理
	 * @param throwable
	 */
	@AfterThrowing(value = "webLog()", throwing = "throwable")
	public void doAfterThrowing(Throwable throwable) {
		RequestAttributes ra = RequestContextHolder.getRequestAttributes();

		ServletRequestAttributes sra = (ServletRequestAttributes) ra;

		HttpServletRequest request = sra.getRequest();
		// 異常信息
		logger.error("{}接口調(diào)用異常,異常信息{}", request.getRequestURI(), throwable);
	}

}

5.測(cè)試接口

@RestController
@RequestMapping("/user")
public class UserController {

	@GetMapping("/getOne")
	@ControllerWebLog(name = "查詢(xún)", intoDb = true)
	public String getOne(Long id, String name) {

		return "1234";
	}
}

6.運(yùn)行測(cè)試

瀏覽器請(qǐng)求:http://127.0.0.1:8080/user/getOne?id=1&name=zwqh ,可以看到后臺(tái)日志輸出: Spring AOP如何實(shí)現(xiàn)簡(jiǎn)單的日志切面

日志記錄只是一個(gè)簡(jiǎn)單的示例,而 Spring AOP 的應(yīng)用讓整個(gè)系統(tǒng)變的更加有條不紊,在其他場(chǎng)景應(yīng)用也很強(qiáng)大。 它幫助我們降低模塊間耦合度,提高程序復(fù)用性,提高開(kāi)發(fā)效率,提高系統(tǒng)可做性和可維護(hù)性。

以上就是Spring AOP如何實(shí)現(xiàn)簡(jiǎn)單的日志切面,小編相信有部分知識(shí)點(diǎn)可能是我們?nèi)粘9ぷ鲿?huì)見(jiàn)到或用到的。希望你能通過(guò)這篇文章學(xué)到更多知識(shí)。更多詳情敬請(qǐng)關(guān)注億速云行業(yè)資訊頻道。

向AI問(wèn)一下細(xì)節(jié)

免責(zé)聲明:本站發(fā)布的內(nèi)容(圖片、視頻和文字)以原創(chuàng)、轉(zhuǎn)載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權(quán)請(qǐng)聯(lián)系站長(zhǎng)郵箱:is@yisu.com進(jìn)行舉報(bào),并提供相關(guān)證據(jù),一經(jīng)查實(shí),將立刻刪除涉嫌侵權(quán)內(nèi)容。

AI