第七色在线视频,2021少妇久久久久久久久久,亚洲欧洲精品成人久久av18,亚洲国产精品特色大片观看完整版,孙宇晨将参加特朗普的晚宴

Spring Boot AOP 應(yīng)用場(chǎng)景

1. 前言

Spring 最重要的兩個(gè)功能,就是依賴注入(DI)和面向切面編程 (AOP)。

AOP 為我們提供了處理問(wèn)題的全局化視角,使用得當(dāng)可以極大提高編程效率。

Spring Boot 中使用 AOP 與 Spring 中使用 AOP 幾乎沒(méi)有什么區(qū)別,只是建議盡量使用 Java 配置代替 XML 配置。

本節(jié)就來(lái)演示下 Spring Boot 中使用 AOP 的常見應(yīng)用場(chǎng)景。

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

首先我們需要構(gòu)建一個(gè) Spring Boot 項(xiàng)目并引入 AOP 依賴,后續(xù)場(chǎng)景演示均是在這個(gè)項(xiàng)目上實(shí)現(xiàn)的。

2.1 使用 Spring Initializr 創(chuàng)建項(xiàng)目

Spring Boot 版本選擇 2.2.5 ,Group 為 com.imooc , Artifact 為 spring-boot-aop,生成項(xiàng)目后導(dǎo)入 Eclipse 開發(fā)環(huán)境。

2.2 引入項(xiàng)目依賴

我們引入 Web 項(xiàng)目依賴與 AOP 依賴。

實(shí)例:

		<!-- Web項(xiàng)目依賴 -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-web</artifactId>
		</dependency>
		<!-- AOP -->
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-aop</artifactId>
		</dependency>

2.3 新建控制層、服務(wù)層、數(shù)據(jù)訪問(wèn)層

為了便于后續(xù)的演示,我們依次新建控制類、服務(wù)類、數(shù)據(jù)訪問(wèn)類,并將其放入對(duì)應(yīng)的包中,項(xiàng)目結(jié)構(gòu)如下:

圖片描述

項(xiàng)目結(jié)構(gòu)

各個(gè)類代碼如下,注意此處僅僅是為了演示 AOP 的使用,并未真實(shí)訪問(wèn)數(shù)據(jù)庫(kù),而是直接返回了測(cè)試數(shù)據(jù)。

實(shí)例:

/**
 * 商品控制器類
 */
@RestController
public class GoodsController {
	@Autowired
	private GoodsService goodsService;

	/**
	 * 獲取商品列表
	 */
	@GetMapping("/goods")
	public List getList() {
		return goodsService.getList();
	}
}

實(shí)例:

/**
 * 商品服務(wù)類
 */
@Service
public class GoodsService {
	@Autowired
	private GoodsDao goodsDao;

	/**
	 * 獲取商品信息列表
	 */
	public List getList() {
		return goodsDao.getList();
	}
}

實(shí)例:

/**
 * 商品數(shù)據(jù)庫(kù)訪問(wèn)類
 */
@Repository // 標(biāo)注數(shù)據(jù)訪問(wèn)類
public class GoodsDao {
	/**
	 * 查詢商品列表
	 */
	public List getList() {
		return new ArrayList();
	}
}

3. 使用 AOP 記錄日志

如果要記錄對(duì)控制器接口的訪問(wèn)日志,可以定義一個(gè)切面,切入點(diǎn)即為控制器中的接口方法,然后通過(guò)前置通知來(lái)打印日志。

實(shí)例:

/**
 * 日志切面
 */
@Component
@Aspect // 標(biāo)注為切面
public class LogAspect {
	private Logger logger = LoggerFactory.getLogger(this.getClass());

	// 切入點(diǎn)表達(dá)式,表示切入點(diǎn)為控制器包中的所有方法
	@Pointcut("within(com.imooc.springbootaop.controller..*)")
	public void LogAspect() {
	}

