Spring框架与Springmvc框架容器详解

spring框架与Springmvc框架概述

现在做为java开发的程序员,我相信基本上每个人都听说过Spring框架,而SpringMVC框架也逐渐的替代了原来的一些MVC框架,成为了Web层框架的佼佼者,但是SpringMVC框架其实本身也是Spring 框架的一个后续产品,这两个框架在结合到一起使用的时候Spring会有自己的IOC容器,而Springmvc也会有自己的ioc容器,两个框架一起使用的时候会有一些什么样的问题,或者两个框架的容器直接是一种什么样的关系,今天我们一起来讨论一下
spring框架与Springmvc框架整合使用的细节


Springmvc框架的基本使用
在理解spring框架与Springmvc框架一起使用时的两个容器关系之前,我们先对SpringMVC框架的使用步骤做一个简单介绍,以下是SpringMVC在使用的时候的基本代码
1. 在web项目的web.xml文件中配置Springmvc前端控制器
2. 在项目中创建springmvc的配置文件

3. 当做了前面两个配置之后就能直接在项目中创建Controller类和方法去接收请求

Springmvc的基本执行过程
当我们做了上面的一些事情之后,服务器在启动的时候就会去创建Springmvc的前端控制器(DispatcherServlet),这个Servlet一创建就会帮助我们去加载Springmvc的配置文件,从而初始化Springmvc的容器,创建所有的Controller。以后发送请求的时候,执行的过程会经过Springmvc的各个组件,大致流程如下图



spring框架与Springmvc框架整合使用


1. spring框架与Springmvc框架整合使用的思路
当我们在实际案例中去使用SpringMVC的时候由于我们需要在Controller中注入Service,所以我们必须要保证Controller对象创建之前Service对象要创建,而我们都知道Service最终都会交给Spring去管理,所以Spring的容器应该要先于Springmvc的容器创建才行
2.具体代码实现
代码实现很简单,想要web项目在加载Springmvc的配置之前先加载Spring的配置,只需要在web.xml文件中配置一下Spring的监听器和配置文件位置就可以

spring框架与Springmvc框架一起使用后两个容器的关系
spring框架与Springmvc框架一起使用后Spring会有Spring的容器,在Spring容器中会创建Service,Dao层的对象,当然也会有SpringMVC的容器,SpringMVC容器会创建Controller对象,那么当我们去发送请求到Controller中去的时候,Controller中会注入Service对象,SpringMVC容器中的Controller对象是如何去注入Spring容器中的service对象的,相信部分刚接触SpringMVC框架的学者会在这个地方有疑问,那么接下来我们就一起去跟着Spring和SpringMVC源码一起来揭秘一下这个问题:
Spring容器加载
首先我们都知道,服务器一启动会去去执行Spring监听器中的代码加载Spring配置文件初始化容器,我们先去看看这一部分Spring是如何去完成的。
通过这一部分源码我们发现,服务器已启动,listener中确实会帮我们去创建Spring容器,但是他还会做一件事情,就是将容器保存到ServletContext域对象中
SpringMVC容器加载
从Springmvc源代码中可以看到SpringMVC容器初始化的过程如下图:

SpringMVC的容器在初始化的时候,会把原来已经保存在ServletContext中的Spring容器设置成SpringMVC的父容器,也就是说Spring容器和SpringMVC容器直接是一种逻辑上的父子关系,SpringMVC容器是子容器,Spring容器是父容器,所以在SpringMVC容器中的Controller能注入Spring容器中的service对象
总结
通过前面的分析,这样我们在清楚了spring和springMVC的父子容器关系;所以在项目中当我们同时去使用Spring框架和Springmvc框架的时候,Springmvc中的Controller能够访问到Springmvc父容器(Spring容器)中的对象
来源:http://bbs.itheima.com/thread-443226-1-1.html

spring和springMVC父子容器的原理

转载自:https://my.oschina.net/jast90/blog/282773#

要想很好理解这三个上下文的关系,需要先熟悉spring是怎样在web容器中启动起来的。spring的启动过程其实就是其IoC容器的启动过程,对于web程序,IoC容器启动过程即是建立上下文的过程。

