基于Spring AOP实现对外接口的耗时监控
Posted
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于Spring AOP实现对外接口的耗时监控相关的知识,希望对你有一定的参考价值。
参考技术AAOP是Spring的核心 Spring不但自身对多种框架的集成是基于AOP 并且以非常方便的形式暴露给普通使用者 以前用AOP不多 主要是因为它以横截面的方式插入到主流程中 担心导致主流程代码不够清晰 定位问题不够方便 而在计费二期的项目里需要一个很适合用AOP来做的功能 就是要把对外接口和所调用的外部接口的耗时时间给记录下来 这个需求主要来自于计费一期的联调 常常发生系统间交互不够顺畅的情况 这就需要看每个接口调用时间来判定是谁的问题
计费中心是整个后台系统的中间环节 与其他系统交互很多 这样的接口也很多 如果在每个接口的调用前后加时间记录比较繁琐 也影响主流程代码的美观 因此比较优雅的方式是用AOP 在不侵入原有代码的情况下 加上对接口调用的监控 并且可以在不需要的时候很容易移除 今天尝试了一下 感觉还挺好用 下面讲述一下实施步骤
)引入包依赖
本项目基于maven构建 因此加上包依赖比较方便 我需要的AOP依赖库有以下三个
[x]
<dependency>
<groupId> springframework</groupId>
<artifactId>spring aop</artifactId>
<version> </version>
</dependency>
<dependency>
<groupId> aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version> </version>
</dependency>
<dependency>
<groupId> aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version> </version>
</dependency>
<dependency>
<groupId> springframework</groupId>
<artifactId>spring aop</artifactId>
<version> </version>
</dependency>
<dependency>
<groupId> aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version> </version>
</dependency>
<dependency>
<groupId> aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version> </version>
</dependency>
)加上AOP的Spring配置文件
billing spring aop xml:
[x]
<?xml version= encoding= UTF ?>
<beans xmlns=
xmlns:xsi= instance
xmlns:aop=
xmlns:tx=
xsi:schemaLocation= beans xsd
aop xsd
tx xsd >
<bean id= openApiLogAspect class= bu billing framework aop OpenApiLogAspect >
</bean>
<aop:config>
<! 配置aspect切面类 >
<aop:aspect ref= openApiLogAspect >
<! 配置pointcut 即切入点 对哪些类的哪些方法起到AOP的作用 >
<aop:pointcut id= E *** PriceService
expression= execution(* product E *** PriceService *()) />
<aop:pointcut id= E *** ProductService
expression= execution(* product E *** ProductService *()) />
<aop:pointcut id= IAuthorizeControllerService
expression= execution(* alibaba bss pc server remoting IAuthorizeControllerService *()) />
<aop:pointcut id= IOpenApiOrderItemService
expression= execution(* llect IOpenApiOrderItemService *()) />
<aop:pointcut id= IOpenApiBillingCollectService
expression= execution(* llect IOpenApiBillingCollectService *()) />
<aop:pointcut id= IOpenApiInvoiceService
expression= execution(* bu billing api invoice IOpenApiInvoiceService *()) />
<aop:pointcut id= IOpenApiChargeProductInfoService
expression= execution(* llect IOpenApiChargeProductInfoService *()) />
<! 配置advice 这里采用在业务方法执行前后进行拦截 >
<aop:around method= logExecuteTime pointcut ref= E *** PriceService />
<aop:around method= logExecuteTime pointcut ref= E *** ProductService />
<aop:around method= logExecuteTime pointcut ref= IAuthorizeControllerService />
<aop:around method= logExecuteTime pointcut ref= IOpenApiOrderItemService />
<aop:around method= logExecuteTime pointcut ref= IOpenApiBillingCollectService />
<aop:around method= logExecuteTime pointcut ref= IOpenApiInvoiceService />
<aop:around method= logExecuteTime pointcut ref= IOpenApiChargeProductInfoService />
</aop:aspect>
</aop:config>
</beans>
<?xml version= encoding= UTF ?>
<beans xmlns=
xmlns:xsi= instance
xmlns:aop=
xmlns:tx=
xsi:schemaLocation= beans xsd
aop xsd
tx xsd >
<bean id= openApiLogAspect class= bu billing framework aop OpenApiLogAspect >
</bean>
<aop:config>
<! 配置aspect切面类 >
<aop:aspect ref= openApiLogAspect >
<! 配置pointcut 即切入点 对哪些类的哪些方法起到AOP的作用 >
<aop:pointcut id= E *** PriceService
expression= execution(* product E *** PriceService *()) />
<aop:pointcut id= E *** ProductService
expression= execution(* product E *** ProductService *()) />
<aop:pointcut id= IAuthorizeControllerService
expression= execution(* alibaba bss pc server remoting IAuthorizeControllerService *()) />
<aop:pointcut id= IOpenApiOrderItemService
expression= execution(* llect IOpenApiOrderItemService *()) />
<aop:pointcut id= IOpenApiBillingCollectService
expression= execution(* llect IOpenApiBillingCollectService *()) />
<aop:pointcut id= IOpenApiInvoiceService
expression= execution(* bu billing api invoice IOpenApiInvoiceService *()) />
<aop:pointcut id= IOpenApiChargeProductInfoService
expression= execution(* llect IOpenApiChargeProductInfoService *()) />
<! 配置advice 这里采用在业务方法执行前后进行拦截 >
<aop:around method= logExecuteTime pointcut ref= E *** PriceService />
<aop:around method= logExecuteTime pointcut ref= E *** ProductService />
<aop:around method= logExecuteTime pointcut ref= IAuthorizeControllerService />
<aop:around method= logExecuteTime pointcut ref= IOpenApiOrderItemService />
<aop:around method= logExecuteTime pointcut ref= IOpenApiBillingCollectService />
<aop:around method= logExecuteTime pointcut ref= IOpenApiInvoiceService />
<aop:around method= logExecuteTime pointcut ref= IOpenApiChargeProductInfoService />
</aop:aspect>
</aop:config>
</beans>
我是基于配置完成AOP接入 这样做的好处是不需要对原有主流程代码有任何浸入 并且也比较容易移除本AOP的拦截 这段代码主要就是配置aspect pointcut和advice
)编写监控耗时的advice
OpenApiLogAspect:
[java]
public class OpenApiLogAspect
private static LoggerService logger = LoggerFactory getLogger(OpenApiLogAspect class);
public Object logExecuteTime(ProceedingJoinPoint joinPoint) throws Throwable
Date start = new Date();
try
return joinPoint proceed(joinPoint getArgs());
catch(Exception err)
throw err;
finally
Date end = new Date();
( OpenApiExecuteTime: +joinPoint getSignature() getName()+ takes +(end getTime() start getTime())+ ms );
public class OpenApiLogAspect
private static LoggerService logger = LoggerFactory getLogger(OpenApiLogAspect class);
public Object logExecuteTime(ProceedingJoinPoint joinPoint) throws Throwable
Date start = new Date();
try
return joinPoint proceed(joinPoint getArgs());
catch(Exception err)
throw err;
finally
Date end = new Date();
( OpenApiExecuteTime: +joinPoint getSignature() getName()+ takes +(end getTime() start getTime())+ ms );
此段代码就是基于around的方式来拦截接口调用 在实际调用的前后加上时间记录 并最后在日志里打印出时间差 其中joinPoint proceed(joinPoint getArgs());是对实际接口的调用
)使监控可以配置化
此功能只会在调试阶段使用 并不需要在生产环境中运行 因此需要可以配置是否监控接口 实施这个配置化很简单 只需要通过配置决定是否把aop spring的配置文件加入到容器里就可以了 因此在总容器applicationContext xml vm里加上如下代码
#if($monitor_openapi_showTime== true )
<import resource= classpath*:bean/billing spring aop xml />
#end
在编译打包过程中会根据变量monitor_openapi_showTime来决定是否把billing spring aop xml引入进来
)运行效果
在监控开启的情况下 若发生接口调用 能从日志里看到如下记录
: : [OpenApiLogAspect java: ] [bu billing framework aop OpenApiLogAspect] INFO bu billing framework aop OpenApiLogAspect :: OpenApiExecuteTime:installOrderItem takes ms
: : [OpenApiLogAspect java: ] [bu billing framework aop OpenApiLogAspect] INFO bu billing framework aop OpenApiLogAspect :: OpenApiExecuteTime:installOrderItem takes ms
lishixinzhi/Article/program/Java/ky/201311/28361
Spring4——基于注解形式的aop实现基于Schema形式的aop实现
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
使用@Componet注解需要扫描器。
<context:component-scan base-package="org.ghl.aop"/>
//给予注解实现aop //加此注解后此类是通知 @Aspect public class LogAspectAnnotation { //添加注解使得方法变成前置通知方法 @Before("execution(public * addStudent(..))") //属性:定义切点 public void logBeforeAnno(){ System.out.println("注解形式【前置通知】..."); } //注解实现后置通知 @AfterReturning("execution(public * addStudent(..))") //属性:定义切点 public void logAfterAnno(){ System.out.println("注解形式【后置通知】..."); } }
注意:扫描器会将指定包中的@Componet, @Service, @Respository, @Controller 修饰的类产生的对象添加到xml中。
//注解实现后置通知 @AfterReturning(pointcut = "execution(public * org.ghl.service.impl.StudentServiceImpl.addStudent(..))",returning = "returningValue") //属性:定义切点 public void logAfterAnno(JoinPoint jp,Object returningValue){ System.out.println("注解形式【后置通知】...:目标对象:"+jp.getTarget()+",方法名:"+jp.getSignature().getName()+",参数列表:"+ Arrays.toString(jp.getArgs())+",返回值:"+returningValue); }
*若报错:IllegalArgumentException: 参数异常。
*注解形式异常通知,若想捕获指定的异常,则用第二个参数e
//异常通知,若想捕获指定的异常,则用第二个参数 @AfterThrowing(value = "execution(public * org.ghl.service.impl.StudentServiceImpl.addStudent(..))",throwing = "e") public void logExceptionAnno(JoinPoint jp,NullPointerException e){ System.out.println("《注解形式【异常通知】》:e:"+e.getMessage()); }
<!--基于Schema形式的aop实现--> <bean id="logSchema" class="org.ghl.aop.LogSchema"> </bean> <aop:config> <!--配置切入点(在哪里执行通知)--> <!--=====连接线的另一方======--> <aop:pointcut expression="execution(public * org.ghl.service.impl.StudentServiceImpl.addStudent(org.ghl.entity.Student))" id="pcSchema"></aop:pointcut> <!--advisor相当于连接切入点和切面的线--> <!--=======连接线=======--> <!-- <aop:advisor advice-ref="logSchema" pointcut-ref="pcSchema"/>--> <!--Schema形式--> <aop:aspect ref="logSchema"> <!--连接线--> <aop:before method="before" pointcut-ref="pcSchema"/> <aop:after-returning method="afterReturning" pointcut-ref="pcSchema" returning="returnValue"/> <aop:after-throwing method="whenException" throwing="e" pointcut-ref="pcSchema"/> <aop:around method="around" pointcut-ref="pcSchema"/> </aop:aspect> </aop:config>
以上是关于基于Spring AOP实现对外接口的耗时监控的主要内容,如果未能解决你的问题,请参考以下文章