Spring容器与Web容器
Spring和SpringMVC容器
容器就是存放对象,管理对象的地方,容器管理Bean的整个生命周期。在一个项目中,容器可能不止有一个,而且容器存在上下级的关系,常见的一种场景是一个项目中引入Spring和SpringMVC框架,这两个框架其实就是两个容器,Spring容器管理dao和service层相关的Bean,SpringMVC容器管理controller(web)相关的bean,它两都是容器,都是存放管理bean的地方,只不过管理的域不同。
容器配置不当,可能会导致Bean多次加载,controller方法无法拦截等问题。
例如,spring-mvc.xml文件配置SpringMVC相关的扫描到controller层
<context:component-scan base-package="cn.wonkey.**.controller"/>
spring-service.xml扫描包加载service
<context:component-scan base-package="cn.wonkey.**.service"/>
Spring容器和SpringMVC容器是父子容器的关系。Spring容器是父容器,SpringMVC容器是子容器,子容器可以访问父容器中的对象,但是父容器中不可以访问子容器中的对象,编程的具体表现就是controller可以直接访问service对象,但是在service中确不能访问controller对象。但就好比继承的特点那样,子类可以调用父类的任何方法,而父类只能调用其本身的方法。
在Spring的具体实现上,子容器和父容器都是通过ServletContext的setAttribute方法放到ServletContext中的。但是,ContextLoaderListener会先于DispatcherServlet创建ApplicationContext,DispatcherServlet在创建ApplicationContext时会先找到由ContextLoaderListener所创建的ApplicationContext,再将后者的ApplicationContext作为参数传给DispatcherServlet的ApplicationContext的setParent()方法。也就是说,子容器的创建依赖于父容器的创建,父容器先于子容器创建。
Web容器
Web容器是管理Servlet,以及监听器(Listener)和过滤器(Filter)的。比如Tomcat、Undertow、Jetty等,当然他们也被称为Servlet容器,大多数Servlet容器同时提供了web容器的功能,Tomcat可以看做是一个”HTTP服务器 + Servlet容器”,也就是说Servlet容器可以独立运行web应用。Apache仅仅是一个web容器,不包含servlet容器,不能处理servlet请求。web容器管理的Servlet,监听器和过滤器不在Spring和SpringMVC掌控范围内。因此,我们无法在这些类中直接使用Spring注解的方式来注入我们需要的对象,是无效的,web容器是无法识别的。
假若项目中有一验证码的功能,是由Servlet实现的一个类(KaptchaServlet),如果我们把它交给spring IOC管理,那么web容器是如何获得这个Servlet呢?web容器在找这个验证码相关的servlet类时,因为不能直接注入,所以要依赖于ContextLoaderListener这一个类,通常我们会在整合SSM时在web.xml中添加ContextLoaderListener监听器类。通过ContextLoaderListener,Servlet容器就可以和Spring容器和SprngMVC容器进行通信。
三者的具体关系如下图:
容器的启动流程
(1)Tomcat在启动时给每个web应用创建一个全局的上下文环境,这个上下文就是ServletContext,并且为后面的Spring容器提供宿主环境。
(2)在web.xml中提供有ContextLoaderListener监听器,在容器启动时,会触发容器的初始化事件,Spring的ContextLoaderListener会监听到这个事件,它的contextInitialized方法会被调用,在这个方法中,spring会初始化一个启动上下文WebApplicationContext,它就是spring的IoC容器。在这个IoC容器初始化完毕后,Spring以WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE为属性Key,将其存储到ServletContext中,便于获取。
(3)ContextLoaderListener初始化完毕之后,开始初始化web.xml中配置的servlet,servlet可以有多个。以DispatcherServlet为例,这个Servlet是一个标准的前端控制器,用于转发、匹配、处理每一个Servlet请求。Servlet采用延迟加载策略,当第一个请求到达时,Tomcat发现DispatcherServlet没有实例化就会调用DispatcherServlet的init方法,它会建立自己的容器,这个 就是SpringMVC容器,用来管理持有Spring MVC相关的Bean。特别地,在建立DispatcherServlet自己的容器时,会利用WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE先从ServletContext中获取之前的根上下文(即WebApplicationContext)作为自己上下文的parent上下文。有了这个parent上下文之后,再初始化自己持有的上下文。SpringMVC的创建是依赖于Spring容器的。
参考: