Description
Antonio Anzivino opened SPR-14883 and commented
I am currently stuck in determining the cause of a NoClassDefFoundError that is occurring in my project.
I am reporting this ticket to request an improvement, and to discuss how it should be implemented.
My specific problem (which I can't diagnose on my own) is a NoClassDefFoundError
java.lang.NoClassDefFoundError: WebJarAssetLocator
at java.lang.Class.getDeclaredMethods0(Native Method) ~[?:1.7.0_79]
at java.lang.Class.privateGetDeclaredMethods(Class.java:2615) ~[?:1.7.0_79]
at java.lang.Class.getDeclaredMethods(Class.java:1860) ~[?:1.7.0_79]
at org.springframework.util.ReflectionUtils.getDeclaredMethods(ReflectionUtils.java:612) ~[spring-core-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.util.ReflectionUtils.doWithMethods(ReflectionUtils.java:524) ~[spring-core-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.util.ReflectionUtils.doWithMethods(ReflectionUtils.java:510) ~[spring-core-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.determineCandidateConstructors(AutowiredAnnotationBeanPostProcessor.java:243) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.determineConstructorsFromBeanPostProcessors(AbstractAutowireCapableBeanFactory.java:1074) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1047) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:510) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:751) ~[spring-beans-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:861) ~[spring-context-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.__refresh(AbstractApplicationContext.java:541) ~[spring-context-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java) ~[spring-context-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:444) ~[spring-web-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:326) [spring-web-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:107) [spring-web-4.3.3.RELEASE.jar:4.3.3.RELEASE]
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:5099) [catalina.jar:7.0.70]
at org.apache.catalina.core.StandardContext.startInternal(StandardContext.java:5615) [catalina.jar:7.0.70]
at org.apache.catalina.util.LifecycleBase.start(LifecycleBase.java:147) [catalina.jar:7.0.70]
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1571) [catalina.jar:7.0.70]
at org.apache.catalina.core.ContainerBase$StartChild.call(ContainerBase.java:1561) [catalina.jar:7.0.70]
at java.util.concurrent.FutureTask.run(FutureTask.java:262) [?:1.7.0_79]
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1145) [?:1.7.0_79]
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:615) [?:1.7.0_79]
at java.lang.Thread.run(Thread.java:745) [?:1.7.0_79]
Caused by: java.lang.ClassNotFoundException: WebJarAssetLocator
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1891) ~[catalina.jar:7.0.70]
at org.apache.catalina.loader.WebappClassLoaderBase.loadClass(WebappClassLoaderBase.java:1734) ~[catalina.jar:7.0.70]
... 31 more
From my understanding, some bean contains an @Autowired
dependency for which the autowired bean contains a constructor dependency with WebJarAssertLocator. I am not willing to inlude WebJarAssetLocator in my classpath, I need to identify the component dependent to that class in order to remove its dependency from webjars package.
Now the problem is that this stack trace does not provide information on what bean caused the failure. I could get more information if I had either the name of the bean that contains the faulty @Autowired
definition or else the name of the bean that is faulty to instantiate as an @Autowired
bean.
My proposal is for Spring Framework to increase the number of catch(Throwable) statements, and log little extra contextual information when these Throwables occur.
A simple, but probably insufficient and too simplicistic, proposal for which I could create a PR easily, is to wrap the entire doGetBean method in the following:
@SuppressWarnings("unchecked")
protected <T> T doGetBean(
final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
throws BeansException {
try{
---- old code
}catch(Throwable ex){
throw new BeansException("Unable to get bean: " + name + " of required type " + requiredType, ex);
}
}
Another alternate proposal, that follows a pattern I like a lot to apply, is the following (DefaultListableBeanFactory#731):
for(String beanName: beanNames)
try{
---old code
}catch(Throwable ex){
throw new BeansException("Unable to init singleton bean "+beanName, ex);
}
The pattern is: when performing a loop, identify the element that caused an exception and throw its identifying information in a new exception that wraps the cause. This helps diagnosis.
This is meant to provide discussion and improvement. I will have to debug my specific error on my own, but I would like to see this (especially the second pattern) implemented in future releases
Affects: 4.3.3
Issue Links:
- PersistenceAnnotationBeanPostProcessor.findPersistenceMetadata(PersistenceAnnotationBeanPostProcessor.java:392) too aggressive, resulting in NoClassDefFoundError [SPR-12461] #17066 PersistenceAnnotationBeanPostProcessor.findPersistenceMetadata(PersistenceAnnotationBeanPostProcessor.java:392) too aggressive, resulting in NoClassDefFoundError
- Improve failure metadata that is available from BeanInstantiationException [SPR-14166] #18738 Improve failure metadata that is available from BeanInstantiationException
Referenced from: commits c44c607, cf479bf, b3cd1ad, b42d731, 3d2e4c3, 37f4f43