SpringMVC中为什么要配置Listener和Servlet

2024-09-03 05:32

本文主要是介绍SpringMVC中为什么要配置Listener和Servlet,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一直以来,我们使用SpringMVC的时候习惯性都配置一个ContextLoaderListener,虽然曾经有过疑问,配置的这个监听器和Servlet究竟做了什么,但也没深究。

要说任何Web框架都离不开Servlet,它是一个容器,也是一种规范,你要和Web搞上关系,无非就是那么几种,监听器、过滤器和Servlet,最终都是为了切进ServletContext。

SpringMVC是基于Servlet的,直接配个Servlet不就行了嘛,但我没有真正这么做过。上次群里有个人说根本不需要配置监听器,他从来不配那玩意,也没啥事,于是我也实验了一把。

然后有了下面的总结:

ContextLoaderListener

启动方式:

<listener><listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

在contextInitialized方法中调用initWebApplicationContext方法

/*** Initialize the root web application context.*/
public void contextInitialized(ServletContextEvent event) {this.contextLoader = createContextLoader();if (this.contextLoader == null) {this.contextLoader = this;}this.contextLoader.initWebApplicationContext(event.getServletContext());
}

1. ServletContext中查找指定key的实例

WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE的实例如果存在则抛出异常,不存在则创建。

String ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE = WebApplicationContext.class.getName() + ".ROOT";

2. createWebApplicationContext(servletContext)

可以指定自己的contextClass 如果没有 则使用默认的:

org.springframework.web.context.support.XmlWebApplicationContext

如果是自己的需要实现ConfigurableWebApplicationContext(通过isAssignableFrom方法判断,注意和instanceof区别)

3. 配置和刷新WebApplicationContext

configureAndRefreshWebApplicationContext(cwac, servletContext);

  • wac.setServletContext(sc); 把servletContext放到上下文中持有
  • wac.setConfigLocation(configLocationParam); 利用servletContext的getInitParameter方法获取配置文件的位置。
  • wac.refresh(); 调用继承的refresh方法初始化IOC容器。

4. 把创建的根上下文放到servletContext中

servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,
this.context);

把这个上下文放到servletContext中。

DispatcherServlet

GenericServlet中定义了一个

 public void init() throws ServletException {}

被HttpServlet继承 HttpServletBean又继承了HttpServlet

在init方法中调用initServletBean();

protected void initServletBean() throws ServletException {}

利用模板方法模式,在子类FrameworkServlet中实现,然后在下面的方法中初始化WebApplicationContext:

initWebApplicationContext();

if (wac == null) {// No context instance was injected at construction time -> see if one// has been registered in the servlet context. If one exists, it is assumed// that the parent context (if any) has already been set and that the// user has performed any initialization such as setting the context idwac = findWebApplicationContext();
}
if (wac == null) {// No context instance is defined for this servlet -> create a local onewac = createWebApplicationContext(rootContext);
}

1. 先取rootContext

WebApplicationContext rootContext = WebApplicationContextUtils.getWebApplicationContext(getServletContext());

在这里会去servletContext中取出key为‘ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE’的WebApplicationContext作为一个rootContext

由于监听器启动创建了这个rootContext,现在可直接拿出来

2. findWebApplicationContext()

protected WebApplicationContext findWebApplicationContext() {String attrName = getContextAttribute();if (attrName == null) {return null;}WebApplicationContext wac =WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName);if (wac == null) {throw new IllegalStateException("No WebApplicationContext found: initializer not registered?");}return wac;
}

由于attrName为null,直接返回null。

3. createWebApplicationContext(rootContext)

在没有启动监听器的情况下,就没有创建父上下文,然后依然是使用 :
org.springframework.web.context.support.XmlWebApplicationContext

ConfigurableWebApplicationContext wac =(ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass);wac.setEnvironment(getEnvironment());
wac.setParent(parent);
wac.setConfigLocation(getContextConfigLocation());

4. configureAndRefreshWebApplicationContext(wac);

wac.setServletContext(getServletContext());
wac.setServletConfig(getServletConfig());
wac.setNamespace(getNamespace());
……
wac.refresh();

使用shiro报错

如果只配置一个dispatcherServlet然后使用shiro

启动时报错:

org.springframework.beans.factory.NoSuchBeanDefinitionException: No bean named ‘lifecycleBeanPostProcessor’ is defined

注意:springMVC的配置文件中包含了对lifecycleBeanPostProcessor的引用
这时候要将SpringMVC的配置文件扫描扩大到所有spring的配置文件

访问时报错:

java.lang.IllegalStateException: No WebApplicationContext found: no ContextLoaderListener registered?

关掉shiro配置后,不再报错,说明是shiro引起的,跟着shiro的配置,找到了一个突破口:DelegatingFilterProxy

<filter><filter-name>shiroFilter</filter-name><filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class><init-param><param-name>targetFilterLifecycle</param-name><param-value>true</param-value></init-param>
</filter>
<filter-mapping><filter-name>shiroFilter</filter-name><url-pattern>/*</url-pattern>
</filter-mapping>
WebApplicationContext wac = findWebApplicationContext();
if (wac == null) {throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener registered?");
}

initFilterBean()的时候调用一次findWebApplicationContext()

doFilter()时还会调用

public void doFilter(ServletRequest request, ServletResponse response, FilterChain filterChain)throws ServletException, IOException {// Lazily initialize the delegate if necessary.Filter delegateToUse = this.delegate;if (delegateToUse == null) {synchronized (this.delegateMonitor) {if (this.delegate == null) {WebApplicationContext wac = findWebApplicationContext();if (wac == null) {throw new IllegalStateException("No WebApplicationContext found: no ContextLoaderListener registered?");}this.delegate = initDelegate(wac);}delegateToUse = this.delegate;}}// Let the delegate perform the actual doFilter operation.invokeDelegate(delegateToUse, request, response, filterChain);
}protected WebApplicationContext findWebApplicationContext() {if (this.webApplicationContext != null) {// the user has injected a context at construction time -> use itif (this.webApplicationContext instanceof ConfigurableApplicationContext) {if (!((ConfigurableApplicationContext)this.webApplicationContext).isActive()) {// the context has not yet been refreshed -> do so before returning it((ConfigurableApplicationContext)this.webApplicationContext).refresh();}}return this.webApplicationContext;}String attrName = getContextAttribute();if (attrName != null) {return WebApplicationContextUtils.getWebApplicationContext(getServletContext(), attrName);}else {return WebApplicationContextUtils.getWebApplicationContext(getServletContext());}
}

出问题的关键在此:

public static WebApplicationContext getWebApplicationContext(ServletContext sc) {return getWebApplicationContext(sc, WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
}

先取attrName,如果不为null,到servletContext中取一个指定名称的WebApplicationContext。没有配置这个属性,默认都是取root webApplicationContext。

现在没有配置监听器,root webApplicationContext当然不存在,不过给它指定一个contextAttribute也许可以解决,于是我在web.xml中给shiro加了一个初始化参数:

<init-param><param-name>contextAttribute</param-name><param-value>org.springframework.web.servlet.FrameworkServlet.CONTEXT.springServlet</param-value>
</init-param>

这下竟然可以跑了,但是其它问题还没验证。反正我不推荐这种方式。

关于DelegatingFilterProxy可以看这里了解下:
http://blog.csdn.net/xh16319/article/details/9771063

总结:

  1. 监听器启动和servlet启动都会initWebApplicationContext,由各自的初始化方法来触发。

  2. 两者各自的上下文最终都会保存到servletContext中,root WebApplicationContext使用固定的attrName,dispatcherServlet使用FrameworkServlet.class.getName() + “.CONTEXT.”+servletName。

  3. 最佳实践还是监听器加servlet的配置,各司其职,父上下文做核心容器,子上下文处理web相关。如果你要把springMVC换成其他框架如struts,也不会有什么影响。

  4. 监听器可以在整个webapp启动时初始化IOC容器,并在关闭时做一些处理。

  5. 不管是基于xml和注解的Application还是WebApplication,最终都是那套,调用继承自AbstractApplicationContext的refresh()方法。

引用下stackoverflow上关于是否要监听器的回答:

In your case, no, there’s no reason to keep the ContextLoaderListener and applicationContext.xml. If your app works fine with just the servlet’s context, that stick with that, it’s simpler.

Yes, the generally-encouraged pattern is to keep non-web stuff in the webapp-level context, but it’s nothing more than a weak convention.

The only compelling reasons to use the webapp-level context are:

  • If you have multiple DispatcherServlet that need to share services
  • If you have legacy/non-Spring servlets that need access to Spring-wired services
  • If you have servlet filters that hook into the webapp-level context (e.g. Spring Security’s DelegatingFilterProxy, OpenEntityManagerInViewFilter, etc)
    None of these apply to you, so the extra complexity is unwarranted.

Just be careful when adding background tasks to the servlet’s context, like scheduled tasks, JMS connections, etc. If you forget to add to your web.xml, then these tasks won’t be started until the first access of the servlet.

You need to understand the difference between Web application context and root application context .

In the web MVC framework, each DispatcherServlet has its own WebApplicationContext, which inherits all the beans already defined in the root WebApplicationContext. These inherited beans defined can be overridden in the servlet-specific scope, and new scope-specific beans can be defined local to a given servlet instance.

相关链接:

http://docs.spring.io/spring/docs/3.0.x/spring-framework-reference/html/mvc.html

http://www.codesenior.com/en/tutorial/Spring-ContextLoaderListener-And-DispatcherServlet-Concepts

http://bbs.csdn.net/topics/391076893?page=1

这篇关于SpringMVC中为什么要配置Listener和Servlet的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/1132158

相关文章

springboot security快速使用示例详解

《springbootsecurity快速使用示例详解》:本文主要介绍springbootsecurity快速使用示例,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝... 目录创www.chinasem.cn建spring boot项目生成脚手架配置依赖接口示例代码项目结构启用s

java之Objects.nonNull用法代码解读

《java之Objects.nonNull用法代码解读》:本文主要介绍java之Objects.nonNull用法代码,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐... 目录Java之Objects.nonwww.chinasem.cnNull用法代码Objects.nonN

springboot security之前后端分离配置方式

《springbootsecurity之前后端分离配置方式》:本文主要介绍springbootsecurity之前后端分离配置方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的... 目录前言自定义配置认证失败自定义处理登录相关接口匿名访问前置文章总结前言spring boot secu

一文详解SpringBoot响应压缩功能的配置与优化

《一文详解SpringBoot响应压缩功能的配置与优化》SpringBoot的响应压缩功能基于智能协商机制,需同时满足很多条件,本文主要为大家详细介绍了SpringBoot响应压缩功能的配置与优化,需... 目录一、核心工作机制1.1 自动协商触发条件1.2 压缩处理流程二、配置方案详解2.1 基础YAML

java中使用POI生成Excel并导出过程

《java中使用POI生成Excel并导出过程》:本文主要介绍java中使用POI生成Excel并导出过程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录需求说明及实现方式需求完成通用代码版本1版本2结果展示type参数为atype参数为b总结注:本文章中代码均为

springboot简单集成Security配置的教程

《springboot简单集成Security配置的教程》:本文主要介绍springboot简单集成Security配置的教程,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,... 目录集成Security安全框架引入依赖编写配置类WebSecurityConfig(自定义资源权限规则

Java的IO模型、Netty原理解析

《Java的IO模型、Netty原理解析》Java的I/O是以流的方式进行数据输入输出的,Java的类库涉及很多领域的IO内容:标准的输入输出,文件的操作、网络上的数据传输流、字符串流、对象流等,这篇... 目录1.什么是IO2.同步与异步、阻塞与非阻塞3.三种IO模型BIO(blocking I/O)NI

java中反射(Reflection)机制举例详解

《java中反射(Reflection)机制举例详解》Java中的反射机制是指Java程序在运行期间可以获取到一个对象的全部信息,:本文主要介绍java中反射(Reflection)机制的相关资料... 目录一、什么是反射?二、反射的用途三、获取Class对象四、Class类型的对象使用场景1五、Class

SpringBoot中封装Cors自动配置方式

《SpringBoot中封装Cors自动配置方式》:本文主要介绍SpringBoot中封装Cors自动配置方式,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教... 目录SpringBoot封装Cors自动配置背景实现步骤1. 创建 GlobalCorsProperties

Spring事务中@Transactional注解不生效的原因分析与解决

《Spring事务中@Transactional注解不生效的原因分析与解决》在Spring框架中,@Transactional注解是管理数据库事务的核心方式,本文将深入分析事务自调用的底层原理,解释为... 目录1. 引言2. 事务自调用问题重现2.1 示例代码2.2 问题现象3. 为什么事务自调用会失效3