From 4e54c25179e38c235bfba1dfca93ebe350e247c0 Mon Sep 17 00:00:00 2001 From: Liu Dongmiao Date: Fri, 22 Jan 2021 02:33:26 +0800 Subject: [PATCH] Fail early FactoryBean instantiation for LinkageError In 0288878 to resolve gh-22409, a little bug was introduced: if there is LinkageError in FactoryBean instantiation, no first exception. In JVM, if a class cannot be initialized, it acts like this: - at the first time, it will show the real reason and stack - then, only show "NoClassDefFoundError: Could not initialize class xxx" --- .../AbstractAutowireCapableBeanFactory.java | 8 +++ ...gressiveFactoryBeanInstantiationTests.java | 64 +++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java index 4defe323d461..e018ab6f76b3 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/AbstractAutowireCapableBeanFactory.java @@ -1022,6 +1022,14 @@ private FactoryBean getSingletonFactoryBeanForTypeCheck(String beanName, Root throw ex; } catch (BeanCreationException ex) { + // LinkageError is unresolvable + Throwable cause = ex.getCause(); + while (cause != null) { + if (cause instanceof LinkageError) { + throw ex; + } + cause = cause.getCause(); + } // Instantiation failure, maybe too early... if (logger.isDebugEnabled()) { logger.debug("Bean creation exception on singleton FactoryBean type check: " + ex); diff --git a/spring-context/src/test/java/org/springframework/context/annotation/AggressiveFactoryBeanInstantiationTests.java b/spring-context/src/test/java/org/springframework/context/annotation/AggressiveFactoryBeanInstantiationTests.java index 9bec24609797..76455af7a9e5 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/AggressiveFactoryBeanInstantiationTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/AggressiveFactoryBeanInstantiationTests.java @@ -16,12 +16,19 @@ package org.springframework.context.annotation; +import java.io.ByteArrayOutputStream; +import java.io.PrintWriter; + import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.BeanCreationException; import org.springframework.beans.factory.BeanFactoryUtils; import org.springframework.beans.factory.FactoryBean; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.ApplicationContext; +import static org.assertj.core.api.Assertions.assertThat; + /** * @author Andy Wilkinson */ @@ -49,6 +56,23 @@ public void beanMethodFactoryBean() { } } + @Test + public void beanMethodFactoryBeanWithError() { + try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) { + context.register(BeanMethodConfigurationWithError.class, StringTypeConfiguration.class); + try { + context.refresh(); + } catch (BeanCreationException ex) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + PrintWriter pw = new PrintWriter(baos); + ex.printStackTrace(pw); + pw.flush(); + String stackTrace = baos.toString(); + assertThat(stackTrace.contains(SimpleLinkageErrorClass.class.getSimpleName() + ".")).isTrue(); + } + } + } + @Configuration static class BeanMethodConfiguration { @@ -60,6 +84,33 @@ public SimpleFactoryBean simpleFactoryBean(ApplicationContext applicationContext } + @Configuration + static class BeanMethodConfigurationWithError { + + @Bean + public SimpleFactoryBean simpleFactoryBean(ApplicationContext applicationContext) { + SimpleFactoryBean simpleFactoryBean = new SimpleFactoryBean(applicationContext); + // cause a linkage error + new SimpleLinkageErrorClass(); + return simpleFactoryBean; + } + } + + + @Configuration + static class StringTypeConfiguration { + + @Autowired + private String foo; + + @Bean + public String foo() { + return "foo"; + } + + } + + static class SimpleFactoryBean implements FactoryBean { public SimpleFactoryBean(ApplicationContext applicationContext) { @@ -76,4 +127,17 @@ public Class getObjectType() { } } + + static class SimpleLinkageErrorClass { + + private static final int ERROR = checkError(); + + public SimpleLinkageErrorClass() { + } + + private static int checkError() { + throw new NoSuchMethodError(); + } + } + }