Spring MVC Web项目Servlet与Listener详述

0.文章起因

最近在搭建Spring MVC + Spring JPA + Hibernate Demo项目时,在配置过程中不小心将web.xml文件中关于listener的配置注释掉了,结果在项目启动时报了如下的错误信息

org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userController': Unsatisfied dependency expressed through field 'userService'; nested exception is org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userService': Unsatisfied dependency expressed through field 'userRepository'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'cn.xue.repository.UserRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    ...
    at java.lang.Thread.run(Thread.java:748)
Caused by: org.springframework.beans.factory.UnsatisfiedDependencyException: Error creating bean with name 'userService': Unsatisfied dependency expressed through field 'userRepository'; nested exception is org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'cn.xue.repository.UserRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at ...
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:584)
    ... 67 more
Caused by: org.springframework.beans.factory.NoSuchBeanDefinitionException: No qualifying bean of type 'cn.xue.repository.UserRepository' available: expected at least 1 bean which qualifies as autowire candidate. Dependency annotations: {@org.springframework.beans.factory.annotation.Autowired(required=true)}
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.raiseNoMatchingBeanFound(DefaultListableBeanFactory.java:1507)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1104)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1065)
    at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:584)
    ... 80 more

1.Spring MVC Web项目在tomcat中执行过程

在具体分析Spring MVC Web项目中Servlet和Listener之前,这里先简要整理一下Spring MVC Web项目在tomcat中初始化及当接收一个请求后处理过程。

1.1 初始化过程

  • Tomcat Web容器在启动时,会为每个WEB应用程序都创建一个唯一对应的ServletContext对象,它代表当前web应用web容器提供其一个全局的上下文环境,它为Spring IoC容器提供宿主环境,用于存放所有的Servlet, Filter, Listener。Spring MVC 启动的时候主要涉及到DispatcherServlet 与 ContextLoaderListener
  • Tomcat启动时加载web.xml文件,通过其中的各种配置来启动项目。web.xml有多项标签,在其加载的过程中顺序依次为:context-param listener fileter servlet,并且需要特别说明的是如果同类标签多次出现加载顺序以出现顺序为准

1.2 请求处理过程

  • tomcat接收到一个具体请求,web容器根据url-path映射到与之相匹配的DispatcherServlet进行处理
  • DispatcherServlet将请求交给HandlerMapping,让它找出对应请求的HandlerExecutionChain对象,HandlerExecutionChain返回拦截器和处理器。HandlerExecutionChain是一个执行链,它包含一个处理该请求的Handler(处理器,也就是我们常习惯写作xxxController),同时还可能包括若干个对该请求实施拦截的Handler Interceptor(拦截器)

2.DispatcherServlet 与 ContextLoaderListener

一个常见的web.xml中关于DispatcherServlet 与 ContextLoaderListener的配置如下:

...
 <listener>
    <!--注册Spring的ServletContext监听器,监听到服务器启动时,自动执行ContextLoaderListener的方法初始化Spring-->
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
  </listener>
  <context-param>
    <!--加载Spring的配置文件,随着监听器触发,Spring调用这里,找到Spring的核心配置文件-->
    <param-name>contextConfigLocation</param-name>
    <param-value>classpath:spring/*.xml</param-value>
  </context-param>

  <!--SpringMVC的相关设置-->
  <servlet>
    <!--SpringMVC是基于Servlet使用中央处理器处理页面请求,配置中央处理器的全路径-->
    <servlet-name>SpringMVC</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
    <init-param>
      <!--当页面有请求时,DispatcherServlet对象调用这里,获取到SpringMVC的核心配置文件-->
      <param-name>contextConfigLocation</param-name>
      <param-value>classpath:springMVC/*.xml</param-value>
    </init-param>
    <!--优先级,数字越小级别越高-->
    <load-on-startup>1</load-on-startup>
  </servlet>
  <servlet-mapping>
    <!--指定请求的映射,链接为指定形式时,使用Servlet处理,其他链接不执行Servlet-->
    <servlet-name>SpringMVC</servlet-name>
    <url-pattern>/</url-pattern>
  </servlet-mapping>
...
  • ContextLoaderListener 作为一个Listener会首先启动,创建一个WebApplicationContext用于加载除Controller等Web组件以外的所有bean,这个ApplicationContext作为根容器存在,对于整个Web应用来说,只能存在一个,也就是父容器,会被所有子容器共享,子容器可以访问父容器里的bean. 简单说它的作用就是启动Web容器时,自动装配ApplicationContext的配置信息。因为它实现了ServletContextListener这个接口,在web.xml配置这个监听器,启动容器时,就会默认执行它实现的方法。另外在ContextLoaderListener中关联了ContextLoader这个类,所以整个加载配置过程由ContextLoader来完成
    其初始化过程对于xml配置文件方式下和注解方式下略有差异:

    • i. xml配置下会直接创建ContextLoaderListener,然后在contextInitialized方法中初始化WebApplicationContext。
    • ii. 如果使用的是注解配置的方式,则通过AnnotationConfigWebApplicationContext获取一个WebApplicationContext之后传给ContextLoadListener,之后再contextInitialized方法中调用父类ContextLoader的initWebApplicationContext进行初始化。
      DispatcherServlet
  • DispatcherServlet作为ServletContext之中很可能唯一的一个Servlet被初始化,作为整个Web应用的前端控制器进行请求转发。
    初始化过程如下:

    • i.XML配置下,生成默认的DispatcherServlet,初始化时通过init-param中的contextConfigLocation指定的配置文件创建WebApplicationContext。
    • ii.注解配置方式下,通过AnnotationConfigWebApplicationContext读取JavaConfig后生成WebApplicationContext,传递给DispatcherServlet进行构造。DispatcherServlet构造完成之后,调用init方法进行初始化,将DispatcherServlet关联的WebApplicationContext的父容器设为之前ContextLoaderListener创建的WebApplicationContext。DispatcherServlet关联的WebApplicationContext会在请求到来的时候被设为request的attribute暴露给handlers和之后的web组件。

3.总结

对于一个使用SpringMVC构建的Web应用来说,ContextLoaderListener虽然只能加载一个根容器,但通俗些来说,如果没有必要的bean需要通过ContextLoader加载,它其实是可选的,而DispatcherServlet作为请求转发处理返回结果的核心是比不可少的,而且可以不唯一

©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,616评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 91,020评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 159,078评论 0 349
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 57,040评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,154评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,265评论 1 292
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,298评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 38,072评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,491评论 1 306
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,795评论 2 328
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,970评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,654评论 4 337
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,272评论 3 318
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,985评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,223评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,815评论 2 365
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,852评论 2 351

推荐阅读更多精彩内容