From f30c49816281c62e63433300309d85e0a0786fe9 Mon Sep 17 00:00:00 2001 From: Juergen Hoeller Date: Tue, 7 Mar 2017 15:41:23 +0100 Subject: [PATCH] Retrieve newly created attribute from underlying request (marking it for update) Issue: SPR-15300 --- .../AbstractRequestAttributesScope.java | 10 +++- .../context/request/SessionScopeTests.java | 48 +++++++++++++++++-- 2 files changed, 54 insertions(+), 4 deletions(-) diff --git a/spring-web/src/main/java/org/springframework/web/context/request/AbstractRequestAttributesScope.java b/spring-web/src/main/java/org/springframework/web/context/request/AbstractRequestAttributesScope.java index 052314f238e..e1a5ccd402c 100644 --- a/spring-web/src/main/java/org/springframework/web/context/request/AbstractRequestAttributesScope.java +++ b/spring-web/src/main/java/org/springframework/web/context/request/AbstractRequestAttributesScope.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2016 the original author or authors. + * Copyright 2002-2017 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. @@ -43,6 +43,14 @@ public abstract class AbstractRequestAttributesScope implements Scope { if (scopedObject == null) { scopedObject = objectFactory.getObject(); attributes.setAttribute(name, scopedObject, getScope()); + // Retrieve object again, registering it for implicit session attribute updates. + // As a bonus, we also allow for potential decoration at the getAttribute level. + Object retrievedObject = attributes.getAttribute(name, getScope()); + if (retrievedObject != null) { + // Only proceed with retrieved object if still present (the expected case). + // If it disappeared concurrently, we return our locally created instance. + scopedObject = retrievedObject; + } } return scopedObject; } diff --git a/spring-web/src/test/java/org/springframework/web/context/request/SessionScopeTests.java b/spring-web/src/test/java/org/springframework/web/context/request/SessionScopeTests.java index a44318fc35a..947eb9d8f63 100644 --- a/spring-web/src/test/java/org/springframework/web/context/request/SessionScopeTests.java +++ b/spring-web/src/test/java/org/springframework/web/context/request/SessionScopeTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2015 the original author or authors. + * Copyright 2002-2017 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,6 +17,7 @@ package org.springframework.web.context.request; import java.io.Serializable; +import java.util.concurrent.atomic.AtomicInteger; import org.junit.After; import org.junit.Before; @@ -48,7 +49,7 @@ public class SessionScopeTests { @Before - public void setUp() throws Exception { + public void setup() throws Exception { this.beanFactory.registerScope("session", new SessionScope()); XmlBeanDefinitionReader reader = new XmlBeanDefinitionReader(this.beanFactory); reader.loadBeanDefinitions(new ClassPathResource("sessionScopeTests.xml", getClass())); @@ -59,9 +60,17 @@ public class SessionScopeTests { RequestContextHolder.setRequestAttributes(null); } + @Test public void getFromScope() throws Exception { - MockHttpSession session = new MockHttpSession(); + AtomicInteger count = new AtomicInteger(); + MockHttpSession session = new MockHttpSession() { + @Override + public void setAttribute(String name, Object value) { + super.setAttribute(name, value); + count.incrementAndGet(); + } + }; MockHttpServletRequest request = new MockHttpServletRequest(); request.setSession(session); ServletRequestAttributes requestAttributes = new ServletRequestAttributes(request); @@ -70,8 +79,41 @@ public class SessionScopeTests { String name = "sessionScopedObject"; assertNull(session.getAttribute(name)); TestBean bean = (TestBean) this.beanFactory.getBean(name); + assertEquals(1, count.intValue()); assertEquals(session.getAttribute(name), bean); assertSame(bean, this.beanFactory.getBean(name)); + assertEquals(1, count.intValue()); + + // should re-propagate updated attribute + requestAttributes.requestCompleted(); + assertEquals(session.getAttribute(name), bean); + assertEquals(2, count.intValue()); + } + + @Test + public void getFromScopeWithSingleAccess() throws Exception { + AtomicInteger count = new AtomicInteger(); + MockHttpSession session = new MockHttpSession() { + @Override + public void setAttribute(String name, Object value) { + super.setAttribute(name, value); + count.incrementAndGet(); + } + }; + MockHttpServletRequest request = new MockHttpServletRequest(); + request.setSession(session); + ServletRequestAttributes requestAttributes = new ServletRequestAttributes(request); + + RequestContextHolder.setRequestAttributes(requestAttributes); + String name = "sessionScopedObject"; + assertNull(session.getAttribute(name)); + TestBean bean = (TestBean) this.beanFactory.getBean(name); + assertEquals(1, count.intValue()); + + // should re-propagate updated attribute + requestAttributes.requestCompleted(); + assertEquals(session.getAttribute(name), bean); + assertEquals(2, count.intValue()); } @Test