From 2c3c3831c179df3bf22edc6bcf6323631a32156e Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 17 Jun 2024 18:42:20 +0200 Subject: [PATCH 1/3] Consistently ignore bridge method on generated subclass for visibility purposes Closes gh-33030 --- .../AutowiredConfigurationTests.java | 36 +++++++++++++++++++ .../core/BridgeMethodResolver.java | 5 +++ 2 files changed, 41 insertions(+) diff --git a/spring-context/src/test/java/org/springframework/context/annotation/configuration/AutowiredConfigurationTests.java b/spring-context/src/test/java/org/springframework/context/annotation/configuration/AutowiredConfigurationTests.java index 9b9a6e5e374f..00cd00e3b8cf 100644 --- a/spring-context/src/test/java/org/springframework/context/annotation/configuration/AutowiredConfigurationTests.java +++ b/spring-context/src/test/java/org/springframework/context/annotation/configuration/AutowiredConfigurationTests.java @@ -26,6 +26,7 @@ import org.junit.jupiter.api.Test; import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.ObjectFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; @@ -43,6 +44,7 @@ import org.springframework.core.annotation.AliasFor; import org.springframework.core.io.ClassPathResource; import org.springframework.core.io.Resource; +import org.springframework.util.Assert; import static org.assertj.core.api.Assertions.assertThat; @@ -183,6 +185,14 @@ void testValueInjectionWithProviderMethodArguments() { context.close(); } + @Test + void testValueInjectionWithAccidentalAutowiredAnnotations() { + AnnotationConfigApplicationContext context = + new AnnotationConfigApplicationContext(ValueConfigWithAccidentalAutowiredAnnotations.class); + doTestValueInjection(context); + context.close(); + } + private void doTestValueInjection(BeanFactory context) { System.clearProperty("myProp"); @@ -494,6 +504,32 @@ public TestBean testBean2(@Value("#{systemProperties[myProp]}") Provider } + @Configuration + static class ValueConfigWithAccidentalAutowiredAnnotations implements InitializingBean { + + boolean invoked; + + @Override + public void afterPropertiesSet() { + Assert.state(!invoked, "Factory method must not get invoked on startup"); + } + + @Bean @Scope("prototype") + @Autowired + public TestBean testBean(@Value("#{systemProperties[myProp]}") Provider name) { + invoked = true; + return new TestBean(name.get()); + } + + @Bean @Scope("prototype") + @Autowired + public TestBean testBean2(@Value("#{systemProperties[myProp]}") Provider name2) { + invoked = true; + return new TestBean(name2.get()); + } + } + + @Configuration static class PropertiesConfig { diff --git a/spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java b/spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java index 10388c522d58..a5c666202515 100644 --- a/spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java +++ b/spring-core/src/main/java/org/springframework/core/BridgeMethodResolver.java @@ -276,8 +276,13 @@ private static Method searchForMatch(Class type, Method bridgeMethod) { */ public static boolean isVisibilityBridgeMethodPair(Method bridgeMethod, Method bridgedMethod) { if (bridgeMethod == bridgedMethod) { + // Same method: for common purposes, return true to proceed as if it was a visibility bridge. return true; } + if (ClassUtils.getUserClass(bridgeMethod.getDeclaringClass()) != bridgeMethod.getDeclaringClass()) { + // Method on generated subclass: return false to consistently ignore it for visibility purposes. + return false; + } return (bridgeMethod.getReturnType().equals(bridgedMethod.getReturnType()) && bridgeMethod.getParameterCount() == bridgedMethod.getParameterCount() && Arrays.equals(bridgeMethod.getParameterTypes(), bridgedMethod.getParameterTypes())); From e79a9a5bff15323545e21b812f7cea8585f8eddd Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 17 Jun 2024 18:42:30 +0200 Subject: [PATCH 2/3] Correct and consistent event class names in constructor javadoc Closes gh-33032 --- .../context/event/ApplicationContextEvent.java | 4 ++-- .../org/springframework/context/event/ContextClosedEvent.java | 4 ++-- .../springframework/context/event/ContextRefreshedEvent.java | 4 ++-- .../springframework/context/event/ContextStartedEvent.java | 4 ++-- .../springframework/context/event/ContextStoppedEvent.java | 4 ++-- 5 files changed, 10 insertions(+), 10 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/context/event/ApplicationContextEvent.java b/spring-context/src/main/java/org/springframework/context/event/ApplicationContextEvent.java index fab9067b20d6..0823d051c340 100644 --- a/spring-context/src/main/java/org/springframework/context/event/ApplicationContextEvent.java +++ b/spring-context/src/main/java/org/springframework/context/event/ApplicationContextEvent.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2024 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. @@ -29,7 +29,7 @@ public abstract class ApplicationContextEvent extends ApplicationEvent { /** - * Create a new ContextStartedEvent. + * Create a new {@code ApplicationContextEvent}. * @param source the {@code ApplicationContext} that the event is raised for * (must not be {@code null}) */ diff --git a/spring-context/src/main/java/org/springframework/context/event/ContextClosedEvent.java b/spring-context/src/main/java/org/springframework/context/event/ContextClosedEvent.java index 900bf30e49ca..8d0e2e56541c 100644 --- a/spring-context/src/main/java/org/springframework/context/event/ContextClosedEvent.java +++ b/spring-context/src/main/java/org/springframework/context/event/ContextClosedEvent.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2024 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. @@ -29,7 +29,7 @@ public class ContextClosedEvent extends ApplicationContextEvent { /** - * Creates a new ContextClosedEvent. + * Create a new {@code ContextClosedEvent}. * @param source the {@code ApplicationContext} that has been closed * (must not be {@code null}) */ diff --git a/spring-context/src/main/java/org/springframework/context/event/ContextRefreshedEvent.java b/spring-context/src/main/java/org/springframework/context/event/ContextRefreshedEvent.java index 27c657a948e6..ba55c6a56c27 100644 --- a/spring-context/src/main/java/org/springframework/context/event/ContextRefreshedEvent.java +++ b/spring-context/src/main/java/org/springframework/context/event/ContextRefreshedEvent.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2024 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. @@ -29,7 +29,7 @@ public class ContextRefreshedEvent extends ApplicationContextEvent { /** - * Create a new ContextRefreshedEvent. + * Create a new {@code ContextRefreshedEvent}. * @param source the {@code ApplicationContext} that has been initialized * or refreshed (must not be {@code null}) */ diff --git a/spring-context/src/main/java/org/springframework/context/event/ContextStartedEvent.java b/spring-context/src/main/java/org/springframework/context/event/ContextStartedEvent.java index bfd615d5c120..f0cf6d6bb0d4 100644 --- a/spring-context/src/main/java/org/springframework/context/event/ContextStartedEvent.java +++ b/spring-context/src/main/java/org/springframework/context/event/ContextStartedEvent.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2024 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. @@ -30,7 +30,7 @@ public class ContextStartedEvent extends ApplicationContextEvent { /** - * Create a new ContextStartedEvent. + * Create a new {@code ContextStartedEvent}. * @param source the {@code ApplicationContext} that has been started * (must not be {@code null}) */ diff --git a/spring-context/src/main/java/org/springframework/context/event/ContextStoppedEvent.java b/spring-context/src/main/java/org/springframework/context/event/ContextStoppedEvent.java index 4a156b207b8c..791e08c282c2 100644 --- a/spring-context/src/main/java/org/springframework/context/event/ContextStoppedEvent.java +++ b/spring-context/src/main/java/org/springframework/context/event/ContextStoppedEvent.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2012 the original author or authors. + * Copyright 2002-2024 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. @@ -30,7 +30,7 @@ public class ContextStoppedEvent extends ApplicationContextEvent { /** - * Create a new ContextStoppedEvent. + * Create a new {@code ContextStoppedEvent}. * @param source the {@code ApplicationContext} that has been stopped * (must not be {@code null}) */ From 9a56a8877f5de1bce2da78ea7c6a4297970e8519 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Mon, 17 Jun 2024 18:42:37 +0200 Subject: [PATCH 3/3] Polishing --- .../MethodValidationAdapter.java | 30 ++++++++----------- 1 file changed, 13 insertions(+), 17 deletions(-) diff --git a/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationAdapter.java b/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationAdapter.java index 11b76a27ba2f..671d3022fad5 100644 --- a/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationAdapter.java +++ b/spring-context/src/main/java/org/springframework/validation/beanvalidation/MethodValidationAdapter.java @@ -234,8 +234,8 @@ public Class[] determineValidationGroups(Object target, Method method) { @Override public final MethodValidationResult validateArguments( - Object target, Method method, @Nullable MethodParameter[] parameters, Object[] arguments, - Class[] groups) { + Object target, Method method, @Nullable MethodParameter[] parameters, + Object[] arguments, Class[] groups) { Set> violations = invokeValidatorForArguments(target, method, arguments, groups); @@ -256,23 +256,21 @@ public final Set> invokeValidatorForArguments( Object target, Method method, Object[] arguments, Class[] groups) { ExecutableValidator execVal = this.validator.get().forExecutables(); - Set> violations; try { - violations = execVal.validateParameters(target, method, arguments, groups); + return execVal.validateParameters(target, method, arguments, groups); } catch (IllegalArgumentException ex) { // Probably a generic type mismatch between interface and impl as reported in SPR-12237 / HV-1011 // Let's try to find the bridged method on the implementation class... Method bridgedMethod = BridgeMethodResolver.getMostSpecificMethod(method, target.getClass()); - violations = execVal.validateParameters(target, bridgedMethod, arguments, groups); + return execVal.validateParameters(target, bridgedMethod, arguments, groups); } - return violations; } @Override public final MethodValidationResult validateReturnValue( - Object target, Method method, @Nullable MethodParameter returnType, @Nullable Object returnValue, - Class[] groups) { + Object target, Method method, @Nullable MethodParameter returnType, + @Nullable Object returnValue, Class[] groups) { Set> violations = invokeValidatorForReturnValue(target, method, returnValue, groups); @@ -305,9 +303,9 @@ private MethodValidationResult adaptViolations( Map nestedViolations = new LinkedHashMap<>(); for (ConstraintViolation violation : violations) { - Iterator itr = violation.getPropertyPath().iterator(); - while (itr.hasNext()) { - Path.Node node = itr.next(); + Iterator nodes = violation.getPropertyPath().iterator(); + while (nodes.hasNext()) { + Path.Node node = nodes.next(); MethodParameter parameter; if (node.getKind().equals(ElementKind.PARAMETER)) { @@ -328,8 +326,8 @@ else if (node.getKind().equals(ElementKind.RETURN_VALUE)) { // https://github.com/jakartaee/validation/issues/194 Path.Node parameterNode = node; - if (itr.hasNext()) { - node = itr.next(); + if (nodes.hasNext()) { + node = nodes.next(); } Object value; @@ -425,7 +423,6 @@ public interface ObjectNameResolver { * @return the name to use */ String resolveName(MethodParameter parameter, @Nullable Object value); - } @@ -456,6 +453,7 @@ private final class ParamValidationResultBuilder { public ParamValidationResultBuilder( Object target, MethodParameter parameter, @Nullable Object value, @Nullable Object container, @Nullable Integer containerIndex, @Nullable Object containerKey) { + this.target = target; this.parameter = parameter; this.value = value; @@ -473,7 +471,6 @@ public ParameterValidationResult build() { this.parameter, this.value, this.resolvableErrors, this.container, this.containerIndex, this.containerKey); } - } @@ -527,8 +524,7 @@ public ParameterErrors build() { /** - * Default algorithm to select an object name, as described in - * {@link #setObjectNameResolver(ObjectNameResolver)}. + * Default algorithm to select an object name, as described in {@link #setObjectNameResolver}. */ private static class DefaultObjectNameResolver implements ObjectNameResolver {