Spring AOP 不拦截 Feign.Client 调用
Spring AOP does not intercept Feign.Client calls
我正在尝试使用 Spring AOP 拦截 Feign.Client
调用并将请求和响应记录到我的 Splunk 服务器。我的项目包中的所有方法都按照我的预期被拦截,但 Feign.Client
没有。
这是我的 AOP class:
@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();
}
Spring AOP 仅适用于 Spring 组件。我的猜测是它不起作用,因为 Feign 不是 Spring 组件,因此超出了 Spring AOP 的范围。如果您需要将方面应用于 non-Spring 类,只需使用完整的 AspectJ。 Spring 手册解释了如何 configure it via LTW (load-time weaving).
@kriegaex 是正确的,我不能在 non-Spring 组件中应用 AOP。但我不想使用纯 AspectJ,所以我使用了另一个解决方案。以下是我解决问题的步骤:
1) 启用 Spring Cloud Ribbon 我得到了由 spring 管理的 class LoadBalancerFeignClient
实现了 feign.Client
,所以我在 [= =34=] 并更改了我的 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
class 我截取了 LoadBalancerFeignClient
class:
@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;
}
现在效果很好,我得到了我需要的所有信息。
我也面临这个问题。但我无法拦截 LoadBalancerFeignClient class。我使用相同的代码进行测试,但它不起作用。enter image description here
当我调试函数时,我发现 (this) 指向 TraceLoadBalanceFeignClient.The LoadBalancerFeignClient 的 subclass.finally 我在使用 sleuth 时发现。假装客户端将由 sleuth 的 feignbuilder.The 切入点创建,将无效
Feign 内置了无需 AOP 即可使用的日志记录。如果您创建一个 feign.Logger
实例并注册它,它将记录请求、响应和 headers。
@Bean
public feign.Logger logger() {
/* requires feign-slf4j */
return new Slf4jLogger();
}
Logger
个实例提供以下功能:
- 请求在发送到客户端之前被记录下来。
- 收到回复后,无论状态如何,都会记录回复。
- 如果重试被触发,则会记录。
如果您只需要日志记录,这可能是更好的解决方案。
我有一个方法可以在拦截任何假客户端时获取有用的信息,我希望它能帮助别人。
@Before("@within(org.springframework.cloud.openfeign.FeignClient)")
public void logBeforeService(JoinPoint joinPoint) {
final MethodMetadata methodMetadata = new SpringMvcContract().parseAndValidateMetadata(
joinPoint.getSignature().getDeclaringType(),
((MethodSignature) joinPoint.getSignature()).getMethod());
String requestUrl = methodMetadata.template().url();
String methodName = methodMetadata.template().method();
log.info("RequestUrl: {}", requestUrl);
log.info("HttpMethod: {}", methodName);
}
我正在尝试使用 Spring AOP 拦截 Feign.Client
调用并将请求和响应记录到我的 Splunk 服务器。我的项目包中的所有方法都按照我的预期被拦截,但 Feign.Client
没有。
这是我的 AOP class:
@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();
}
Spring AOP 仅适用于 Spring 组件。我的猜测是它不起作用,因为 Feign 不是 Spring 组件,因此超出了 Spring AOP 的范围。如果您需要将方面应用于 non-Spring 类,只需使用完整的 AspectJ。 Spring 手册解释了如何 configure it via LTW (load-time weaving).
@kriegaex 是正确的,我不能在 non-Spring 组件中应用 AOP。但我不想使用纯 AspectJ,所以我使用了另一个解决方案。以下是我解决问题的步骤:
1) 启用 Spring Cloud Ribbon 我得到了由 spring 管理的 class LoadBalancerFeignClient
实现了 feign.Client
,所以我在 [= =34=] 并更改了我的 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
class 我截取了 LoadBalancerFeignClient
class:
@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;
}
现在效果很好,我得到了我需要的所有信息。
我也面临这个问题。但我无法拦截 LoadBalancerFeignClient class。我使用相同的代码进行测试,但它不起作用。enter image description here
当我调试函数时,我发现 (this) 指向 TraceLoadBalanceFeignClient.The LoadBalancerFeignClient 的 subclass.finally 我在使用 sleuth 时发现。假装客户端将由 sleuth 的 feignbuilder.The 切入点创建,将无效
Feign 内置了无需 AOP 即可使用的日志记录。如果您创建一个 feign.Logger
实例并注册它,它将记录请求、响应和 headers。
@Bean
public feign.Logger logger() {
/* requires feign-slf4j */
return new Slf4jLogger();
}
Logger
个实例提供以下功能:
- 请求在发送到客户端之前被记录下来。
- 收到回复后,无论状态如何,都会记录回复。
- 如果重试被触发,则会记录。
如果您只需要日志记录,这可能是更好的解决方案。
我有一个方法可以在拦截任何假客户端时获取有用的信息,我希望它能帮助别人。
@Before("@within(org.springframework.cloud.openfeign.FeignClient)")
public void logBeforeService(JoinPoint joinPoint) {
final MethodMetadata methodMetadata = new SpringMvcContract().parseAndValidateMetadata(
joinPoint.getSignature().getDeclaringType(),
((MethodSignature) joinPoint.getSignature()).getMethod());
String requestUrl = methodMetadata.template().url();
String methodName = methodMetadata.template().method();
log.info("RequestUrl: {}", requestUrl);
log.info("HttpMethod: {}", methodName);
}