基于Spring AOP实现对外接口的耗时监控

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于Spring AOP实现对外接口的耗时监控相关的知识,希望对你有一定的参考价值。

参考技术A

  AOP是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实现
1.jar
与实现接口方式的一致。
2.配置
将业务类、通知类纳入IOC容器。
开启注解对AOP的支持。
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>

使用@Componet注解需要扫描器。

<context:component-scan base-package="org.ghl.aop"/>
3.编写
通知类
//给予注解实现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中。

 
*通过注解形式实现的aop,获取目标对象的信息,要通过对象JoinPoint。
注解形式返回值问题:
声明返回值的参数名。
//注解实现后置通知
@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实现(通过配置实现aop)
(1)编写一个普通类;
(2)将该类通过配置变成一个“通知”。
<!--基于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>

 

获取目标对象时:
注解、Schema形式:JoinPoint
接口:Object returnValue, Method method, Object[] args, Object target
 

以上是关于基于Spring AOP实现对外接口的耗时监控的主要内容,如果未能解决你的问题,请参考以下文章

Spring的AOP快速实现通用日志打印

Spring框架的AOP实现(JDK+CGLIB)

Spring4——基于注解形式的aop实现基于Schema形式的aop实现

AOP,AspectJ 实现统一接口耗时统计

Spring总结七:AOP动态代理的实现

Spring Aop