spring容器和springMVC容器

spring和springMVC父子容器:

何为父子上下文呢?

父上下文:

使用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,事情就简单了,什么麻烦事儿也没有了



借用更多解释:

首先,必须先理解springweb容器的是如何运行的。

 1 对于一个web应用,当其部署在web容器上时,web容器就会给其提供一个全局的上下文环境ServletContext. 这个上下文环境将为后面的所有的spring有关的容器提供宿主环境,相当于是提供一个仓库。后面的所有的容器都将装在里面。

补充:什么是ServletContex对象?

    ServletContext对象是WEB服务器中的一个已知路径的根。例如Servlet上下文被定位到http://localhost:8080/mybatisTest。以/mybatisTest请求路径(上下文路径)开始的所有请求被发送到与此ServletContext关联的WEB应用程序。

    Servlet容器在Web应用程序加载时创建ServletContext对象,ServletContext对象可以被Web应用程序中所有的Servlet所访问。

2 紧接着,在web.xml 中会提供有contextLoaderListener。在web容器启动时,会触发容器初始化事件,此时contextLoaderListener会监听到这个事件,其contextInitialized方法会被调用。初始化配置文件;

3 随后,开始初始化web.xml 中配置的Servletservlet可以配置多个。在spring中:

以最常见的DispatcherServlet为例,这个servlet实际上是一个标准的前端控制器,用以转发、匹配、处理每个servlet请求。DispatcherServlet上下文在初始化的时候会建立自己的IoC上下文,用以持有spring mvc相关的bean。这个DispatcherServlet初始化自己上下文的工作在其initStrategies方法中可以看到,大概的工作就是初始化处理器映射、视图解析等。当然,在初始化自己的上下文前提下,先初始化parent上下文。每个servlet就持有自己的上下文。

 

好了,切入正题:何为父子容器?

Spring正题框架的核心概念中, 容器是核心思想。用来管理bean 的整个生命周期,在一个项目中,容器不仅仅只有一个。容器之间也存在上下层关系,当一个项目中引入SpringSpringMVC这两个框架,其实就是2个容器,Spring容器,SpringMVC是其子容器,

子容器可以访问父容器对象,而父容器不可以访问子容器对象。(对父子属性不成立)

该如何理解子容器可以访问父容器对象,而父容器不可以访问子容器对象? 举个栗子

Web层的congtroller定义的类中,可以引用service层的对象(的接口,IC即对象),相反则不成立。

 

好了,接下来进入讨论的关键:

当我们使用注解开发的时候,对于Bean 的注册,我们使用如下的配置:

<context:component-scan base-package=com.it.wu" />


Spring参考手册中讲到,该配置的功能是扫描默认包下的所有的@Component注解,并且自动注册到容器(生成bean,简化了xml文件中配置的繁琐工作)中,同时也扫描@Controller@Service@Respository这三个注解注册他们所对应的bean到对应的容器中因为他们是继承自@Component

以上的这个注解配置,使用于父类和子类容器。

当然,还有一个SpringMVC相关的配置 

      <mvc:annotation-driven />

 

经过验证,这个是必须要配置的,因为它是和@RequestMapping结合使用的,这里补充下与SpringMVC框架相关的知识点。

处理器映射器(HandlerMapping):用来处理用户请求的URL找到具体Controller的,有两种方式; 
处理器适配器(HandlerAdapter): 用来处理具体Controller对应的具体方法的,有3种;

@RequestMapping这个注解的主要目的就是对具体的Controller类或者方法进行注册,以方便HandlerMapping用来处理请求的映射。但是@RequestMapping需要结合<mvc:annotation-driven />使用才能生效。

 

问题来源?

在实际的开发中,由于有了强大的注解功能,很多基于XML的配置方式已经被替代,但是在实际项目中,同时配置SpringSpringMVC时会出现一些奇怪的异常,比如Bean被多次加载,多次实例化,或者依赖注入时,Bean不能被自动注入,但是明明你已经将该Bean注册了。

Spring配置文件applicationContext.xmlSpringMVC配置文件applicationContext-MVC.xml,这样项目中就有2个容器

配置方式1

applicationContext.xml中配置了<context:component-scan base-package=“com.it" />,负责所有需要注册的Bean的扫描工作。applicationContext-MVC.xml中配<mvc:annotation-driven />,负责springMVC相关注解的使用,启动项目发现,springMVC失效,无法进行跳转,进行调试,发现springMVC容器中的请求好像没有映射到具体controller中;

 配置方式2:

将<context:component-scan base-package=“com.it" />和<mvc:annotation-driven />都配置在

applicationContext-MVC.xml中。Ok,可以运行。

为何?我用方式1.父容器进行扫描注册所有的bean,自容器负责使用相关的注解,难道不可以吗?查看源码发现的确不可以。

方式2却可以。原因是:

springMVC初始化时,会寻找所有当前容器中的所有@Controller注解的Bean在方式1中,而当前容器springMVC容器中却没有注册的Bean。所有的@Controller配置的Bean都注册在Spring父容器中了

What?难道就没有方式1的解决方案了吗?

答案是有的!在默认的情况下,自类容器不可以获得父类容器的Bean,当然我们可以在子类容器中配置使用父类的容器。

在applicationContext-MVC.xml中再配置如下的一段代码:

<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping">

        <property name="detectHandlerMethodsInAncestorContexts">

            <value>true</value>

        </property>

</bean>

 

好了,前面的两种方式不是最完美的。

下面介绍第三种比较好的方式:

在实际工程中,会包括很多配置,根据不同的业务模块来划分,所以我们一般思路是各负其责,明确边界,Spring根容器负责所有其他非controllerBean的注册,而SpringMVC只负责controller相关的Bean的注册。第三种方案如下:

Spring容器配置,排除所有@controllerBean

<context:component-scan base-package="com.fsnip.open">

        <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/>

    </context:component-scan>

 

SpringMVC容器配置,让其只包括@controllerBean

<context:component-scan base-package="com.fsnip.open" use-default-filters="false">

        <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller" />

    </context:component-scan>

 

引用一下参阅的博客的图片。 原图地址http://jinnianshilongnian.iteye.com/blog/1602617


 

猜你喜欢

转载自blog.csdn.net/jxpCoder/article/details/77775339