【问题标题】:Spring AOP does not intercept Feign.Client callsSpring AOP 不拦截 Feign.Client 调用
【发布时间】:2019-06-20 22:01:29
【问题描述】:

我正在尝试使用 Spring AOP 拦截 Feign.Client 调用并记录对我的 Splunk 服务器的请求和响应。我的项目包中的所有方法都按我的预期被拦截,但Feign.Client 没有。

这是我的 AOP 类:

@Component
@Aspect
public class MyAspect {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Pointcut("execution(* com.example.demo.*.*(..))")
    public void pointCutDemo(){}

    @Pointcut("execution(* feign.Client+.*(..))")
    public void pointCutFeign(){}

    @Around("pointCutDemo()")
    public void myAroundDemo(ProceedingJoinPoint joinPoint) throws Throwable {
        logger.info("calling joinpoint "+joinPoint.getSignature().getName());
        joinPoint.proceed();
    }

    @Around("pointCutFeign()")
    public void myAroundFeign(ProceedingJoinPoint joinPoint) throws Throwable {
        logger.info("calling feign joinpoint "+joinPoint.getSignature().getName());
        joinPoint.proceed();
    }
}

正如我所料,方法myAroundDemo 被多次调用,但从未调用myAroundFeign

我有一个简单的控制器来调用我的接口(Feign API),这是控制器:

@RestController
public class Controller {
    private Logger logger = LoggerFactory.getLogger(this.getClass());

    @Autowired
    private ExternalAPI externalAPI;

    @GetMapping
    public String get(){
        logger.info("calling get method");
        logger.info(String.valueOf(externalAPI.listUsers()));
        return "I'm here";
    }
}

这是我的 Feign 界面:

@FeignClient(url = "http://localhost:3000", name = "feign", configuration = FeignConfig.class)
public interface ExternalAPI {
    @GetMapping(value = "/menu")
    String listUsers();
}

【问题讨论】:

    标签: aop spring-aop spring-cloud-feign feign


    【解决方案1】:

    Spring AOP 仅适用于 Spring 组件。我的猜测是它不起作用,因为 Feign 不是 Spring 组件,因此超出了 Spring AOP 的范围。如果您需要将方面应用到非 Spring 类,只需使用完整的 AspectJ。 Spring手册解释了如何configure it via LTW (load-time weaving)

    【讨论】:

      【解决方案2】:

      @kriegaex 是正确的,我不能在非 Spring 组件中应用 AOP。但我不喜欢使用纯 AspectJ,所以我用另一种解决方案进行了修复。以下是我解决问题的步骤:

      1) 启用 Spring Cloud Ribbon 我得到了由实现 feign.Client 的 spring 管理的类 LoadBalancerFeignClient,所以我在 pom.xml 中添加了依赖项并更改了我的 application.yml。

      application.yml

      myfeign:
        ribbon:
          listOfServers: localhost:3000
      

      pom.xml

      <dependency>
          <groupId>org.springframework.cloud</groupId>
          <artifactId>spring-cloud-starter-netflix-ribbon</artifactId>
      </dependency>
      

      2) 在MyAspect 类中我截获了LoadBalancerFeignClient 类:

      @Pointcut("execution(* org.springframework.cloud.openfeign.ribbon.LoadBalancerFeignClient.execute(..))")
      public void pointCutFeign(){}
      
      @Around("pointCutFeign()")
      public Object myAroundFeign(ProceedingJoinPoint joinPoint) throws Throwable {
          if (joinPoint.getArgs().length > 0) {
              Request request = (Request) joinPoint.getArgs()[0];
              logger.info("REQUEST >>>>>>>>>>>>");
              logger.info("URL = "+request.url());
              logger.info("METHOD = "+request.httpMethod().name());
              logger.info("BODY = "+request.requestBody().asString());
              logger.info("HEADERS = "+request.headers().toString());
          }
      
          Response response = (Response) joinPoint.proceed();
      
          logger.info("RESPONSE <<<<<<<<<<<<<<");
          logger.info("STATUS = "+response.status());
          logger.info("HEADERS = "+response.headers().toString());
          logger.info("BODY = " + IOUtils.toString(response.body().asInputStream(), "UTF-8"));
          return response;
      }
      

      现在效果很好,我得到了我需要的所有信息。

      【讨论】:

      • 很高兴您为自己找到了这个简单的解决方案。一般来说,我们可以说,如果你想避免 AspectJ,你可以选择围绕你想要拦截的任何 3rd 类对象编写一个 Spring 组件包装器。在您的情况下,已经有一个预制组件,使其更加容易。 :-) 顺便说一句,您应该接受自己的答案才能结束此问题。
      • 根据您的回答,我解决了同样的日志记录问题。但是,当我尝试使用您的示例中的 listUsers() 方法的返回值时,即使建议正在获取响应对象,我也会得到 null 。对此有何见解? @罗纳尔多
      【解决方案3】:

      Feign 内置了无需 AOP 即可使用的日志记录。如果您创建一个feign.Logger 实例并注册它,它将记录请求、响应和标头。

      @Bean
      public feign.Logger logger() {
          /* requires feign-slf4j */
          return new Slf4jLogger();
      }
      

      Logger 实例提供以下功能:

      • 在发送到客户端之前记录请求。
      • 一旦收到响应,无论状态如何,都会记录下来。
      • 如果触发重试,则会记录它们。

      如果您只需要日志记录,这可能是一个更好的解决方案。

      【讨论】:

        【解决方案4】:

        我也面临这个问题。但我无法拦截 LoadBalancerFeignClient 类。我使用相同的代码进行测试,但它不起作用。enter image description here

        当我调试函数时,我发现(this)指向 TraceLoadBalanceFeignClient。LoadBalancerFeignClient 的子类。最后我在使用侦探时找到。 feign 客户端将由 sleuth 的 feignbuilder 创建。切入点无效

        【讨论】:

        • 检查是否在 pom.xml 文件中使用正确的依赖项来激活 spring aop abd 在 spring 属性中启用了功能区
        • 原因是侦探。如果我从我的项目中删除侦探,LoadBalancerFeignClient 类可以被拦截。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-03-17
        相关资源
        最近更新 更多