diff --git a/spring-context/src/main/java/org/springframework/cache/interceptor/SimpleKey.java b/spring-context/src/main/java/org/springframework/cache/interceptor/SimpleKey.java index da97487d4a4d..e6a2556e4a58 100644 --- a/spring-context/src/main/java/org/springframework/cache/interceptor/SimpleKey.java +++ b/spring-context/src/main/java/org/springframework/cache/interceptor/SimpleKey.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2018 the original author or authors. + * Copyright 2002-2020 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. @@ -16,6 +16,8 @@ package org.springframework.cache.interceptor; +import java.io.IOException; +import java.io.ObjectInputStream; import java.io.Serializable; import java.util.Arrays; @@ -27,19 +29,23 @@ * A simple key as returned from the {@link SimpleKeyGenerator}. * * @author Phillip Webb + * @author Juergen Hoeller * @since 4.0 * @see SimpleKeyGenerator */ @SuppressWarnings("serial") public class SimpleKey implements Serializable { - /** An empty key. */ + /** + * An empty key. + */ public static final SimpleKey EMPTY = new SimpleKey(); private final Object[] params; - private final int hashCode; + // Effectively final, just re-calculated on deserialization + private transient int hashCode; /** @@ -49,6 +55,7 @@ public class SimpleKey implements Serializable { public SimpleKey(Object... elements) { Assert.notNull(elements, "Elements must not be null"); this.params = elements.clone(); + // Pre-calculate hashCode field this.hashCode = Arrays.deepHashCode(this.params); } @@ -61,6 +68,7 @@ public boolean equals(@Nullable Object other) { @Override public final int hashCode() { + // Expose pre-calculated hashCode field return this.hashCode; } @@ -69,4 +77,10 @@ public String toString() { return getClass().getSimpleName() + " [" + StringUtils.arrayToCommaDelimitedString(this.params) + "]"; } + private void readObject(ObjectInputStream ois) throws IOException, ClassNotFoundException { + ois.defaultReadObject(); + // Re-calculate hashCode field on deserialization + this.hashCode = Arrays.deepHashCode(this.params); + } + } diff --git a/spring-context/src/test/java/org/springframework/cache/interceptor/SimpleKeyGeneratorTests.java b/spring-context/src/test/java/org/springframework/cache/interceptor/SimpleKeyGeneratorTests.java index 5357949f4451..2d9398ccb1a1 100644 --- a/spring-context/src/test/java/org/springframework/cache/interceptor/SimpleKeyGeneratorTests.java +++ b/spring-context/src/test/java/org/springframework/cache/interceptor/SimpleKeyGeneratorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2019 the original author or authors. + * Copyright 2002-2020 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. @@ -18,17 +18,16 @@ import org.junit.jupiter.api.Test; -import static org.assertj.core.api.Assertions.assertThat; - - - +import org.springframework.core.testfixture.io.SerializationTestUtils; +import static org.assertj.core.api.Assertions.assertThat; /** * Tests for {@link SimpleKeyGenerator} and {@link SimpleKey}. * * @author Phillip Webb * @author Stephane Nicoll + * @author Juergen Hoeller */ public class SimpleKeyGeneratorTests { @@ -47,7 +46,7 @@ public void noValues() { } @Test - public void singleValue(){ + public void singleValue() { Object k1 = generateKey(new Object[] { "a" }); Object k2 = generateKey(new Object[] { "a" }); Object k3 = generateKey(new Object[] { "different" }); @@ -59,7 +58,7 @@ public void singleValue(){ } @Test - public void multipleValues() { + public void multipleValues() { Object k1 = generateKey(new Object[] { "a", 1, "b" }); Object k2 = generateKey(new Object[] { "a", 1, "b" }); Object k3 = generateKey(new Object[] { "b", 1, "a" }); @@ -114,6 +113,17 @@ public void arrayWithExtraParameter() { assertThat(k1).isNotEqualTo(k3); } + @Test + public void serializedKeys() throws Exception { + Object k1 = SerializationTestUtils.serializeAndDeserialize(generateKey(new Object[] { "a", 1, "b" })); + Object k2 = SerializationTestUtils.serializeAndDeserialize(generateKey(new Object[] { "a", 1, "b" })); + Object k3 = SerializationTestUtils.serializeAndDeserialize(generateKey(new Object[] { "b", 1, "a" })); + assertThat(k1.hashCode()).isEqualTo(k2.hashCode()); + assertThat(k1.hashCode()).isNotEqualTo(k3.hashCode()); + assertThat(k1).isEqualTo(k2); + assertThat(k1).isNotEqualTo(k3); + } + private Object generateKey(Object[] arguments) { return this.generator.generate(null, null, arguments);