From b0e4b7e29cb66016dd2421241b8ed3224d0bbe08 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Thu, 9 Jan 2020 15:39:04 +0100 Subject: [PATCH] Re-calculate SimpleKey's hashCode field on deserialization Closes gh-24320 --- .../cache/interceptor/SimpleKey.java | 20 +++++++++++++--- .../interceptor/SimpleKeyGeneratorTests.java | 24 +++++++++++++------ 2 files changed, 34 insertions(+), 10 deletions(-) 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 da97487d4a4..e6a2556e4a5 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 @@ import org.springframework.util.StringUtils; * 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 class SimpleKey implements Serializable { @Override public final int hashCode() { + // Expose pre-calculated hashCode field return this.hashCode; } @@ -69,4 +77,10 @@ public class SimpleKey implements Serializable { 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 5357949f445..2d9398ccb1a 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 @@ package org.springframework.cache.interceptor; 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 class SimpleKeyGeneratorTests { } @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 class SimpleKeyGeneratorTests { } @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 class SimpleKeyGeneratorTests { 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);