diff --git a/spring-web/src/main/java/org/springframework/web/context/request/ServletRequestAttributes.java b/spring-web/src/main/java/org/springframework/web/context/request/ServletRequestAttributes.java index 8ad2fdb997..20a17c3fce 100644 --- a/spring-web/src/main/java/org/springframework/web/context/request/ServletRequestAttributes.java +++ b/spring-web/src/main/java/org/springframework/web/context/request/ServletRequestAttributes.java @@ -248,7 +248,7 @@ public class ServletRequestAttributes extends AbstractRequestAttributes { String name = entry.getKey(); Object newValue = entry.getValue(); Object oldValue = this.session.getAttribute(name); - if (oldValue == newValue) { + if (oldValue == newValue && !isImmutableSessionAttribute(name, newValue)) { this.session.setAttribute(name, newValue); } } @@ -260,6 +260,23 @@ public class ServletRequestAttributes extends AbstractRequestAttributes { this.sessionAttributesToUpdate.clear(); } + /** + * Determine whether the given value is to be considered as an immutable session + * attribute, that is, doesn't have to be re-set via {@code session.setAttribute} + * since its value cannot meaningfully change internally. + *

The default implementation returns {@code true} for {@code String}, + * {@code Character}, {@code Boolean} and {@code Number} values. + * @param name the name of the attribute + * @param value the corresponding value to check + * @return {@code true} if the value is to be considered as immutable for the + * purposes of session attribute management; {@code false} otherwise + * @see #updateAccessedSessionAttributes() + */ + protected boolean isImmutableSessionAttribute(String name, Object value) { + return (value instanceof String || value instanceof Character || + value instanceof Boolean || value instanceof Number); + } + /** * Register the given callback as to be executed after session termination. *

Note: The callback object should be serializable in order to survive diff --git a/spring-web/src/test/java/org/springframework/web/context/request/ServletRequestAttributesTests.java b/spring-web/src/test/java/org/springframework/web/context/request/ServletRequestAttributesTests.java index 2e5688857f..226b05d3d1 100644 --- a/spring-web/src/test/java/org/springframework/web/context/request/ServletRequestAttributesTests.java +++ b/spring-web/src/test/java/org/springframework/web/context/request/ServletRequestAttributesTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2013 the original author or authors. + * Copyright 2002-2014 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. @@ -17,10 +17,12 @@ package org.springframework.web.context.request; import java.io.Serializable; - +import java.math.BigInteger; import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpSession; import org.junit.Test; + import org.springframework.mock.web.test.MockHttpServletRequest; import org.springframework.mock.web.test.MockHttpSession; @@ -39,23 +41,12 @@ public class ServletRequestAttributesTests { private static final Serializable VALUE = new Serializable() { }; + @Test(expected = IllegalArgumentException.class) public void ctorRejectsNullArg() throws Exception { new ServletRequestAttributes(null); } - @Test - public void updateAccessedAttributes() throws Exception { - MockHttpSession session = new MockHttpSession(); - session.setAttribute(KEY, VALUE); - MockHttpServletRequest request = new MockHttpServletRequest(); - request.setSession(session); - ServletRequestAttributes attrs = new ServletRequestAttributes(request); - Object value = attrs.getAttribute(KEY, RequestAttributes.SCOPE_SESSION); - assertSame(VALUE, value); - attrs.requestCompleted(); - } - @Test public void setRequestScopedAttribute() throws Exception { MockHttpServletRequest request = new MockHttpServletRequest(); @@ -162,4 +153,64 @@ public class ServletRequestAttributesTests { verify(request).getSession(false); } + @Test + public void updateAccessedAttributes() throws Exception { + HttpServletRequest request = mock(HttpServletRequest.class); + HttpSession session = mock(HttpSession.class); + when(request.getSession(anyBoolean())).thenReturn(session); + when(session.getAttribute(KEY)).thenReturn(VALUE); + + ServletRequestAttributes attrs = new ServletRequestAttributes(request); + assertSame(VALUE, attrs.getAttribute(KEY, RequestAttributes.SCOPE_SESSION)); + attrs.requestCompleted(); + + verify(session, times(2)).getAttribute(KEY); + verify(session).setAttribute(KEY, VALUE); + verifyNoMoreInteractions(session); + } + + @Test + public void skipImmutableString() { + doSkipImmutableValue("someString"); + } + + @Test + public void skipImmutableCharacter() { + doSkipImmutableValue(new Character('x')); + } + + @Test + public void skipImmutableBoolean() { + doSkipImmutableValue(Boolean.TRUE); + } + + @Test + public void skipImmutableInteger() { + doSkipImmutableValue(new Integer(1)); + } + + @Test + public void skipImmutableFloat() { + doSkipImmutableValue(new Float(1.1)); + } + + @Test + public void skipImmutableBigInteger() { + doSkipImmutableValue(new BigInteger("1")); + } + + private void doSkipImmutableValue(Object immutableValue) { + HttpServletRequest request = mock(HttpServletRequest.class); + HttpSession session = mock(HttpSession.class); + when(request.getSession(anyBoolean())).thenReturn(session); + when(session.getAttribute(KEY)).thenReturn(immutableValue); + + ServletRequestAttributes attrs = new ServletRequestAttributes(request); + attrs.getAttribute(KEY, RequestAttributes.SCOPE_SESSION); + attrs.requestCompleted(); + + verify(session, times(2)).getAttribute(KEY); + verifyNoMoreInteractions(session); + } + }