spring的启动过程:

  1. 首先,对于一个web应用,其部署在web容器中,web容器提供其一个全局的上下文环境,这个上下文就是ServletContext,其为后面的spring IoC容器提供宿主环境;
  2. 其次,在web.xml中会提供有contextLoaderListener。在web容器启动时,会触发容器初始化事件,此时contextLoaderListener会监听到这个事件,其contextInitialized方法会被调用,在这个方法中,spring会初始化一个启动上下文,这个上下文被称为根上下文,即WebApplicationContext,这是一个接口类,确切的说,其实际的实现类是XmlWebApplicationContext。这个就是spring的IoC容器,其对应的Bean定义的配置由web.xml中的context-param标签指定。在这个IoC容器初始化完毕后,spring以WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE为属性Key,将其存储到ServletContext中,便于获取;
  3. 再次,contextLoaderListener监听器初始化完毕后,开始初始化web.xml中配置的Servlet,这个servlet可以配置多个,以最常见的DispatcherServlet为例,这个servlet实际上是一个标准的前端控制器,用以转发、匹配、处理每个servlet请求。DispatcherServlet上下文在初始化的时候会建立自己的IoC上下文,用以持有spring mvc相关的bean。在建立DispatcherServlet自己的IoC上下文时,会利用WebApplicationContext.ROOTWEBAPPLICATIONCONTEXTATTRIBUTE先从ServletContext中获取之前的根上下文(即WebApplicationContext)作为自己上下文的parent上下文。有了这个parent上下文之后,再初始化自己持有的上下文。这个DispatcherServlet初始化自己上下文的工作在其initStrategies方法中可以看到,大概的工作就是初始化处理器映射、视图解析等。这个servlet自己持有的上下文默认实现类也是mlWebApplicationContext。初始化完毕后,spring以与servlet的名字相关(此处不是简单的以servlet名为Key,而是通过一些转换,具体可自行查看源码)的属性为属性Key,也将其存到ServletContext中,以便后续使用。这样每个servlet就持有自己的上下文,即拥有自己独立的bean空间,同时各个servlet共享相同的bean,即根上下文(第2步中初始化的上下文)定义的那些bean。

说完了spring上下文的初始化过程,这三个上下文的关系应该就了解了。如还是不太清楚,我就爱莫能助了,只能自行看代码去了。

===============================================================================================================

最近在做项目时牵扯到有关父子上下文的概念。

何为父子上下文呢?

父上下文:

使用listener监听器来加载配置文件,如下:

?
1
2
3
<listener>   
  <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>   
</listener>

Spring 会创建一个WebApplicationContext上下文,称为父上下文(父容器),保存在 ServletContext中,key是WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE的值。

可以使用Spring提供的工具类取出上下文对象:WebApplicationContextUtils.getWebApplicationContext(ServletContext);

子上下文:

使用Spring MVC 来处理拦截相关的请求时,会配置DispatchServlet:

?
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<servlet>
    <servlet-name>dispatcherServlet</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet
    </servlet-class>
    <init-param>
        <param-name>contextConfigLocation</param-name>
        <param-value>/WEB-INF/applicationContext-mvc.xml</param-value>
    </init-param>
    <load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
    <servlet-name>dispatcherServlet</servlet-name>
    <url-pattern>/</url-pattern>
</servlet-mapping>

每个DispatchServlet会有一个自己的上下文,称为子上下文,它也保存在 ServletContext中,key 是"org.springframework.web.servlet.FrameworkServlet.CONTEXT"+Servlet名称。当一 个Request对象产生时,会把这个子上下文对象(WebApplicationContext)保存在Request对象中,key是 DispatcherServlet.class.getName() + ".CONTEXT"。

可以使用工具类取出上下文对象:RequestContextUtils.getWebApplicationContext(request);

父上下文(父容器)和子上下文(子容器)的访问权限:

子上下文可以访问父上下文中的bean,但是父上下文不可以访问子上下文中的bean。

父上下文使用与否

方案一,传统型:

父上下文容器中保存数据源、服务层、DAO层、事务的Bean。

子上下文容器中保存Mvc相关的Action的Bean.

事务控制在服务层。

由于父上下文容器不能访问子上下文容器中内容,事务的Bean在父上下文容器中,无法访问子上下文容器中内容,就无法对子上下文容器中Action进行AOP(事务)。

当然,做为“传统型”方案,也没有必要这要做。

 

方案二,激进型:

Java世界的“面向接口编程”的思想是正确的,但在增删改查为主业务的系统里,Dao层接口,Dao层实现类,Service层接口,Service层实现类,Action父类,Action。再加上众多的O(vo\po\bo)和jsp页面。写一个小功能 7、8个类就写出来了。 开发者说我就是想接点私活儿,和PHP,ASP抢抢饭碗,但我又是Java程序员。最好的结果是大项目能做好,小项目能做快。所以“激进型”方案就出现了-----没有接口、没有Service层、还可以没有众多的O(vo\po\bo)。那没有Service层事务控制在哪一层?只好上升的Action层。

本文不想说这是不是正确的思想,我想说的是Spring不会限制你这样做。

由于有了父子上下文,你将无法实现这一目标。解决方案是只使用子上下文容器,不要父上下文容器 。所以数据源、服务层、DAO层、事务的Bean、Action的Bean都放在子上下文容器中。就可以实现了,事务(注解事务)就正常工作了。这样才够激进。

总结:不使用listener监听器来加载spring的配置文件,只使用DispatcherServlet来加载spring的配置,不要父子上下文,只使用一个DispatcherServlet,事情就简单了,什么麻烦事儿也没有了。