Я обновляю наше существующее приложение Spring Boot 1.5 до Spring Boot 2.0. Приложение также использует XML-файлы Spring Integration. Компонент Ehcache вызывает проблемы при попытке выполнить это. При попытке добавить ответ на вызов GET в кеш возникает исключение:
Servlet.service() for servlet [dispatcherServlet] in context with path [] threw exception [Request processing failed; nested exception is org.springframework.messaging.MessageHandlingException: error occurred in message handler [httpGatewayLookupSize]; nested exception is net.sf.ehcache.CacheException: When configured copyOnRead or copyOnWrite, a Store will only accept Serializable values, failedMessage=GenericMessage [payload = {}, headers = {http_requestMethod=GET .....]] with root cause
java.io.NotSerializableException: org.springframework.integration.support.MessageBuilder
С Spring Boot 1.5 он отлично работает, а значение (XML-ответ), вставленное в кэш, имеет тип GenericMessage. Ниже приведена вся трассировка стека:
java.io.NotSerializableException: org.springframework.integration.support.MessageBuilder
at java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1184)
at java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:348)
at net.sf.ehcache.store.compound.ReadWriteSerializationCopyStrategy.copyForWrite(ReadWriteSerializationCopyStrategy.java:58)
at net.sf.ehcache.store.compound.ReadWriteSerializationCopyStrategy.copyForWrite(ReadWriteSerializationCopyStrategy.java:35)
at net.sf.ehcache.store.CopyStrategyHandler.copyElementForWriteIfNeeded(CopyStrategyHandler.java:84)
at net.sf.ehcache.store.AbstractCopyingCacheStore.put(AbstractCopyingCacheStore.java:78)
at net.sf.ehcache.store.CopyingCacheStore.put(CopyingCacheStore.java:29)
at net.sf.ehcache.Cache.putInternal(Cache.java:1616)
at net.sf.ehcache.Cache.put(Cache.java:1542)
at net.sf.ehcache.Cache.put(Cache.java:1507)
at org.springframework.cache.ehcache.EhCacheCache.put(EhCacheCache.java:128)
at org.springframework.cache.interceptor.AbstractCacheInvoker.doPut(AbstractCacheInvoker.java:87)
at org.springframework.cache.interceptor.CacheAspectSupport$CachePutRequest.apply(CacheAspectSupport.java:783)
at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:400)
at org.springframework.cache.interceptor.CacheAspectSupport.execute(CacheAspectSupport.java:316)
at org.springframework.cache.interceptor.CacheInterceptor.invoke(CacheInterceptor.java:61)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:185)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:212)
at com.sun.proxy.$Proxy96.handleRequestMessage(Unknown Source)
at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.doInvokeAdvisedRequestHandler(AbstractReplyProducingMessageHandler.java:127)
at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:112)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:158)
at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116)
at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:132)
at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:105)
at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:73)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:445)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:394)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:181)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:160)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:47)
at org.springframework.messaging.core.AbstractMessageSendingTemplate.send(AbstractMessageSendingTemplate.java:108)
at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutput(AbstractMessageProducingHandler.java:426)
at org.springframework.integration.handler.AbstractMessageProducingHandler.produceOutput(AbstractMessageProducingHandler.java:336)
at org.springframework.integration.handler.AbstractMessageProducingHandler.sendOutputs(AbstractMessageProducingHandler.java:227)
at org.springframework.integration.handler.AbstractReplyProducingMessageHandler.handleMessageInternal(AbstractReplyProducingMessageHandler.java:115)
at org.springframework.integration.handler.AbstractMessageHandler.handleMessage(AbstractMessageHandler.java:158)
at org.springframework.integration.dispatcher.AbstractDispatcher.tryOptimizedDispatch(AbstractDispatcher.java:116)
at org.springframework.integration.dispatcher.UnicastingDispatcher.doDispatch(UnicastingDispatcher.java:132)
at org.springframework.integration.dispatcher.UnicastingDispatcher.dispatch(UnicastingDispatcher.java:105)
at org.springframework.integration.channel.AbstractSubscribableChannel.doSend(AbstractSubscribableChannel.java:73)
at org.springframework.integration.channel.AbstractMessageChannel.send(AbstractMessageChannel.java:445)
at org.springframework.messaging.core.GenericMessagingTemplate.doSend(GenericMessagingTemplate.java:181)
at org.springframework.messaging.core.GenericMessagingTemplate.doSendAndReceive(GenericMessagingTemplate.java:227)
at org.springframework.messaging.core.GenericMessagingTemplate.doSendAndReceive(GenericMessagingTemplate.java:47)
at org.springframework.messaging.core.AbstractMessagingTemplate.sendAndReceive(AbstractMessagingTemplate.java:45)
at org.springframework.integration.core.MessagingTemplate.sendAndReceive(MessagingTemplate.java:97)
at org.springframework.integration.gateway.MessagingGatewaySupport.doSendAndReceive(MessagingGatewaySupport.java:497)
at org.springframework.integration.gateway.MessagingGatewaySupport.sendAndReceiveMessage(MessagingGatewaySupport.java:465)
at org.springframework.integration.http.inbound.HttpRequestHandlingEndpointSupport.actualDoHandleRequest(HttpRequestHandlingEndpointSupport.java:363)
at org.springframework.integration.http.inbound.HttpRequestHandlingEndpointSupport.doHandleRequest(HttpRequestHandlingEndpointSupport.java:255)
at org.springframework.integration.http.inbound.HttpRequestHandlingMessagingGateway.handleRequest(HttpRequestHandlingMessagingGateway.java:105)
at org.springframework.web.servlet.mvc.HttpRequestHandlerAdapter.handle(HttpRequestHandlerAdapter.java:53)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:991)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:925)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:974)
at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:866)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:635)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:851)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.tomcat.websocket.server.WsFilter.doFilter(WsFilter.java:52)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.boot.actuate.web.trace.servlet.HttpTraceFilter.doFilterInternal(HttpTraceFilter.java:90)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:99)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.HttpPutFormContentFilter.doFilterInternal(HttpPutFormContentFilter.java:109)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.HiddenHttpMethodFilter.doFilterInternal(HiddenHttpMethodFilter.java:93)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.filterAndRecordMetrics(WebMvcMetricsFilter.java:155)
at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.filterAndRecordMetrics(WebMvcMetricsFilter.java:123)
at org.springframework.boot.actuate.metrics.web.servlet.WebMvcMetricsFilter.doFilterInternal(WebMvcMetricsFilter.java:108)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.springframework.web.filter.CharacterEncodingFilter.doFilterInternal(CharacterEncodingFilter.java:200)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:107)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:193)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:166)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:198)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:96)
at org.apache.catalina.authenticator.AuthenticatorBase.invoke(AuthenticatorBase.java:493)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:140)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:81)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:87)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:342)
at org.apache.coyote.http11.Http11Processor.service(Http11Processor.java:800)
at org.apache.coyote.AbstractProcessorLight.process(AbstractProcessorLight.java:66)
at org.apache.coyote.AbstractProtocol$ConnectionHandler.process(AbstractProtocol.java:806)
at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.doRun(NioEndpoint.java:1498)
at org.apache.tomcat.util.net.SocketProcessorBase.run(SocketProcessorBase.java:49)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1149)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:624)
at org.apache.tomcat.util.threads.TaskThread$WrappingRunnable.run(TaskThread.java:61)
at java.lang.Thread.run(Thread.java:748)
Нужно ли мне явно указывать ответ, чтобы это сработало?
CacheInterceptor реализация, которая у меня есть:
private MethodInterceptor createGatewayInterceptor(String cacheName) {
CacheInterceptor interceptor = new CacheInterceptor();
interceptor.setCacheManager(cacheManager());
NameMatchCacheOperationSource source = new NameMatchCacheOperationSource();
CacheableOperation.Builder operationBuilder = new CacheableOperation.Builder();
operationBuilder.setCacheName(cacheName);
source.addCacheMethod("handle*Message", Collections.singleton(operationBuilder.build()));
interceptor.setCacheOperationSources(source);
interceptor.setKeyGenerator((target, method, params) -> ((Message) params[0]).getHeaders().get(HEADER_CACHEKEY));
return interceptor;
}
Реализация кэша DSL:
adviceChain.add(eternalCacheAdvice);
adviceChain.add(genericMessageAdvice);
handler.setAdviceChain(adviceChain);
return handler;
@Bean
public MethodInterceptor cacheAdvice() {
return createGatewayInterceptor(TWENTY_FOUR_HOUR_CACHE);
}
@Bean
public MethodInterceptor eternalCacheAdvice() {
return createGatewayInterceptor(ETERNAL_CACHE);
}
@Bean
public MethodInterceptor genericMessageAdvice(){
return invocation -> ((MessageBuilder<?>) invocation.proceed()).build();
}
@Bean
public MethodInterceptor genericXMLMessageAdvice(){
return invocation -> ((MessageBuilder<?>) invocation.proceed()).build();
}
Цепочка рекомендаций XML:
<int-http:request-handler-advice-chain>
<ref bean = "genericXMLMessageAdvice" />
<ref bean = "cacheAdvice" />
</int-http:request-handler-advice-chain>




Для некоторой внутренней оптимизации многие готовые компоненты Spring Integration теперь возвращают MessageBuilder перед отправкой ответов. Это необходимо для обогащения целевого ответного сообщения дополнительными заголовками.
Похоже, что объявление CacheInterceptor выполняет invocation.proceed() попытку использовать результат как есть для кэширования.
Подумайте о том, чтобы добавить еще один MethodInterceptorдо, настроенный вами CacheInterceptor, с помощью простой реализации, подобной этой:
invocation -> ((MessageBuilder<?>) invocation.proceed()).build()
Итак, ваш CacheInterceptor получит ожидаемый GenericMessage<?> как и прежде.
Кстати, у нас есть PR о кэшировании этой интеграции Spring: https://github.com/spring-projects/spring-integration/pull/2105. Не стесняйтесь делиться там своим опытом, так что это может подтолкнуть к готовой поддержке.
По вашему вопросу не ясно, какой тип конфигурации вы используете, но для XML это `<int:request-handler-advice-chain>`, а bean-компоненты или их ссылки, объявленные как вложенные элементы, обрабатываются в том порядке, в котором вы их объявляете. .
@ServiceActivator, как и многие другие аннотации обмена сообщениями, имеет adviceChain, который представляет собой массив имен компонентов, которые должны обрабатываться в порядке их объявления.
В случае Java DSL у ConsumerEndpointSpec есть опция конфигурации advice(Advice... advice), позволяющая обрабатывать vararg в том порядке, в котором они объявлены здесь.
Итак, в любом случае вам нужно объявить MethodInterceptors и иметь этот CacheInterceptor в порядке, уже после перехватчика с явным созданием Message.
поэтому у меня есть комбинация конфигураций DSL и xml. Я добавил это к вопросу. Когда у меня так, кэширование для варианта DSL работает нормально, но вариант xml дает ту же сериализуемую ошибку. Я даже пытался поменять порядок советов в конфигурации xml, но все равно выдает ту же ошибку.
Какой у тебя genericXMLMessageAdvice?
Есть ли шансы, что вы можете поделиться каким-нибудь простым проектом, чтобы поиграть с ним?
О, да! Совет кэша должен быть первым в стеке. Вы действительно кэшируете результат вызова, так что он должен быть преобразован после вызова.
genericXMLMessageAdvice точно такой же, как genericMessageAdvice. Я добавил это тоже здесь. Я попытался сделать cacheAdvice первым в стеке, но все равно получаю ту же ошибку сериализации.
Да... нам нужен образец для игры. Что-то может быть упущено. Вы уверены, что даете новый совет везде, где есть кеширующий?
да, это одна вещь, которую мне нужно сделать и проверить... в настоящее время у нас есть 20 XML-файлов, которые используют cacheConfig. Я только что изменил один из этих XML-файлов и попытался проверить этот конкретный вызов и его сбой. Я попытаюсь изменить все xmls (что я в любом случае буду делать) и добавить этот дополнительный совет по каждому из них, а затем протестировать.
Ага, так оно и было... дополнительные советы требовались во всех местах, где вызывался cacheAdvice. Спасибо @Артем!!
Спасибо @Artem, а как связать 2
MethodInterceptorподряд? Я добавил свою реализациюCacheInterceptorк вопросу выше.