spring 自定义注解annotation+aspect 环绕通知配置对dubbo的consumer监控报警

背景:

对dubbo 的consumer端进行统一监控,实现consumer的统一异常处理、前置provider服务的可用性校验(若dubobo服务不可以发短信提醒)

思路:

(1)自定义annotation,仅作用在类、方法上。减少代码耦合性,consumer的类或方法只要增加自定义的注解即可。

(2)犹豫自定义的annotation有可能标注在服务类上,不一定只标注在controller上,所以用springmvc的interceptor有弊端,所以想到用spring的aop的环绕通知进行拦截处理

1.自定义annotation

<span style="font-family:SimSun;font-size:10px;">@Target({ TYPE, METHOD })
@Retention(RetentionPolicy.RUNTIME)
public @interface Monitor {
    /**
     * 消费的服务
     * @return
     */
    String serviceName() default "";
    /**
     * 服务描述
     * @return
     */
    String desc() default "";
}</span>

1.annotation实现逻辑

<span style="font-family: Arial; font-size: 14px;">  </span><span style="font-family:SimSun;font-size:10px;"> //注解在类上或方法上
                Monitor monitor=mHandler.getMethodAnnotation(Monitor.class);
                if (beanClass.isAnnotationPresent(Monitor.class)||monitor!=null) {
                    String serviceName=monitor.serviceName();
                    if(StringUtils.isNotBlank(serviceName)){
                        EchoService echoService = null;
                        try {
                            echoService = (EchoService) SpringContextUtil.getBean(monitor.serviceName());
                        } catch (Exception e) {
                            log.error("不存在的bean,benName:"+serviceName,e);
                        }
                        try {
                            Object status = echoService.$echo("OK");
                            assert ("OK".equals(status.toString()));
                        } catch (Exception e) {
                            log.error("dubbo 调用失败!请检查是否存在此服务或dubbo是否正常!serviceName:"+serviceName,e);
                            //发送短信通知 TODO
                        }
                    }
                }</span>



3. Spring AOP

以@AspectJ方式在Spring中实现AOP。由于@Aspect是基于注解的,因此要求支持注解的5.0版本以上的JDK。

要在项目中使用Spring AOP 则需要在项目中导入除了spring jar包之外,还有aspectjweaver.jar,aspectjrt.jar 和cglib.jar 。

在Spring MVC基本上只需另外加上aspectjweaver.jar和cglib.jar就可以了

[html]  view plain copy print ?
  1. <dependency>  
  2.     <groupId>org.aspectj</groupId>  
  3.     <artifactId>aspectjweaver</artifactId>  
  4.     <version>1.7.1</version>  
  5. </dependency>  
  6.   
  7. <dependency>  
  8.     <groupId>cglib</groupId>  
  9.     <artifactId>cglib</artifactId>  
  10.     <version>2.2.2</version>  
  11. </dependency>  

好了,前提工作准备完成。


b.环绕增强的@AspectJ代码

<span style="font-family:SimSun;font-size:10px;">@Component
@Aspect
public class DubboMonitor {
    @Around(value = "execution(* dubbo.consumer.adapter.*.*(..)) && @annotation(monitor)")
    public Object aroundMethod(ProceedingJoinPoint pjd, Monitor monitor) {
        Object result = null;
        System.out.println(monitor.desc());
        try {
            System.out.println("前置通知");
            result = pjd.proceed();
            System.out.println("后置通知");
        } catch (Throwable e) {
            System.out.println("异常通知");
        }
        System.out.println("返回通知");
        return result;
    }
}</span>

[java]  view plain copy print ?
  1. @Aspect  
  2. public class ApiAspect {  
  3.   
  4.     private static final Logger logger = LoggerFactory.getLogger("com.xxx.log");  
  5.   
  6.     //通过within匹配目标方法的class  
  7.     @Around("within(com.xxx.api.*Controller)")  
  8.     public String arountAction(ProceedingJoinPoint pjp){  
  9.           
  10.         //接口request参数检查,  
  11.         HttpServletRequest request = (HttpServletRequest) pjp.getArgs()[0];  
  12.         try {  
  13.             //TODO check  
  14.         } catch (Exception e) {  
  15.             //TODO log & return  
  16.         }  
  17.   
  18.         String result = null;  
  19.         try {  
  20.             //执行目标方法  
  21.             result = (String) pjp.proceed();  
  22.             //TODO log  
  23.         } catch (Throwable e) {  
  24.             //TODO log & return  
  25.         }  
  26.           
  27.         return result;  
  28.     }  
  29. }  
c.Spring xml配置

[html]  view plain copy print ?
  1. <aop:aspectj-autoproxy />  
  2. <beans:bean class="com.xxx.api.ApiAspect" />  (可以用@component注解代替此行配置)
  3. <context:component-scan base-package="com.xxx.api.*" />  

这样就实现了所需的功能 ,AOP的优点就是对目标方法代码不做任何改动,就可以实现前后处理,另外在b中也可以用@Before,@After等其它注解来实现不同的功能,下面就介绍下@AspectJ的详细用法

4. @AspectJ的详细用法

在Spring AOP中目前只有执行方法这一个连接点,Spring AOP支持的AspectJ切入点指示符如下:


一些常见的切入点的例子 
execution(public * * (. .))    任意公共方法被执行时,执行切入点函数。 
execution( * set* (. .))   任何以一个“set”开始的方法被执行时,执行切入点函数。 
execution( * com.demo.service.AccountService.* (. .))  当接口AccountService 中的任意方法被执行时,执行切入点函数。 
execution( * com.demo.service.*.* (. .))  当service 包中的任意方法被执行时,执行切入点函数。 within(com.demo.service.*)  在service 包里的任意连接点。 within(com.demo.service. .*)  在service 包或子包的任意连接点。
this(com.demo.service.AccountService) 实现了AccountService 接口的代理对象的任意连接点。 
target(com.demo.service.AccountService) 实现了AccountService 接口的目标对象的任意连接点。 
args(java.io.Serializable)  任何一个只接受一个参数,且在运行时传入参数实现了 Serializable 接口的连接点

增强的方式:

@Before:方法前执行

@AfterReturning:运行方法后执行

@AfterThrowing:Throw后执行

@After:无论方法以何种方式结束,都会执行(类似于finally)

@Around:环绕执行



@AspectJ语法基础
@AspectJ使用JDK 5.0注解和正规的AspectJ 5的切点表达式语言描述切面,由于Spring只支持方法的连接点,所以Spring仅支持部分AspectJ的切点语言。在这节时,我们将对AspectJ切点表达式语言进行必要的学习。
切点表达式函数
    AspectJ 5的切点表达式由关键字和操作参数组成,如execution(* greetTo(..))的切点表达式,“execute”为关键字,而“* greetTo(..)”为操作参数。在这里,execute代表目标类执行某一方法,而“* greetTo(..)”是描述目标方法的匹配模式串,两者联合起来所表示的切点匹配目标类greetTo()方法的连接点。为了描述方便,我们将 execution()称作函数,而将匹配串“* greetTo(..)”称作函数的入参。 
Spring支持9个@ApsectJ切点表达式函数,它们用不同的方式描述目标类的连接点,根据描述对象的不同,可以将它们大致分为4种类型: 
? 方法切点函数:通过描述目标类方法信息定义连接点; 
? 方法入参切点函数:通过描述目标类方法入参的信息定义连接点; 
? 目标类切点函数:通过描述目标类类型信息定义连接点; 
? 代理类切点函数:通过描述目标类的代理类的信息定义连接点; 
     这4种类型的切点函数,通过表 1进行说明: 
    表 1 切点函数

类别
函数
入参
说明
方法切点函数
execution()
方法
匹配模式串
表示满足某一匹配模式的所有目标类方法连接点。如execution(* greetTo(..))表示所有目标类中的greetTo()方法。
@annotation()
方法注
解类名
表示标注了特定注解的目标方法连接点。如@annotation(com.baobaotao.anno.NeedTest)表示任何标注了@NeedTest注解的目标类方法。
方法入参切点函数
args()
类名
通过判别目标类方法运行时入参对象的类型定义指定连接点。如args(com.baobaotao.Waiter)表示所有有且仅有一个按类型匹配于Waiter的入参的方法。
@args()
类型注
解类名
通过判别目标方法的运行时入参对象的类是否标注特定注解来指定连接点。如@args(com.baobaotao.Monitorable)表示任何这样的一个目标方法:它有一个入参且入参对象的类标注@Monitorable注解。
目标类切点函数
within()
类名匹配串
   表 示特定域下的所有连接点。如within(com.baobaotao.service.*)表示com.baobaotao.service包中的所有 连接点,也即包中所有类的所有方法,而within(com.baobaotao.service.*Service)表示在 com.baobaotao.service包中,所有以Service结尾的类的所有连接点。
target()
类名
   假如目标类按类型匹配于指定类,则目标类的所有连接点匹配这个切点。如通过target(com.baobaotao.Waiter)定义的切点,Waiter、以及Waiter实现类NaiveWaiter中所有连接点都匹配该切点。
@within()
类型注解类名
   假如目标类按类型匹配于某个类A,且类A标注了特定注解,则目标类的所有连接点匹配这个切点。
   如@within(com.baobaotao.Monitorable)定义的切点,假如Waiter类标注了@Monitorable注解,则Waiter以及Waiter实现类NaiveWaiter类的所有连接点都匹配。
@target()
类型注解类名
   目标类标注了特定注解,则目标类所有连接点匹配该切点。如@target(com.baobaotao.Monitorable),假如NaiveWaiter标注了@Monitorable,则NaiveWaiter所有连接点匹配切点。
代理类切点函数
this()
类名
 代理类按类型匹配于指定类,则被代理的目标类所有连接点匹配切点。这个函数比较难理解,这里暂不举例,留待后面详解。

@AspectJ 除上表中所列的函数外,还有call()、initialization()、 preinitialization()、 staticinitialization()、 get()、 set()、handler()、 adviceexecution()、 withincode()、 cflow()、 cflowbelow()、 if()、 @this()以及@withincode()等函数,这些函数在Spring中不能使用,否则会抛出IllegalArgumentException 异常。在不特别声明的情况下,本书中所讲@AspectJ函数均指表 1中所列的函数。


  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值