Skip to content

Commit 9f77ef4

Browse files
committed
Exclude overloaded from equals & hashCode in MethodOverride
Prior to this commit, the inclusion of the 'overloaded' flag in the implementations of equals() and hashCode() in MethodOverride could lead to adverse effects in the outcome of equals() in AbstractBeanDefinition. For example, given two bean definitions A and B that represent the exact same bean definition metadata for a bean that relies on method injection, if A has been validated and B has not, then A.equals(B) will potentially return false, which is not acceptable behavior. This commit addresses this issue by removing the 'overloaded' flag from the implementations of equals() and hashCode() for MethodOverride. Issue: SPR-11420 (cherry picked from commit 9534245)
1 parent 7f95a27 commit 9f77ef4

File tree

2 files changed

+72
-37
lines changed

2 files changed

+72
-37
lines changed

spring-beans/src/main/java/org/springframework/beans/factory/support/MethodOverride.java

Lines changed: 15 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2012 the original author or authors.
2+
* Copyright 2002-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -23,14 +23,15 @@
2323
import org.springframework.util.ObjectUtils;
2424

2525
/**
26-
* Object representing the override of a method on a managed
27-
* object by the IoC container.
26+
* Object representing the override of a method on a managed object by the IoC
27+
* container.
2828
*
29-
* <p>Note that the override mechanism is <i>not</i> intended as a
30-
* generic means of inserting crosscutting code: use AOP for that.
29+
* <p>Note that the override mechanism is <em>not</em> intended as a generic
30+
* means of inserting crosscutting code: use AOP for that.
3131
*
3232
* @author Rod Johnson
3333
* @author Juergen Hoeller
34+
* @author Sam Brannen
3435
* @since 1.1
3536
*/
3637
public abstract class MethodOverride implements BeanMetadataElement {
@@ -59,17 +60,18 @@ public String getMethodName() {
5960
}
6061

6162
/**
62-
* Set whether the overridden method has to be considered as overloaded
63-
* (that is, whether arg type matching has to happen).
64-
* <p>Default is "true"; can be switched to "false" to optimize runtime performance.
63+
* Set whether the overridden method is <em>overloaded</em> (i.e., whether argument
64+
* type matching needs to occur to disambiguate methods of the same name).
65+
* <p>Default is {@code true}; can be switched to {@code false} to optimize
66+
* runtime performance.
6567
*/
6668
protected void setOverloaded(boolean overloaded) {
6769
this.overloaded = overloaded;
6870
}
6971

7072
/**
71-
* Return whether the overridden method has to be considered as overloaded
72-
* (that is, whether arg type matching has to happen).
73+
* Return whether the overridden method is <em>overloaded</em> (i.e., whether argument
74+
* type matching needs to occur to disambiguate methods of the same name).
7375
*/
7476
protected boolean isOverloaded() {
7577
return this.overloaded;
@@ -87,17 +89,15 @@ public Object getSource() {
8789
return this.source;
8890
}
8991

90-
9192
/**
92-
* Subclasses must override this to indicate whether they match
93-
* the given method. This allows for argument list checking
94-
* as well as method name checking.
93+
* Subclasses must override this to indicate whether they <em>match</em> the
94+
* given method. This allows for argument list checking as well as method
95+
* name checking.
9596
* @param method the method to check
9697
* @return whether this override matches the given method
9798
*/
9899
public abstract boolean matches(Method method);
99100

100-
101101
@Override
102102
public boolean equals(Object other) {
103103
if (this == other) {
@@ -108,15 +108,13 @@ public boolean equals(Object other) {
108108
}
109109
MethodOverride that = (MethodOverride) other;
110110
return (ObjectUtils.nullSafeEquals(this.methodName, that.methodName) &&
111-
this.overloaded == that.overloaded &&
112111
ObjectUtils.nullSafeEquals(this.source, that.source));
113112
}
114113

115114
@Override
116115
public int hashCode() {
117116
int hashCode = ObjectUtils.nullSafeHashCode(this.methodName);
118117
hashCode = 29 * hashCode + ObjectUtils.nullSafeHashCode(this.source);
119-
hashCode = 29 * hashCode + (this.overloaded ? 1 : 0);
120118
return hashCode;
121119
}
122120

Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
* Copyright 2002-2013 the original author or authors.
2+
* Copyright 2002-2014 the original author or authors.
33
*
44
* Licensed under the Apache License, Version 2.0 (the "License");
55
* you may not use this file except in compliance with the License.
@@ -16,63 +16,95 @@
1616

1717
package org.springframework.beans.factory.support;
1818

19-
import junit.framework.TestCase;
19+
import org.junit.Test;
2020

2121
import org.springframework.beans.factory.config.BeanDefinition;
2222
import org.springframework.beans.factory.config.RuntimeBeanReference;
2323
import org.springframework.tests.sample.beans.TestBean;
2424

25+
import static org.junit.Assert.*;
2526

2627
/**
28+
* Unit tests for {@code equals()} and {@code hashCode()} in bean definitions.
29+
*
2730
* @author Rob Harrop
31+
* @author Sam Brannen
2832
*/
29-
public class DefinitionMetadataEqualsHashCodeTests extends TestCase {
33+
@SuppressWarnings("serial")
34+
public class DefinitionMetadataEqualsHashCodeTests {
3035

31-
@SuppressWarnings("serial")
32-
public void testRootBeanDefinitionEqualsAndHashCode() throws Exception {
36+
@Test
37+
public void rootBeanDefinition() {
3338
RootBeanDefinition master = new RootBeanDefinition(TestBean.class);
3439
RootBeanDefinition equal = new RootBeanDefinition(TestBean.class);
3540
RootBeanDefinition notEqual = new RootBeanDefinition(String.class);
36-
RootBeanDefinition subclass = new RootBeanDefinition(TestBean.class) {};
41+
RootBeanDefinition subclass = new RootBeanDefinition(TestBean.class) {
42+
};
3743
setBaseProperties(master);
3844
setBaseProperties(equal);
3945
setBaseProperties(notEqual);
4046
setBaseProperties(subclass);
4147

42-
assertEqualsContract(master, equal, notEqual, subclass);
43-
assertEquals("Hash code for equal instances should match", master.hashCode(), equal.hashCode());
48+
assertEqualsAndHashCodeContracts(master, equal, notEqual, subclass);
4449
}
4550

46-
@SuppressWarnings("serial")
47-
public void testChildBeanDefinitionEqualsAndHashCode() throws Exception {
51+
/**
52+
* @since 3.2.8
53+
* @see <a href="https://jira.springsource.org/browse/SPR-11420">SPR-11420</a>
54+
*/
55+
@Test
56+
public void rootBeanDefinitionAndMethodOverridesWithDifferentOverloadedValues() {
57+
RootBeanDefinition master = new RootBeanDefinition(TestBean.class);
58+
RootBeanDefinition equal = new RootBeanDefinition(TestBean.class);
59+
60+
setBaseProperties(master);
61+
setBaseProperties(equal);
62+
63+
// Simulate AbstractBeanDefinition.validate() which delegates to
64+
// AbstractBeanDefinition.prepareMethodOverrides():
65+
master.getMethodOverrides().getOverrides().iterator().next().setOverloaded(false);
66+
// But do not simulate validation of the 'equal' bean. As a consequence, a method
67+
// override in 'equal' will be marked as overloaded, but the corresponding
68+
// override in 'master' will not. But... the bean definitions should still be
69+
// considered equal.
70+
71+
assertEquals("Should be equal", master, equal);
72+
assertEquals("Hash code for equal instances must match", master.hashCode(), equal.hashCode());
73+
}
74+
75+
@Test
76+
public void childBeanDefinition() {
4877
ChildBeanDefinition master = new ChildBeanDefinition("foo");
4978
ChildBeanDefinition equal = new ChildBeanDefinition("foo");
5079
ChildBeanDefinition notEqual = new ChildBeanDefinition("bar");
51-
ChildBeanDefinition subclass = new ChildBeanDefinition("foo"){};
80+
ChildBeanDefinition subclass = new ChildBeanDefinition("foo") {
81+
};
5282
setBaseProperties(master);
5383
setBaseProperties(equal);
5484
setBaseProperties(notEqual);
5585
setBaseProperties(subclass);
5686

57-
assertEqualsContract(master, equal, notEqual, subclass);
58-
assertEquals("Hash code for equal instances should match", master.hashCode(), equal.hashCode());
87+
assertEqualsAndHashCodeContracts(master, equal, notEqual, subclass);
5988
}
6089

61-
public void testRuntimeBeanReference() throws Exception {
90+
@Test
91+
public void runtimeBeanReference() {
6292
RuntimeBeanReference master = new RuntimeBeanReference("name");
6393
RuntimeBeanReference equal = new RuntimeBeanReference("name");
6494
RuntimeBeanReference notEqual = new RuntimeBeanReference("someOtherName");
65-
RuntimeBeanReference subclass = new RuntimeBeanReference("name"){};
66-
assertEqualsContract(master, equal, notEqual, subclass);
95+
RuntimeBeanReference subclass = new RuntimeBeanReference("name") {
96+
};
97+
assertEqualsAndHashCodeContracts(master, equal, notEqual, subclass);
6798
}
99+
68100
private void setBaseProperties(AbstractBeanDefinition definition) {
69101
definition.setAbstract(true);
70102
definition.setAttribute("foo", "bar");
71103
definition.setAutowireCandidate(false);
72104
definition.setAutowireMode(AbstractBeanDefinition.AUTOWIRE_BY_TYPE);
73-
//definition.getConstructorArgumentValues().addGenericArgumentValue("foo");
105+
// definition.getConstructorArgumentValues().addGenericArgumentValue("foo");
74106
definition.setDependencyCheck(AbstractBeanDefinition.DEPENDENCY_CHECK_OBJECTS);
75-
definition.setDependsOn(new String[]{"foo", "bar"});
107+
definition.setDependsOn(new String[] { "foo", "bar" });
76108
definition.setDestroyMethodName("destroy");
77109
definition.setEnforceDestroyMethod(false);
78110
definition.setEnforceInitMethod(true);
@@ -89,10 +121,15 @@ private void setBaseProperties(AbstractBeanDefinition definition) {
89121
definition.setSource("foo");
90122
}
91123

92-
private void assertEqualsContract(Object master, Object equal, Object notEqual, Object subclass) {
124+
private void assertEqualsAndHashCodeContracts(Object master, Object equal, Object notEqual, Object subclass) {
93125
assertEquals("Should be equal", master, equal);
94-
assertFalse("Should not be equal", master.equals(notEqual));
126+
assertEquals("Hash code for equal instances should match", master.hashCode(), equal.hashCode());
127+
128+
assertNotEquals("Should not be equal", master, notEqual);
129+
assertNotEquals("Hash code for non-equal instances should not match", master.hashCode(), notEqual.hashCode());
130+
95131
assertEquals("Subclass should be equal", master, subclass);
132+
assertEquals("Hash code for subclass should match", master.hashCode(), subclass.hashCode());
96133
}
97134

98135
}

0 commit comments

Comments
 (0)