Aqui está a aparência do meu código e não está claro como / por executorService.submit(work::get)
que isso causaria um ClassNotFoundException
erro na classe anônima em questão. Isso não acontece o tempo todo, mas, quando essa exceção é encontrada, ela não parece se recuperar - as solicitações subsequentes são atendidas com as mesmas exceções. Alguém sabe o que poderia estar causando isso?
Edição: Posso confirmar que todas as chamadas para este método funcionam, ou nenhuma, em uma sessão VM - não é como alguns suceder, enquanto outros falham devido à referida exceção.
Edição adicional: https://bugs.openjdk.java.net/browse/JDK-8148560 é exatamente o bug que estou enfrentando, mas esse foi fechado porque não era reproduzível e / ou o repórter não respondeu. De alguma forma, parece que o tipo anônimo resultante da expressão lambda é lixo coletado antes do executor executar a expressão, mas obviamente nem sempre. O jdk em uso é openjdk1.8.0_221
.
package com.ab.cde.ct.service.impl;
@Service
public class IngestionService {
@Autowired private TransactionTemplate transactionTemplate;
@Autowired private AsyncTaskExecutor executorService;
@Transactional
public void ingest(Data data) {
Supplier<Optional<String>> work = () -> transactionTemplate.execute(s -> {
// actual work on the data object, enclosed in a try/catch/finally
});
executorService.submit(work::get); // this is where the exception gets thrown
}
}
Aqui está a aparência do stacktrace de exceção (os números de linha não corresponderão, pois o código acima é apenas um protótipo):
2019-10-23 19:11:35,267|[http-apr-26001-exec-10]|[B6AC864143092042BBB4A0876BB51EB6.1]|[]|[ERROR] web.error.ErrorServlet [line:142] org.springframework.web.util.NestedServletException: Handler processing failed; nested exception is java.lang.NoClassDefFoundError: com/ab/cde/ct/service/impl/IngestionService$$Lambda$53
org.springframework.web.util.NestedServletException: Handler processing failed; nested exception is java.lang.NoClassDefFoundError: com/ab/cde/ct/service/impl/IngestionService$$Lambda$53
at org.springframework.web.servlet.DispatcherServlet.triggerAfterCompletionWithError(DispatcherServlet.java:1275)
at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:951)
at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:867)
at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:951)
at org.springframework.web.servlet.FrameworkServlet.doPost(FrameworkServlet.java:853)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:661)
at org.springframework.web.servlet.FrameworkServlet.service(FrameworkServlet.java:827)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:742)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:231)
Caused by: java.lang.NoClassDefFoundError: com/ab/cde/ct/service/impl/IngestionService$$Lambda$53
at com.ab.cde.ct.service.impl.IngestionService$$Lambda$53/812375226.get$Lambda(Unknown Source)
at com.ab.cde.ct.service.impl.IngestionService.ingest(IngestionService.java:264)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:498)
at org.springframework.aop.support.AopUtils.invokeJoinpointUsingReflection(AopUtils.java:317)
at org.springframework.aop.framework.ReflectiveMethodInvocation.invokeJoinpoint(ReflectiveMethodInvocation.java:183)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:150)
at org.springframework.transaction.interceptor.TransactionInterceptor$1.proceedWithInvocation(TransactionInterceptor.java:96)
at org.springframework.transaction.interceptor.TransactionAspectSupport.invokeWithinTransaction(TransactionAspectSupport.java:260)
at org.springframework.transaction.interceptor.TransactionInterceptor.invoke(TransactionInterceptor.java:94)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:172)
at org.springframework.aop.framework.JdkDynamicAopProxy.invoke(JdkDynamicAopProxy.java:204)
at com.sun.proxy.$Proxy252.ingest(Unknown Source)
Caused by: java.lang.ClassNotFoundException: com.ab.cde.ct.service.impl.IngestionService$$Lambda$53
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1364)
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1185)
... 115 more
@Transactional
usando também otransactionTemplate
inside?Respostas:
É o caso do método sintético gerado pelo lambda, que não consegue encontrar a classe necessária (ou seja, TransactionCallback ) e, portanto, o erro abaixo
O código específico que causa esse problema é
Para superar isso, modifique o código como abaixo
Se acima ainda não funcionar, use abaixo da solução alternativa
Informe nos comentários se mais alguma informação é necessária.
PS: Não há necessidade de
@Transactional
setransactionTemplate
está sendo usado, pois ambos servem essencialmente ao mesmo propósito.Referências:
fonte
transactionTemplate
, este método contém interações que usam os@Transactional
comportamentos padrão , bem como aqueles que usam o objeto de modelo personalizado. Eu deixei de fora todo esse código por questões de concisão.TransactionCallback
item ainda não foi carregado se esse método for o primeiro a usá-lo em uma determinada sessão da VM. Isso explica o comportamento?Eu já tive isso antes com problemas de DI e com erros de ambiguidade / problemas de configuração na resolução de pacotes. Estou supondo pela sua postagem que o erro ocorre após a inicialização bem-sucedida e exatamente na invocação dessa linha no método, e pode ser atingido no depurador.
Primeira sugestão:
Com o Gradle / Maven, verifique os pacotes dependentes para garantir que tudo tenha a versão necessária e você não está substituindo uma versão globalmente que pode afetar um pacote dependente que requer uma versão mais alta ou mais baixa dessa dependência.
Algumas frutas baixas para experimentar primeiro (se for fácil o suficiente escolher):
Em relação à injeção de dependência,
Eu recomendaria tentar algo como o seguinte .. e também é uma boa prática para injeção de dependência na primavera, pois fornece à primavera um mapa de dependência mais explícito e aumenta sua capacidade de depurar as dependências do aplicativo.
Existem algumas razões pelas quais eu recomendo:
Como essa deve ser uma definição de bean válida, desde que a classe seja incluída na varredura de componente da sua configuração, pode ser necessário definir explicitamente o bean em uma classe de configuração, especialmente se você tiver vários beans de cada tipo (o que também pode ser seu problema). )
Ex:
Observe que, para a configuração, os parâmetros para o método bean serão enviados automaticamente até a primavera, assim que esses beans forem inicializados nessa configuração ou em outra configuração. Muito legal né?
Além disso, o nome do seu bean corresponde ao nome do método aqui e, se você tiver vários beans do mesmo tipo, spring pode passar como parâmetros, pode ser necessário informar ao spring qual nome do bean usar. Para fazer isso, você utilizaria a anotação @Qualifier.
Eu realmente espero que isso ajude, ou pelo menos validar a instanciação está acontecendo corretamente.
fonte