	// 切入點(diǎn)之前執(zhí)行
	@Before("LogAspect()")
	public void doBefore(JoinPoint joinPoint) {
		logger.info("訪問(wèn)時(shí)間:{}--訪問(wèn)接口:{}", new Date(), joinPoint.getSignature());
	}
}

啟動(dòng)項(xiàng)目后,訪問(wèn)控制器中的方法之前會(huì)先執(zhí)行 doBefore 方法??刂婆_(tái)打印如下:

2020-05-25 22:14:12.317  INFO 9992 --- [nio-8080-exec-2] com.imooc.springbootaop.LogAspect        : 
訪問(wèn)時(shí)間:Mon May 25 22:14:12 CST 2020--訪問(wèn)接口:List com.imooc.springbootaop.controller.GoodsController.getList()

4. 使用 AOP 監(jiān)控性能

在研發(fā)項(xiàng)目的性能測(cè)試階段,或者項(xiàng)目部署后,我們會(huì)希望查看服務(wù)層方法執(zhí)行的時(shí)間。以便精準(zhǔn)的了解項(xiàng)目中哪些服務(wù)方法執(zhí)行速度慢,后續(xù)可以針對(duì)性的進(jìn)行性能優(yōu)化。

此時(shí)我們就可以使用 AOP 的環(huán)繞通知,監(jiān)控服務(wù)方法的執(zhí)行時(shí)間。

實(shí)例:

/**
 * 服務(wù)層方法切面
 */
@Component
@Aspect // 標(biāo)注為切面
public class ServiceAspect {
	private Logger logger = LoggerFactory.getLogger(this.getClass());

	// 切入點(diǎn)表達(dá)式,表示切入點(diǎn)為服務(wù)層包中的所有方法
	@Pointcut("within(com.imooc.springbootaop.service..*)")
	public void ServiceAspect() {
	}

	@Around("ServiceAspect()") // 環(huán)繞通知
	public Object deAround(ProceedingJoinPoint joinPoint) throws Throwable {
		long startTime = System.currentTimeMillis();// 記錄開始時(shí)間
		Object result = joinPoint.proceed();
		logger.info("服務(wù)層方法:{}--執(zhí)行時(shí)間:{}毫秒", joinPoint.getSignature(), System.currentTimeMillis() - startTime);
		return result;
	}
}

當(dāng)服務(wù)層方法被調(diào)用時(shí),控制臺(tái)輸入日志如下:

2020-05-25 22:25:56.830  INFO 4800 --- [nio-8080-exec-1] com.imooc.springbootaop.ServiceAspect    : 
服務(wù)層方法:List com.imooc.springbootaop.service.GoodsService.getList()--執(zhí)行時(shí)間:3毫秒

Tips:正常情況下,用戶查看頁(yè)面或進(jìn)行更新操作時(shí),耗時(shí)超過(guò) 1.5 秒,就會(huì)感覺到明顯的遲滯感。由于前后端交互也需要耗時(shí),按正態(tài)分布的話,大部分交互耗時(shí)在 0.4秒 左右。所以在我參與的項(xiàng)目中,會(huì)對(duì)耗時(shí)超過(guò) 1.1 秒的服務(wù)層方法進(jìn)行跟蹤分析,通過(guò)優(yōu)化 SQL 語(yǔ)句、優(yōu)化算法、添加緩存等方式縮短方法執(zhí)行時(shí)間。上面的數(shù)值均為我個(gè)人的經(jīng)驗(yàn)參考值,還要視乎具體的服務(wù)器、網(wǎng)絡(luò)、應(yīng)用場(chǎng)景來(lái)確定合理的監(jiān)控臨界值。

5. 使用 AOP 統(tǒng)一后端返回值格式

前后端分離的項(xiàng)目結(jié)構(gòu)中,前端通過(guò) Ajax 請(qǐng)求后端接口,此時(shí)最好使用統(tǒng)一的返回值格式供前端處理。此處就可以借助 AOP 來(lái)實(shí)現(xiàn)正常情況、異常情況返回值的格式統(tǒng)一。

5.1 定義返回值類

