Skip to content

Commit 32309e8

Browse files
committed
Fix context caching for Bean Overrides w/ different annotation order
Prior to this commit, ApplicationContext caching support was broken if two Bean Override fields declared the same annotations but in a different order. This commit fixes that by switching to Set semantics for the annotations declared on a Bean Override field. Closes gh-33633
1 parent 8cd2c40 commit 32309e8

File tree

3 files changed

+43
-7
lines changed

3 files changed

+43
-7
lines changed

spring-test/src/main/java/org/springframework/test/context/bean/override/OverrideMetadata.java

+16-4
Original file line numberDiff line numberDiff line change
@@ -19,9 +19,12 @@
1919
import java.lang.annotation.Annotation;
2020
import java.lang.reflect.Field;
2121
import java.util.Arrays;
22+
import java.util.Collections;
23+
import java.util.HashSet;
2224
import java.util.LinkedList;
2325
import java.util.List;
2426
import java.util.Objects;
27+
import java.util.Set;
2528
import java.util.concurrent.atomic.AtomicBoolean;
2629

2730
import org.springframework.beans.BeanUtils;
@@ -61,6 +64,8 @@ public abstract class OverrideMetadata {
6164

6265
private final Field field;
6366

67+
private final Set<Annotation> fieldAnnotations;
68+
6469
private final ResolvableType beanType;
6570

6671
@Nullable
@@ -73,6 +78,7 @@ protected OverrideMetadata(Field field, ResolvableType beanType, @Nullable Strin
7378
BeanOverrideStrategy strategy) {
7479

7580
this.field = field;
81+
this.fieldAnnotations = annotationSet(field);
7682
this.beanType = beanType;
7783
this.beanName = beanName;
7884
this.strategy = strategy;
@@ -183,16 +189,17 @@ public boolean equals(Object other) {
183189
if (this.beanName != null) {
184190
return true;
185191
}
186-
// by type lookup
187-
return Objects.equals(this.field.getName(), that.field.getName()) &&
188-
Arrays.equals(this.field.getAnnotations(), that.field.getAnnotations());
192+
193+
// by-type lookup
194+
return (Objects.equals(this.field.getName(), that.field.getName()) &&
195+
this.fieldAnnotations.equals(that.fieldAnnotations));
189196
}
190197

191198
@Override
192199
public int hashCode() {
193200
int hash = Objects.hash(getClass(), this.beanType.getType(), this.beanName, this.strategy);
194201
return (this.beanName != null ? hash : hash +
195-
Objects.hash(this.field.getName(), Arrays.hashCode(this.field.getAnnotations())));
202+
Objects.hash(this.field.getName(), this.fieldAnnotations));
196203
}
197204

198205
@Override
@@ -205,4 +212,9 @@ public String toString() {
205212
.toString();
206213
}
207214

215+
private static Set<Annotation> annotationSet(Field field) {
216+
Annotation[] annotations = field.getAnnotations();
217+
return (annotations.length != 0 ? new HashSet<>(Arrays.asList(annotations)) : Collections.emptySet());
218+
}
219+
208220
}

spring-test/src/test/java/org/springframework/test/context/bean/override/BeanOverrideContextCustomizerTests.java

+3-3
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616

1717
package org.springframework.test.context.bean.override;
1818

19-
import java.lang.reflect.Field;
2019
import java.util.Arrays;
2120
import java.util.LinkedHashSet;
2221
import java.util.Objects;
@@ -26,9 +25,9 @@
2625
import org.springframework.beans.factory.config.BeanDefinition;
2726
import org.springframework.core.ResolvableType;
2827
import org.springframework.lang.Nullable;
28+
import org.springframework.util.ReflectionUtils;
2929

3030
import static org.assertj.core.api.Assertions.assertThat;
31-
import static org.mockito.Mockito.mock;
3231

3332
/**
3433
* Tests for {@link BeanOverrideContextCustomizer}.
@@ -72,7 +71,8 @@ private static class DummyOverrideMetadata extends OverrideMetadata {
7271
private final String key;
7372

7473
public DummyOverrideMetadata(String key) {
75-
super(mock(Field.class), ResolvableType.forClass(Object.class), null, BeanOverrideStrategy.REPLACE_DEFINITION);
74+
super(ReflectionUtils.findField(DummyOverrideMetadata.class, "key"),
75+
ResolvableType.forClass(Object.class), null, BeanOverrideStrategy.REPLACE_DEFINITION);
7676
this.key = key;
7777
}
7878

spring-test/src/test/java/org/springframework/test/context/bean/override/OverrideMetadataTests.java

+24
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,7 @@
2121
import java.lang.annotation.RetentionPolicy;
2222
import java.lang.annotation.Target;
2323
import java.lang.reflect.Field;
24+
import java.util.Arrays;
2425
import java.util.HashSet;
2526
import java.util.List;
2627
import java.util.function.Consumer;
@@ -42,6 +43,7 @@
4243
*
4344
* @author Simon Baslé
4445
* @author Stephane Nicoll
46+
* @author Sam Brannen
4547
* @since 6.2
4648
*/
4749
class OverrideMetadataTests {
@@ -143,6 +145,20 @@ void isEqualToWithSameMetadataAndSameQualifierValues() {
143145
assertThat(metadata).hasSameHashCodeAs(metadata2);
144146
}
145147

148+
@Test
149+
void isEqualToWithSameMetadataAndSameQualifierValuesButWithAnnotationsDeclaredInDifferentOrder() {
150+
Field field1 = field(ConfigA.class, "qualifiedDummyBean");
151+
Field field2 = field(ConfigB.class, "qualifiedDummyBean");
152+
153+
// Prerequisite
154+
assertThat(Arrays.equals(field1.getAnnotations(), field2.getAnnotations())).isFalse();
155+
156+
OverrideMetadata metadata1 = createMetadata(field1);
157+
OverrideMetadata metadata2 = createMetadata(field2);
158+
assertThat(metadata1).isEqualTo(metadata2);
159+
assertThat(metadata1).hasSameHashCodeAs(metadata2);
160+
}
161+
146162
@Test
147163
void isNotEqualToWithSameMetadataAndDifferentQualifierValues() {
148164
OverrideMetadata metadata = createMetadata(field(ConfigA.class, "directQualifier"));
@@ -241,6 +257,10 @@ static class ConfigA {
241257

242258
@CustomQualifier
243259
ExampleService customQualifier;
260+
261+
@DummyBean
262+
@Qualifier("test")
263+
ExampleService qualifiedDummyBean;
244264
}
245265

246266
static class ConfigB {
@@ -251,6 +271,10 @@ static class ConfigB {
251271

252272
@Qualifier("test")
253273
ExampleService directQualifier;
274+
275+
@Qualifier("test")
276+
@DummyBean
277+
ExampleService qualifiedDummyBean;
254278
}
255279

256280
@Target(ElementType.FIELD)

0 commit comments

Comments
 (0)