在Spring
应用中,将一个较大作用域的bean
注入到一个较小作用域的bean
中是很自然的一件事情,因为往较小作用域bean
中注入较大作用域bean
时,较大作用域的bean
已经存在了。但是,如果反过来,将一个较小作用域的bean
往一个较大作用域的bean
中注入时,较小作用域的bean
可能根本不存在,比如Spring MVC
应用中,如果要将一个ServletRequest
注入到一个单例作用域的Web
控制器组件中,则该注入动作会在该单例Web
控制器组件初始化时发生(容器对单例bean
的创建过程决定的),而通常此时容器正在启动,任何用户请求尚未到达,所以注入任何一个ServletRequest
听起来都是不正确的。以此类推,将一个较小作用域的bean
往一个较大作用域的bean
中注入概念上讲是不可行的。
但通过实践,我们明确知道,在一个Spring MVC
应用中,将一个ServletRequest
注入到一个单例作用域的Web
控制器中是可以的。这又是为什么呢 ?这里面,我们有两个方面的问题要解答 :
- 注入的
bean
是什么?为什么ServletRequest
能够注入成功 ? - 注入的
bean
如何工作的?为什么请求过程中访问注入的ServletRequest
能访问到正确的信息?
为了解答上面两个问题,我们准备了一个例子演示ServletRequest
的注入。该例子如下:
@Controller
public class HomeController {
ServletRequest request;
@Autowired
public void setRequest(ServletRequest request){
this.request=request;
}
@RequestMapping("/")
@ResponseBody
public String home() {
return "Hello "+ request.getParameter("name");
}
}
这是Spring MVC
项目中的一个Web
控制器类HomeController
,使用了注解@Controller
。另外这里在setRequest
方法上使用注解@Autowired
方便调试模式下加断点观察注入的对象。
基于这个例子中,我们不难推断出如下结论,实际上也是事实 :
- 因为注解
@Controller
,容器中会有一个单例bean HomeController
, request
是单例bean HomeController
的属性,所以该属性会在bean HomeController
创建过程中注入。
换句话讲,此时request
对象必须已经存在并且作用域不能小于bean HomeController
的作用域。
另外我们也希望 : 每次替换不同的参数值{name}
访问首页http://localhost:8080?name={name}
时,request
总是表示针对该次访问的请求。实际上这也是事实:
http://localhost:8080/?name=John ==对应输出==> Hello John
http://localhost:8080/?name=Snow ==对应输出==> Hello Snow
到这里,我们的问题就凸显出来了 : bean HomeController
创建时还没有任何请求发生,也无法预知会发生什么样的请求,但注入的该request
对象在请求发生时却能正确地访问到用户请求,那么,该现象背后到底是怎么回事呢 ?
接下来,基于上述例子,我们将分两部分分析现象背后的工作原理,进而你可以举一反三地思考将一个较小作用域的bean
往一个较大作用域的bean
中进行注入的可行性:
Spring MVC : ServletRequest 注入原理分析 1 : 为什么可以注入成功?
Spring MVC : ServletRequest 注入原理分析 2 : 为什么可以正确访问?