首先定義返回值類,它屬于業(yè)務(wù)邏輯對(duì)象 (Bussiness Object),所以此處命名為 ResultBo ,代碼如下:

實(shí)例:

public class ResultBo<T> {
	/**
	 * 錯(cuò)誤碼 0表示沒(méi)有錯(cuò)誤(異常) 其他數(shù)字代表具體錯(cuò)誤碼
	 */
	private int code;
	/**
	 * 后端返回消息
	 */
	private String msg;
	/**
	 * 后端返回的數(shù)據(jù)
	 */
	private T data;
	/**
	 * 無(wú)參數(shù)構(gòu)造函數(shù)
	 */
	public ResultBo() {
		this.code = 0;
		this.msg = "操作成功";
	}
	/**
	 * 帶數(shù)據(jù)data構(gòu)造函數(shù)
	 */
	public ResultBo(T data) {
		this();
		this.data = data;
	}
	/**
	 * 存在異常的構(gòu)造函數(shù)
	 */
	public ResultBo(Exception ex) {
		this.code = 99999;// 其他未定義異常
		this.msg = ex.getMessage();
	}
	// 省略 get set
}

5.2 修改控制層返回值類型

對(duì)所有的控制層方法進(jìn)行修改,保證返回值均通過(guò) ResultBo 包裝,另外我們?cè)俣x一個(gè)方法,模擬拋出異常的控制層方法。

實(shí)例:

	/**
	 * 獲取商品列表
	 */
	@GetMapping("/goods")
	public ResultBo getList() {
		return new ResultBo(goodsService.getList());
	}
	/**
	 * 模擬拋出異常的方法
	 */
	@GetMapping("/test")
	public ResultBo test() {
		int a = 1 / 0;
		return new ResultBo(goodsService.getList());
	}

5.3 定義切面處理異常返回值

正??刂茖臃椒ǘ挤祷?ResultBo 類型對(duì)象,然后我們需要定義切面,處理控制層拋出的異常。當(dāng)發(fā)生異常時(shí),同樣返回 ResultBo 類型的對(duì)象,并且對(duì)象中包含異常信息。

實(shí)例:

/**
 * 返回值切面
 */
@Component
@Aspect
public class ResultAspect {
	// 切入點(diǎn)表達(dá)式,表示切入點(diǎn)為返回類型ResultBo的所有方法
	@Pointcut("execution(public com.imooc.springbootaop.ResultBo *(..))")
	public void ResultAspect() {
	}

	// 環(huán)繞通知
	@Around("ResultAspect()")
	public Object deAround(ProceedingJoinPoint joinPoint) throws Throwable {
		try {
			return joinPoint.proceed();// 返回正常結(jié)果
		} catch (Exception ex) {
			return new ResultBo<>(ex);// 被切入的方法執(zhí)行異常時(shí),返回ResultBo
		}
	}
}

5.4 測(cè)試

啟動(dòng)項(xiàng)目,訪問(wèn) http://127.0.0.1:8080/goods 返回?cái)?shù)據(jù)如下:

實(shí)例:

{"code":0,"msg":"操作成功","data":[]}

然后訪問(wèn) http://127.0.0.1:8080/test ,返回?cái)?shù)據(jù)如下:

實(shí)例:

{"code":99999,"msg":"/ by zero","data":null}

這樣,前端可以根據(jù)返回值的 code, 來(lái)判斷后端是否正常響應(yīng)。如果 code 為 0 ,則進(jìn)行正常業(yè)務(wù)邏輯操作;如果 code 非 0 ,則可以彈窗顯示 msg 提示信息。

6. 小結(jié)

AOP 之所以如此重要,在于它提供了解決問(wèn)題的新視角。通過(guò)將業(yè)務(wù)邏輯抽象出切面,功能代碼可以切入指定位置,從而消除重復(fù)的模板代碼。

使用 AOP 有一種掌握全局的快感,發(fā)現(xiàn)業(yè)務(wù)邏輯中的切面頗有一番趣味,希望大家都能多多體會(huì),編程且快樂(lè)著應(yīng)該是我輩的追求。