Reputation: 1593
If somewhere I have a singleton @Component
bean "Foo" with a @Autowired
HttpSession or HttpServletRequest, does Foo itself have to be declared session (or request) scoped, or can I just keep it as a simple singleton --- in which case HttpSession and/or HttpServletRequest are probably already injected as scoped proxies anyway via Spring?
Upvotes: 3
Views: 1545
Reputation: 280174
Here's a pretty simple test to check behavior (Spring 4.0.0.RELEASE)
@Controller
@RequestMapping("/service")
public class NewController {
@RequestMapping(method = RequestMethod.GET)
public @ResponseBody String test(ModelMap model) {
System.out.println(requestEntity.request.getAttribute("type")); // get it
requestEntity.request.setAttribute("type", "Scope"); // set it so that we can make sure that our second request doesn't contain it
System.out.println(requestEntity.request.getClass());
return "whatever";
}
@Autowired
private Foo requestEntity;
}
and
@Component
public class Foo {
@Autowired
public HttpServletRequest request;
}
If you send a request, you'll note that the attribute returned is always null
and that the class is something like
class com.sun.proxy.$Proxy19
Therefore you are always getting a different HttpServletRequest
object even though the @Component
bean is singleton scoped.
Here's the explanation:
When you instantiate a WebApplicationContent
, it registers some special ObjectFactory
instances for resolving some web types in the underlying BeanFactory
. This is done in WebApplicationContextUtils.registerWebApplicationScopes(..)
. One of these is the following
beanFactory.registerResolvableDependency(ServletRequest.class, new RequestObjectFactory());
When Spring scans your beans and determines that it needs to autowire the HttpServletRequest
(which is a sub type of ServletRequest
) field, it will look in its map of resolvable dependencies and get this RequestObjectFactory
.
Because this is an ObjectFactory
and the injection target is an interface type, Spring will create a Proxy of that type that delegates to the object created/returned by the RequestObjectFactory
on each request. This is done in AutowireUtils.resolveAutowiringValue(..)
.
As such, your Foo
bean does not need to be request scoped.
Upvotes: 8
Reputation: 1707
My understanding is that Spring does use a proxy, yes. Spring allows you to inject request or session-scoped objects into singleton-scope objects by injecting a proxy into the singleton.
For Spring MVC, I believe the injected proxy is backed by a ThreadLocal
variable because each request is bound do a thread. The real HttpServletRequest
is then available via this injected proxy that delegates onto the ThreadLocal
when invoked.
Therefore, you don't need to declare "Foo" to be session or request scoped and can keep it as a Singleton.
Upvotes: 1