diff --git a/spring-beans/src/main/java/org/springframework/beans/AbstractPropertyAccessor.java b/spring-beans/src/main/java/org/springframework/beans/AbstractPropertyAccessor.java index 1d6b5f48eab9..41bf5d4cffac 100644 --- a/spring-beans/src/main/java/org/springframework/beans/AbstractPropertyAccessor.java +++ b/spring-beans/src/main/java/org/springframework/beans/AbstractPropertyAccessor.java @@ -116,6 +116,14 @@ public void setPropertyValues(PropertyValues pvs, boolean ignoreUnknown, boolean // Otherwise, just ignore it and continue... } catch (PropertyAccessException ex) { + // never suppress unresolvable LinkageError + Throwable cause = ex.getCause(); + while (cause != null) { + if (cause instanceof LinkageError) { + throw ex; + } + cause = cause.getCause(); + } if (propertyAccessExceptions == null) { propertyAccessExceptions = new ArrayList<>(); } diff --git a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java index 0a81aef4e9fe..791d4a9a7229 100644 --- a/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java +++ b/spring-beans/src/main/java/org/springframework/beans/factory/support/DefaultSingletonBeanRegistry.java @@ -274,6 +274,16 @@ public Object getSingleton(String beanName, ObjectFactory singletonFactory) { * @see BeanCreationException#getRelatedCauses() */ protected void onSuppressedException(Exception ex) { + // never suppress unresolvable LinkageError during bean creation + if (BeanCreationException.class.equals(ex.getClass())) { + Throwable cause = ex.getCause(); + while (cause != null) { + if (cause instanceof LinkageError) { + throw (BeanCreationException) ex; + } + cause = cause.getCause(); + } + } synchronized (this.singletonObjects) { if (this.suppressedExceptions != null && this.suppressedExceptions.size() < SUPPRESSED_EXCEPTIONS_LIMIT) { this.suppressedExceptions.add(ex); diff --git a/spring-context/src/test/java/org/springframework/context/annotation/configuration/ExceptionInInitializerTests.java b/spring-context/src/test/java/org/springframework/context/annotation/configuration/ExceptionInInitializerTests.java new file mode 100644 index 000000000000..aa814c2bbdee --- /dev/null +++ b/spring-context/src/test/java/org/springframework/context/annotation/configuration/ExceptionInInitializerTests.java @@ -0,0 +1,154 @@ +/* + * Copyright 2002-2021 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.context.annotation.configuration; + +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.FactoryBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.annotation.AnnotationConfigApplicationContext; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.context.support.ClassPathXmlApplicationContext; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.fail; + +/** + * @author Liu Dongmiao + */ +public class ExceptionInInitializerTests { + + @Test + public void checkXml() { + try (ClassPathXmlApplicationContext ignored = new ClassPathXmlApplicationContext("ExceptionInInitializerTests.xml", getClass())) { + fail("shouldn't happen"); + } catch (BeanCreationException ex) { + checkBeanCreationException(ex); + } + } + + @Test + public void checkAnnotation() { + try (AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext()) { + context.register(BeanMethodConfiguration.class); + context.refresh(); + fail("shouldn't happen"); + } catch (BeanCreationException ex) { + checkBeanCreationException(ex); + } + } + + private static void checkBeanCreationException(BeanCreationException ex) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(); + PrintWriter pw = new PrintWriter(baos); + ex.printStackTrace(pw); + pw.flush(); + String stackTrace = baos.toString(); + assertThat(stackTrace.contains(".")).isTrue(); + assertThat(stackTrace.contains("java.lang.NoClassDefFoundError")).isFalse(); + } + + + @Configuration + static class BeanMethodConfiguration { + + @Bean + public String foo() { + return "foo"; + } + + @Bean + public AutowiredBean autowiredBean() { + return new AutowiredBean(); + } + + @Bean + public SimpleFactoryBean simpleFactoryBean() { + SimpleFactoryBean bean = new SimpleFactoryBean(); + bean.setObjectType(SimpleExceptionInInitializer2.class); + return bean; + } + } + + + static class AutowiredBean { + + protected String foo; + + @Autowired + public void setFoo(String foo) { + this.foo = foo; + } + } + + + static class SimpleBean { + + protected Class objectType; + + protected Object object; + + public void setObjectType(Class objectType) { + this.objectType = objectType; + try { + this.object = objectType.newInstance(); + } catch (ReflectiveOperationException ex) { + throw new RuntimeException(ex); + } + } + } + + + static class SimpleFactoryBean extends SimpleBean implements FactoryBean { + + @Override + public Object getObject() { + return object; + } + + @Override + public Class getObjectType() { + return objectType; + } + } + + + static class SimpleExceptionInInitializer1 { + + private static final int ERROR = callInClinit(); + + private static int callInClinit() { + throw new UnsupportedOperationException(); + } + } + + + static class SimpleExceptionInInitializer2 { + + private static final int ERROR = callInClinit(); + + private static int callInClinit() { + throw new UnsupportedOperationException(); + } + } + +} diff --git a/spring-context/src/test/resources/org/springframework/context/annotation/configuration/ExceptionInInitializerTests.xml b/spring-context/src/test/resources/org/springframework/context/annotation/configuration/ExceptionInInitializerTests.xml new file mode 100644 index 000000000000..b41a26f6e482 --- /dev/null +++ b/spring-context/src/test/resources/org/springframework/context/annotation/configuration/ExceptionInInitializerTests.xml @@ -0,0 +1,11 @@ + + + + + + + + \ No newline at end of file