Así es como se ve mi código, y no está claro cómo / por executorService.submit(work::get)
qué arrojaría un ClassNotFoundException
sobre la clase anónima en cuestión. No sucede todo el tiempo, pero una vez que se encuentra esta excepción, no parece recuperarse; las solicitudes posteriores se cumplen con las mismas excepciones. ¿Alguien sabe qué podría estar causando que esto ocurra?
EDITAR: Puedo confirmar que todas las llamadas a este método funcionan, o ninguna funciona, en una sesión de VM: no es como que algunas tengan éxito mientras que otras fallan debido a dicha excepción.
Edición adicional: https://bugs.openjdk.java.net/browse/JDK-8148560 es exactamente el error que estoy experimentando, pero ese estaba cerrado ya que no era reproducible y / o el periodista no respondió. De alguna manera, parece que el tipo anónimo resultante de la expresión lambda es basura recolectada antes de que el ejecutor ejecute la expresión, pero obviamente no siempre. El jdk en uso es 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
}
}
Así es como se ve la excepción stacktrace (los números de línea no se corresponden ya que el código anterior es solo un prototipo):
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 eltransactionTemplate
inside?Respuestas:
Este es el caso del método sintético generado por lambda que no puede encontrar la clase requerida (es decir, TransactionCallback ) y, por lo tanto, el siguiente error
El código particular que causa este problema es
Para superar esto, modifique el código de la siguiente manera
Si lo anterior aún no funciona, use la solución alternativa debajo
Háganos saber en los comentarios si se requiere más información.
PD: No es necesario
@Transactional
sitransactionTemplate
se está utilizando, ya que ambos sirven esencialmente para el mismo propósito.Referencias
fuente
transactionTemplate
, este método contiene interacciones que usan los@Transactional
comportamientos predeterminados , así como aquellos que usan el objeto de plantilla personalizada. Dejé todo ese código por brevedad.TransactionCallback
todavía no está cargado si este método es el primero que lo usa en una sesión de VM determinada. ¿Eso explica el comportamiento?He tenido esto antes con problemas de DI y con errores de ambigüedad / problemas de configuración en la resolución del paquete. Supongo por su publicación que el error ocurre después de un inicio exitoso, y exactamente al invocar esa línea en el método, y puede ser golpeado en el depurador.
Primera sugerencia:
Con Gradle / Maven, verifique los paquetes dependientes para asegurarse de que todo tenga la versión que necesita, y no anule una versión global que pueda afectar a un paquete dependiente que requiera una versión superior o inferior de esa dependencia.
Algunas frutas bajas para probar primero (si es lo suficientemente fácil de recoger):
En cuanto a la inyección de dependencia,
Recomendaría probar algo como lo siguiente ... y también es una buena práctica para la inyección de dependencias en primavera, ya que le da a Spring un mapa de dependencias más explícito y aumenta su capacidad para depurar las dependencias de la aplicación.
Hay algunas razones por las que recomiendo esto:
Como esta debería ser una definición de bean válida siempre que la clase esté incluida en el escaneo de componentes de su configuración, es posible que necesite definir el bean explícitamente en una clase de configuración, especialmente si tiene múltiples beans de cada tipo (lo que también podría ser su problema )
Ex:
Tenga en cuenta que para la configuración, los parámetros para el método de bean se enviarán en primavera automáticamente una vez que esos beans se hayan inicializado dentro de esta configuración u otra configuración. Genial eh?
Además, el nombre de su bean corresponde con el nombre del método aquí, y si tiene múltiples beans del mismo tipo que spring podría pasar como parámetros, es posible que necesite decirle a spring qué nombre de bean usar. Para hacer esto, utilizaría la anotación @Qualifier.
Realmente espero que esto ayude, o al menos validar la instanciación está sucediendo correctamente.
fuente