diff --git a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java index 9db1a1253a..98645c369b 100644 --- a/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java +++ b/spring-context/src/main/java/org/springframework/context/annotation/ConfigurationClassEnhancer.java @@ -16,6 +16,7 @@ package org.springframework.context.annotation; +import java.lang.reflect.Constructor; import java.lang.reflect.Field; import java.lang.reflect.Method; import java.lang.reflect.Modifier; @@ -139,14 +140,20 @@ class ConfigurationClassEnhancer { } /** - * Checks whether the given config class relies on package visibility, - * either for the class itself or for any of its {@code @Bean} methods. + * Checks whether the given config class relies on package visibility, either for + * the class and any of its constructors or for any of its {@code @Bean} methods. */ private boolean reliesOnPackageVisibility(Class configSuperClass) { int mod = configSuperClass.getModifiers(); if (!Modifier.isPublic(mod) && !Modifier.isProtected(mod)) { return true; } + for (Constructor ctor : configSuperClass.getDeclaredConstructors()) { + mod = ctor.getModifiers(); + if (!Modifier.isPublic(mod) && !Modifier.isProtected(mod)) { + return true; + } + } for (Method method : ReflectionUtils.getDeclaredMethods(configSuperClass)) { if (BeanAnnotationHelper.isBeanAnnotated(method)) { mod = method.getModifiers(); diff --git a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassEnhancerTests.java b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassEnhancerTests.java index 2dc8ba872a..38779f588c 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassEnhancerTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/ConfigurationClassEnhancerTests.java @@ -104,6 +104,31 @@ class ConfigurationClassEnhancerTests { assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent()); } + @Test + void withNonPublicConstructor() { + ConfigurationClassEnhancer configurationClassEnhancer = new ConfigurationClassEnhancer(); + + ClassLoader classLoader = new URLClassLoader(new URL[0], getClass().getClassLoader()); + Class enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicConstructor.class, classLoader); + assertThat(MyConfigWithNonPublicConstructor.class).isAssignableFrom(enhancedClass); + assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent()); + + classLoader = new OverridingClassLoader(getClass().getClassLoader()); + enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicConstructor.class, classLoader); + assertThat(MyConfigWithNonPublicConstructor.class).isAssignableFrom(enhancedClass); + assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent()); + + classLoader = new CustomSmartClassLoader(getClass().getClassLoader()); + enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicConstructor.class, classLoader); + assertThat(MyConfigWithNonPublicConstructor.class).isAssignableFrom(enhancedClass); + assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent()); + + classLoader = new BasicSmartClassLoader(getClass().getClassLoader()); + enhancedClass = configurationClassEnhancer.enhance(MyConfigWithNonPublicConstructor.class, classLoader); + assertThat(MyConfigWithNonPublicConstructor.class).isAssignableFrom(enhancedClass); + assertThat(enhancedClass.getClassLoader()).isEqualTo(classLoader.getParent()); + } + @Test void withNonPublicMethod() { ConfigurationClassEnhancer configurationClassEnhancer = new ConfigurationClassEnhancer(); @@ -160,6 +185,19 @@ class ConfigurationClassEnhancerTests { } + @Configuration + public static class MyConfigWithNonPublicConstructor { + + MyConfigWithNonPublicConstructor() { + } + + @Bean + public String myBean() { + return "bean"; + } + } + + @Configuration public static class MyConfigWithNonPublicMethod {