Created
July 15, 2010 22:58
-
-
Save jawspeak/477662 to your computer and use it in GitHub Desktop.
New request scope for factory beans. Be careful though since it does not support injecting FactoryBeans as collaborators, but we didn't want this so it's okay. Solves the problem of Spring's Stupid FactoryBeans being instantiated multiple times per req
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<bean class="org.springframework.beans.factory.config.CustomScopeConfigurer"> | |
<property name="scopes"> | |
<map> | |
<entry key="request"> | |
<bean class="com.example.spring.FactoryRequestScope" /> | |
</entry> | |
</map> | |
</property> | |
</bean> | |
<!-- | |
I've observed with spring 2.5 FactoryBeans are created: | |
* As singletons if they aren't annotated, or are with @Scope("request") {duh} | |
* Per request if annotated with @Scope("request") which isn't what I expected with the documentation, because I thought the documentation said that scope would apply to what is instantiated _BY_ the FactoryBean, not the bean itself. | |
--> |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.spring; | |
import org.springframework.beans.factory.FactoryBean; | |
import org.springframework.beans.factory.ObjectFactory; | |
import org.springframework.web.context.request.AbstractRequestAttributesScope; | |
import org.springframework.web.context.request.RequestAttributes; | |
import org.springframework.web.context.request.RequestContextHolder; | |
public class FactoryRequestScope extends AbstractRequestAttributesScope{ | |
@Override | |
public Object get(String name, ObjectFactory objectFactory) { | |
RequestAttributes attributes = RequestContextHolder.currentRequestAttributes(); | |
Object scopedObject = attributes.getAttribute(name, getScope()); | |
if (scopedObject == null) { | |
scopedObject = createScopedObject(name, objectFactory); | |
attributes.setAttribute(name, scopedObject, getScope()); | |
} | |
return scopedObject; | |
} | |
private Object createScopedObject(String beanName, ObjectFactory objectFactory){ | |
Object scopedObject = objectFactory.getObject(); | |
if(scopedObject instanceof FactoryBean){ | |
try { | |
// This does not deal with the case of passing in a &beanName which is dereferenced, wanting the factory returned. | |
// This does not deal with the case of requesting injection of a FactoryBean into another class. | |
return ((FactoryBean) scopedObject).getObject(); | |
} catch (Exception e) { | |
throw new RuntimeException("Exception instantiating bean: " + beanName + " from factory bean: " + scopedObject, e); | |
} | |
} | |
return scopedObject; | |
} | |
@Override | |
protected int getScope() { | |
return RequestAttributes.SCOPE_REQUEST; | |
} | |
//Returns null because request scope doesn't require a conversation id. | |
@Override | |
public String getConversationId() { | |
return null; | |
} | |
} |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package com.example.spring; | |
import org.junit.After; | |
import org.junit.Before; | |
import org.junit.Test; | |
import org.springframework.beans.factory.FactoryBean; | |
import org.springframework.beans.factory.ObjectFactory; | |
import org.springframework.mock.web.MockHttpServletRequest; | |
import org.springframework.web.context.request.RequestAttributes; | |
import org.springframework.web.context.request.RequestContextHolder; | |
import org.springframework.web.context.request.ServletRequestAttributes; | |
import static org.junit.Assert.assertEquals; | |
import static org.mockito.Mockito.*; | |
import static org.springframework.web.context.request.RequestAttributes.SCOPE_REQUEST; | |
public class FactoryRequestScopeTest { | |
ObjectFactory objectFactory = mock(ObjectFactory.class); | |
FactoryRequestScope scope = new FactoryRequestScope(); | |
private RequestAttributes requestAttributes; | |
private RequestAttributes originalAttributes; | |
@Before | |
public void setUp(){ | |
MockHttpServletRequest request = new MockHttpServletRequest(); | |
requestAttributes = new ServletRequestAttributes(request); | |
originalAttributes = RequestContextHolder.getRequestAttributes(); | |
RequestContextHolder.setRequestAttributes(requestAttributes); | |
} | |
@After | |
public void tearDown() { | |
RequestContextHolder.setRequestAttributes(originalAttributes); | |
} | |
@Test | |
public void shouldReturnNewInstanceOfNonFactoryBean(){ | |
Object newBean = new Object(); | |
when(objectFactory.getObject()).thenReturn(newBean); | |
Object returnedInstance = scope.get("foo", objectFactory); | |
assertEquals(newBean, returnedInstance); | |
assertEquals(returnedInstance, requestAttributes.getAttribute("foo",SCOPE_REQUEST)); | |
} | |
@Test | |
public void shouldReturnCachedInstanceOfNonFactoryBean(){ | |
Object newBean = new Object(); | |
requestAttributes.setAttribute("foo", newBean, SCOPE_REQUEST); | |
Object returnedInstance = scope.get("foo", objectFactory); | |
assertEquals(newBean, returnedInstance); | |
assertEquals(returnedInstance, requestAttributes.getAttribute("foo",SCOPE_REQUEST)); | |
verifyZeroInteractions(objectFactory); | |
} | |
@Test | |
public void shouldReturnFactoryBeanObjectNotFactoryBeanItself(){ | |
Object newBean = new Object(); | |
when(objectFactory.getObject()).thenReturn(new ObjectFactoryBean(newBean)); | |
Object returnedInstance = scope.get("foo", objectFactory); | |
assertEquals(newBean, returnedInstance); | |
assertEquals(returnedInstance, requestAttributes.getAttribute("foo",SCOPE_REQUEST)); | |
} | |
private static class ObjectFactoryBean implements FactoryBean { | |
private final Object object; | |
private ObjectFactoryBean(Object object) { | |
this.object = object; | |
} | |
@Override | |
public Object getObject() throws Exception { | |
return object; | |
} | |
@Override | |
public Class getObjectType() { | |
return Object.class; | |
} | |
@Override | |
public boolean isSingleton() { | |
return false; | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment