最近客戶現(xiàn)在提出系統(tǒng)訪問(wèn)非常慢遥赚,需要優(yōu)化提升訪問(wèn)速度担扑,在排查了nginx琴许、tomcat內(nèi)存和服務(wù)器負(fù)載之后聋涨,判斷是數(shù)據(jù)庫(kù)查詢速度慢晾浴,進(jìn)一步排查發(fā)現(xiàn)是因?yàn)椴糠忠晥D和表查詢特別慢導(dǎo)致了整個(gè)系統(tǒng)的響應(yīng)時(shí)間特別長(zhǎng)。知道了問(wèn)題之后牛郑,就需要對(duì)查詢比較慢的接口進(jìn)行優(yōu)化怠肋,但哪些接口需要優(yōu)化、哪些不需要呢淹朋?只能通過(guò)日志里的執(zhí)行時(shí)間來(lái)判斷笙各,那么如何才能知道每一個(gè)接口的執(zhí)行時(shí)間呢?
如果想學(xué)習(xí)Java工程化础芍、高性能及分布式杈抢、深入淺出。微服務(wù)仑性、Spring惶楼,MyBatis,Netty源碼分析的朋友可以加我的Java高級(jí)交流:854630135诊杆,群里有阿里大牛直播講解技術(shù)歼捐,以及Java大型互聯(lián)網(wǎng)技術(shù)的視頻免費(fèi)分享給大家。
對(duì)于這個(gè)問(wèn)題晨汹,想到了使用動(dòng)態(tài)代理的方式統(tǒng)一記錄方法的執(zhí)行時(shí)間并打印日志豹储,這樣就能很直觀、方便的看到每個(gè)接口的執(zhí)行時(shí)間了淘这。
由于使用的是spring框架剥扣,對(duì)象都是由spring統(tǒng)一管理的巩剖,所以最后使用的是?Spring AOP 切面編程來(lái)統(tǒng)一記錄接口的執(zhí)行時(shí)間,具體代碼如下(基于注解的方式):
@Component
@Aspect
public class AopLoggerAspect {
? ? private static final Logger logger = Logger.getLogger(AopLoggerAspect.class);
? ? @Pointcut("execution(public * com.iflytek.credit.platform.*.service.impl.*Impl.*(..)) || execution(public * com.iflytek.credit.platform.*.controller.*Controller.*(..))")
? ? public void pointCut() {
? ? }
? ? @Before("pointCut()")
? ? public void boBefore(JoinPoint joinPoint) {
? ? ? ? String methodName = joinPoint.getSignature().getName();
? ? ? ? logger.info("Method Name : [" + methodName + "] ---> AOP before ");
? ? }
? ? @After("pointCut()")
? ? public void doAfter(JoinPoint joinPoint) {
? ? ? ? String methodName = joinPoint.getSignature().getName();
? ? ? ? logger.info("Method Name : [" + methodName + "] ---> AOP after ");
? ? }
? ? @AfterReturning(pointcut = "pointCut()",returning = "result")
? ? public void afterReturn(JoinPoint joinPoint, Object result) {
? ? ? ? String methodName = joinPoint.getSignature().getName();
? ? ? ? logger.info("Method Name : [" + methodName + "] ---> AOP after return ,and result is : " + result.toString());
? ? }
? ? @AfterThrowing(pointcut = "pointCut()",throwing = "throwable")
? ? public void afterThrowing(JoinPoint joinPoint, Throwable throwable) {
? ? ? ? String methodName = joinPoint.getSignature().getName();
? ? ? ? logger.info("Method Name : [" + methodName + "] ---> AOP after throwing ,and throwable message is : " + throwable.getMessage());
? ? }
? ? @Around("pointCut()")
? ? public Object around(ProceedingJoinPoint joinPoint) {
? ? ? ? String methodName = joinPoint.getSignature().getName();
? ? ? ? try {
? ? ? ? ? ? logger.info("Method Name : [" + methodName + "] ---> AOP around start");
? ? ? ? ? ? long startTimeMillis = System.currentTimeMillis();
? ? ? ? ? ? //調(diào)用 proceed() 方法才會(huì)真正的執(zhí)行實(shí)際被代理的方法
? ? ? ? ? ? Object result = joinPoint.proceed();
? ? ? ? ? ? long execTimeMillis = System.currentTimeMillis() - startTimeMillis;
? ? ? ? ? ? logger.info("Method Name : [" + methodName + "] ---> AOP method exec time millis : " + execTimeMillis);
? ? ? ? ? ? logger.info("Method Name : [" + methodName + "] ---> AOP around end , and result is : " + result.toString());
? ? ? ? ? ? return result;
? ? ? ? } catch (Throwable te) {
? ? ? ? ? ? logger.error(te.getMessage(),te);
? ? ? ? ? ? throw new RuntimeException(te.getMessage());
? ? ? ? }
? ? }
}
首先钠怯,需要?jiǎng)?chuàng)建一個(gè)類佳魔,然后在類名上加上兩個(gè)注解
@Component
@Aspect
@Component 注解是讓這個(gè)類被spring當(dāng)作一個(gè)bean管理,@Aspect 注解是標(biāo)明這個(gè)類是一個(gè)切面對(duì)象
類里面每個(gè)方法的注解含義如下:
@Pointcut? 用于定義切面的匹配規(guī)則晦炊,如果想要同事匹配多個(gè)的話鞠鲜,可以使用 || 把兩個(gè)規(guī)則連接起來(lái),具體可以參照上面的代碼
@Before? 目標(biāo)方法執(zhí)行前調(diào)用
@After? 目標(biāo)方法執(zhí)行后調(diào)用
@AfterReturning? 目標(biāo)方法執(zhí)行后調(diào)用刽锤,可以拿到返回結(jié)果镊尺,執(zhí)行順序在 @After 之后
@AfterThrowing? 目標(biāo)方法執(zhí)行異常時(shí)調(diào)用
@Around? 調(diào)用實(shí)際的目標(biāo)方法,可以在目標(biāo)方法調(diào)用前做一些操作并思,也可以在目標(biāo)方法調(diào)用后做一些操作庐氮。使用場(chǎng)景有:事物管理、權(quán)限控制宋彼,日志打印弄砍、性能分析等等
以上就是各個(gè)注解的含義和作用,重點(diǎn)的兩個(gè)注解就是?@Pointcut 和 @Around 注解输涕,@Pointcut用來(lái)指定切面規(guī)則音婶,決定哪些地方使用這個(gè)切面;@Around 會(huì)實(shí)際的去調(diào)用目標(biāo)方法莱坎,這樣就可以在目標(biāo)方法的調(diào)用前后做一些處理衣式,例如事物、權(quán)限檐什、日志等等碴卧。
需要注意的是,這些方法的執(zhí)行順序:
執(zhí)行目標(biāo)方法前: 先進(jìn)入 around 乃正,再進(jìn)入 before?
目標(biāo)方法執(zhí)行完成后: 先進(jìn)入 around 住册,再進(jìn)入 after ,最后進(jìn)入 afterreturning?
實(shí)際的日志信息如下瓮具,可以看出各個(gè)方法的執(zhí)行順序:
另外荧飞,使用spring aop 需要在spring的配置文件加上以下這行配置,以開(kāi)啟aop :
<aop:aspectj-autoproxy/>
同時(shí)名党,maven中需要加入依賴的jar包:
<dependency>
? <groupId>org.aspectj</groupId>
? <artifactId>aspectjrt</artifactId>
? <version>1.6.12</version>
</dependency>
<dependency>
? <groupId>org.aspectj</groupId>
? <artifactId>aspectjweaver</artifactId>
? <version>1.6.12</version>
</dependency>
總結(jié)一下叹阔,Spring AOP 其實(shí)就是使用動(dòng)態(tài)代理來(lái)對(duì)切面層進(jìn)行統(tǒng)一的處理,動(dòng)態(tài)代理的方式有:JDK動(dòng)態(tài)代理和 cglib 動(dòng)態(tài)代理传睹,JDK動(dòng)態(tài)代理基于接口實(shí)現(xiàn)耳幢, cglib 動(dòng)態(tài)代理基于子類實(shí)現(xiàn)。spring默認(rèn)使用的是JDK動(dòng)態(tài)代理蒋歌,如果沒(méi)有接口帅掘,spring會(huì)自動(dòng)的使用cglib動(dòng)態(tài)代理。
如果想學(xué)習(xí)Java工程化堂油、高性能及分布式修档、深入淺出。微服務(wù)府框、Spring吱窝,MyBatis,Netty源碼分析的朋友可以加我的Java高級(jí)交流:854630135迫靖,群里有阿里大牛直播講解技術(shù)院峡,以及Java大型互聯(lián)網(wǎng)技術(shù)的視頻免費(fèi)分享給大家