Create a gist now

Instantly share code, notes, and snippets.

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
<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.
-->
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;
}
}
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