Ehcache выдает NotSerializableException при переходе на Spring boot 2

Я обновляю наше существующее приложение 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>
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Поднятие тревоги для долго выполняющихся методов в Spring Boot
Приходилось ли вам сталкиваться с требованиями, в которых вас могли попросить поднять тревогу или выдать ошибку, когда метод Java занимает больше...
Версия Java на основе версии загрузки
Версия Java на основе версии загрузки
Если вы зайдете на официальный сайт Spring Boot , там представлен start.spring.io , который упрощает создание проектов Spring Boot, как показано ниже.
Документирование API с помощью Swagger на Springboot
Документирование API с помощью Swagger на Springboot
В предыдущей статье мы уже узнали, как создать Rest API с помощью Springboot и MySql .
1
0
354
1
Перейти к ответу Данный вопрос помечен как решенный

Ответы 1

Ответ принят как подходящий

Для некоторой внутренней оптимизации многие готовые компоненты 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. Не стесняйтесь делиться там своим опытом, так что это может подтолкнуть к готовой поддержке.

Спасибо @Artem, а как связать 2 MethodInterceptor подряд? Я добавил свою реализацию CacheInterceptor к вопросу выше.

user5758361 12.03.2019 01:06

По вашему вопросу не ясно, какой тип конфигурации вы используете, но для XML это `<int:request-handler-advice-chain>`, а bean-компоненты или их ссылки, объявленные как вложенные элементы, обрабатываются в том порядке, в котором вы их объявляете. .

Artem Bilan 12.03.2019 16:42

@ServiceActivator, как и многие другие аннотации обмена сообщениями, имеет adviceChain, который представляет собой массив имен компонентов, которые должны обрабатываться в порядке их объявления.

Artem Bilan 12.03.2019 16:43

В случае Java DSL у ConsumerEndpointSpec есть опция конфигурации advice(Advice... advice), позволяющая обрабатывать vararg в том порядке, в котором они объявлены здесь.

Artem Bilan 12.03.2019 16:44

Итак, в любом случае вам нужно объявить MethodInterceptors и иметь этот CacheInterceptor в порядке, уже после перехватчика с явным созданием Message.

Artem Bilan 12.03.2019 16:45

поэтому у меня есть комбинация конфигураций DSL и xml. Я добавил это к вопросу. Когда у меня так, кэширование для варианта DSL работает нормально, но вариант xml дает ту же сериализуемую ошибку. Я даже пытался поменять порядок советов в конфигурации xml, но все равно выдает ту же ошибку.

user5758361 12.03.2019 19:23

Какой у тебя genericXMLMessageAdvice?

Artem Bilan 12.03.2019 19:28

Есть ли шансы, что вы можете поделиться каким-нибудь простым проектом, чтобы поиграть с ним?

Artem Bilan 12.03.2019 19:30

О, да! Совет кэша должен быть первым в стеке. Вы действительно кэшируете результат вызова, так что он должен быть преобразован после вызова.

Artem Bilan 12.03.2019 19:35

genericXMLMessageAdvice точно такой же, как genericMessageAdvice. Я добавил это тоже здесь. Я попытался сделать cacheAdvice первым в стеке, но все равно получаю ту же ошибку сериализации.

user5758361 12.03.2019 19:57

Да... нам нужен образец для игры. Что-то может быть упущено. Вы уверены, что даете новый совет везде, где есть кеширующий?

Artem Bilan 12.03.2019 20:06

да, это одна вещь, которую мне нужно сделать и проверить... в настоящее время у нас есть 20 XML-файлов, которые используют cacheConfig. Я только что изменил один из этих XML-файлов и попытался проверить этот конкретный вызов и его сбой. Я попытаюсь изменить все xmls (что я в любом случае буду делать) и добавить этот дополнительный совет по каждому из них, а затем протестировать.

user5758361 12.03.2019 20:12

Ага, так оно и было... дополнительные советы требовались во всех местах, где вызывался cacheAdvice. Спасибо @Артем!!

user5758361 12.03.2019 20:29

Другие вопросы по теме