Skip to content

Improve exception when failing to create a specific bean due to a NoClassDefFoundError [SPR-14883] #19449

Closed
@spring-projects-issues

Description

@spring-projects-issues

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:

Referenced from: commits c44c607, cf479bf, b3cd1ad, b42d731, 3d2e4c3, 37f4f43

Metadata

Metadata

Assignees

Labels

in: coreIssues in core modules (aop, beans, core, context, expression)type: enhancementA general enhancement

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions