背景

项目中,一个第三方jar包提供的方法无法满足需求,需要对jar包方法进行增强。使用了Spring的自定义切面进行方法的增强。代码如下:

@Aspect
@Component
public class SendMessageAspect{

    public SendMessageAspect(){
        System.out.println("测试入口");
    }

    @Autowired
    ICimKeywordService keywordService;


    @Pointcut("execution(public * com.farsunset.cim.component.handler.SendMessageHandler.process(..))")
   // @Pointcut("execution(public * com.farsunset.cim.config.CIMConfig.process(..))")
    public void execute() {

    }


    @Around("execute()")
    public void around(ProceedingJoinPoint joinPoint)throws Throwable {
        // 获取目标方法的名称
        String methodName = joinPoint.getSignature().getName();
        // 获取方法传入参数
        Object[] params = joinPoint.getArgs();
        SentBody body=(SentBody)params[1];
        String content=body.get("content");
        String format=body.get("format");
        if("text".equals(format)&& StringUtils.isNotEmpty(content)){
            //将关键字替换成*
          List<CimKeyword> keywords= keywordService.selectCimKeywordList(null);
          if(keywords!=null&&keywords.size()>0){
              for (CimKeyword keyword:
                      keywords) {
                  if(content.contains(keyword.getKeyword())){
                      content=content.replaceAll(keyword.getKeyword(),"**");
                  }
              }
              body.put("content",content);
              params[1]=body;
          }
        }

        // 执行源方法
        joinPoint.proceed(params);

    }
}

问题

执行代码后,发现不但没有增强效果,之前jar包的方法反而也执行不到了。around方法根本执行不进来。但是切面确实起作用了,因为去掉切面后,jar包的方法正常执行,加上切面后,jar包的方法无法正常执行。这说明切面是拦截到方法的。

解决

刚开始遇到这个问题,也是无从下手,甚至想到了从Spring源码入手,看切面的执行流程,进行问题的查找。如果真这么解决这个问题,那时间成本就大了去了。最终还是放弃了这种想法。
解决问题的本质还是理解切面的含义。切面中定义的切入点就是在执行切入点的方法时,调用around方法,进行方法的增强。而断点是没走进around方法的,证明就没有调用要增强的方法。所以,解决这个问题,还得找哪里调用的这个切入点方法。
由于这个jar包是我们自己开发的jar包,所以很容易找到了切入点调用的地方,方法如下:

@Override
	public void process(Channel channel, SentBody body) {
		
        CIMRequestHandler handler = handlerMap.get(body.getKey());
		
		if(handler == null) {return ;}
		
		handler.process(channel, body);
		
	}

可以看到,切入点方法的对象是存入了handlerMap中,这里通过Map获得切入点对象,然后执行切入点方法process。我现在对这个方法进行了增强,加了切面,那这个对象就是代理对象了,而不是原生对象了。所以,我们要看handlerMap是在哪里加的这个对象。代码如下:

spring 自定义conversionservice spring 自定义 切面 不生效_aop


可以看到,该方法是Spring事件机制中的监听者方法,从Spring容器中获取CIMRequestHandler类型的对象,然后遍历,判断对象是否有CIMHandler注解,如果有,才加入handlerMap集合。因为我用了代理,所以现在获取到的是代理对象,该方法之前用的

CIMHandler annotation = handler.getClass().getAnnotation(CIMHandler.class);

代码无法获取到代理对象的注解。相当于注解丢失了。所以handlerMap中不会有切入点的代理对象。这就是为什么走不到around方法的原因。因为压根就没有这个对象,更不用说调用它的方法了。
解决方法是用如下方法获取代理对象的注解:

CIMHandler annotation =AnnotatedElementUtils.findMergedAnnotation(handler.getClass(),CIMHandler.class);

这里用到了AnnotatedElementUtils工具类。这个类,我们以后进行单独详解。

总结

解决该bug主要是从哪里考虑问题。没有执行around方法,证明就没有调用被增强的方法,所以找到哪里调用的这个方法是关键。