我在看2014年的一篇关于使用Spring AOP记录HTTP请求/回复的帖子:
Spring集成+http适配器(或任何端点(的日志响应时间
为此,我尝试了这种AOP配置:
<aop:config >
<aop:aspect id="myAspect" ref="inboundOutboundHttpLogging">
<aop:pointcut id="handleRequestMessageMethod"
expression="execution(* org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleRequestMessage(*))
and
args(message))" />
<aop:before method="requestMessageSent" pointcut-ref="handleRequestMessageMethod" arg-names="message"/>
</aop:aspect>
</aop:config>
是否有一种使用AOP记录HTTP请求的新方法?我想避免按请求记录日志(即每个网关上的出站网关建议(。
谢谢你的指点。
handleRequestMessage()
本质上是该网关的输入消息和输出消息。因此,如果您不喜欢实现AbstractRequestHandlerAdvice
并通过它们的<request-handler-advice-chain>
将其添加到每个网关中,那么可以考虑将<wire-tap>
用于这些网关的输入和输出通道。
不过,您可以实现BeanPostProcessor.postProcessBeforeInitialization()
,将自定义AbstractRequestHandlerAdvice
添加到您感兴趣的HTTP网关中。
我的观点是,你向我们展示的<aop:aspect>
确实可能会导致一些意想不到的行为,比如你从问题中编辑出来的final
方法问题。。。
根据@artem-bilan的建议,我找到了一个类似于AOP的解决方案,将日志AbstractRequestHandlerAdvice
注入HTTP出站请求处理中。我贡献这一点是为了向其他遇到这个问题的人展示一个可能的解决方案。
正如@artem bilan所提到的,有一种将AbstractRequestHandlerAdvice
注射到AbstractReplyProducingMessageHandler
(如HttpRequestExecutingMessageHandler
(中的机制。在我的情况下,我希望在HTTP调用之前记录消息内容(标头和有效负载(,并记录返回消息(标头和无效负载(。这很好用。
@artem-bilan建议BeanPostProcessor
机制可以允许注入建议,而不必将该声明添加到每个http出站bean。BeanPostProcessor
看起来像这样:
public class AddHttpOutboundAdvicePostProcessor implements BeanPostProcessor {
final List<Advice> adviceList;
final AddHttpOutboundAdvicePostProcessor(List<Advice> adviceList) {
this.adviceList = adviceList;
}
@Override
public Object postProcessAfterInitialization(@NonNull Object bean,
@NonNull String beanName)
throws BeansException {
if (bean instanceof AbstractHttpRequestExecutingMessageHandler) {
((AbstractHttpRequestExecutingMessageHandler) bean).setAdviceChain(adviceList);
}
return bean;
}
}
我们需要在上下文中设置这个bean。(我是一个铁杆的陈述性粉丝,因此这是用XML编写的。(
<bean id = "addHttpLoggingPostProcessor"
class = "com.my.package.AddHttpOutboundAdvicePostProcessor" >
<constructor-arg name="adviceList>
<util:list>
<ref bean="outboundLogger" />
</util:list>
</constructor-arg>
</bean>
这里,outboundLogger
是管理request-handler-advice
的bean。在我选择的实现中,我将出站消息的副本发送到一个通道以预先记录,并将响应消息的副本向下发送到另一个通道以便记录响应。bean的XML声明采用两个通道名称作为构造函数:
<bean id="outboundLogger" class="com.my.package.HttpRequestProcessorLogger" >
<constructor-arg name="requestLoggingChannelName" value="XXX" />
<constructor-arg name="responseLoggingChannelName" value="YYY" />
</bean>
其中CCD_ 17和CCD_。我已经将这些通道设置为ExecutorChannel
s,以便异步执行日志记录。
HttpRequestProcessorLogger
bean管理对handleRequestMessage()
:的调用
public class HttpRequestProcessorLogger extends AbstractRequestHandlerAdvice {
private MessageChannel requestLoggingChannel;
private MessageChannel responseLoggingChannel;
private String requestLoggingChannelName;
private String responseLoggingChannelName;
private BeanFactory beanFactory;
public HttpRequestProcessorLogger(String requestLoggingChannelName, String responseLoggingChannelName) {
this.requestLoggingChannelName = requestLoggingChannelName;
this.responseLoggingChannelName = responseLoggingChannelName;
}
@Override
protected Object doInvoke(ExecutionCallback callback, Object target, Message<?> message) {
getChannels();
requestLoggingChannel.send(message);
final Object result = callback.execute();
final message<?> outputMessage =
(MessageBuilder.class.isInstance(result) ? ((MessageBuilder<?>) result).build()
: (Message<?>) result;
responseLoggingChannel.send(outputMessage);
return outputMessage;
}
private synchronized void getChannels() {
if (requestLoggingChannelName != null) {
final DestinationResolver<MessageChannel>
channelResolver = ChannelResolverUtils.getChannelResolver(this.beanFactory);
requestLoggingChannel = channelResolver.resolverDestination(requestLoggingChannelName);
responseLoggingChannel = channelResolver.resolverDestination(responseLoggingChannelName);
requestLoggingChannelName = null;
responseLoggingChannelName = null;
}
}
@Override
public void setBeanFactory(@NonNull BeanFactory beanFactory) throws BeanException {
this.beanFactory = beanFactory;
}
}