Spring MVC是严格遵守java servlet规范的一种web框架,可以打包成war包交由web容器(Tomcat或者jetty)运行。接下来就来学习下spring mvc的运行过程以及其中的细节,如何和Tomcat无缝合作,如何和spring 本身的核心功能IOC、AOP合作。
MVC 实例
接下来就搭建一个基于maven的spring mvc的实例。
项目结构
StudentController 类
@Controller@RequestMappingpublic class StudentController { @RequestMapping(value = "/h") @ResponseBody public String getStudentInfo() { return "hello world"; }}
web.xml 文件
<?xml version="1.0" encoding="UTF-8"?>contextConfigLocationWEB-INF/applicationContext.xmldemoorg.springframework.web.servlet.DispatcherServletcontextConfigLocationWEB-INF/applicationContext.xml1demo/org.springframework.web.context.ContextLoaderListener
applicationContext.xml
pom.xml
// 插件部分demoorg.apache.tomcat.maven tomcat7-maven-plugin 2.2/${file_encoding}9912tomcat7org.apache.maven.plugins maven-war-plugin 2.5org.apache.maven.plugins maven-compiler-plugin 3.3${java_source_version}${java_target_version}${file_encoding}src/main/resources
然后通过mvn tomcat7:run就可以正常启动了
Tomcat 基础
在介绍spring mvc的工作原理之前,有必要介绍下web容器的一种Tomcat。Tomcat是一个开源的web容器,严格准守servlet规范,在Tomcat中包含了各种各样的组件,层层嵌套依赖,如下图所示
catalina是最核心的组件,也可以认为Tomcat是从它开始启动的,他持有1个server容器,server容器可以包含了多个service容器,每个service容器都持有了一个connector连接器以及一个engine,engine有层层包含了host、context、wrapper等。
其中engine、host、context、wrapper又分别存在各自的一个管道pipeline以及至少一个阀门valve,阀门可以为request和response添加任何外置的功能。
Tomcat启动是由各自容器的监听器调用启动的,按照上面所说的顺序依次执行启动的。
那我们常用的servlet是在哪里的么?他是被wrapper包装的,每一个wrapper持有一个servlet,所以在xml中配置了几个servlet,则就会存在多少个wrapper。并且servlet是通过ServletContext传递上下文的。在具体的URL映射的时候,会先根据各自的servlet的URL配置在Tomcat的mappingdata中体现,经过host、context、再到选择不同类型的wrapper(包含了wildcardWrappers、extensionWrappers、defaultWrapper、exactWrappers四种wrapper类型)最后才具体到某一个servlet请求上。
DispatcherServlet init 过程
DispatcherServlet类是spring mvc中实现的了servlet规范的实体类,实现了HttpServlet类,如下图
首先需要明确一点的是,DispatcherServlet也只是一个HttpServlet类,他是被wrapper调用的init(ServletConfig config)方法进入的,设置好config之后,进入到init()方法。
PS:在当前环境中,ServletConfig是StandardWrapperFacade类,可以从中获取到在xml配置的例如contextConfigLocation数据
HttpServletBean 类
public final void init() throws ServletException { if (logger.isDebugEnabled()) { logger.debug("Initializing servlet '" + getServletName() + "'"); } // Set bean properties from init parameters. PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties); // 获取xml配置的属性,在下面贴的图片中可以看到,当DispatcherServlet没有任何配置的时候,就会抛出异常,说缺少必备的配置属性 if (!pvs.isEmpty()) { try { BeanWrapper bw = PropertyAccessorFactory.forBeanPropertyAccess(this); ResourceLoader resourceLoader = new ServletContextResourceLoader(getServletContext()); bw.registerCustomEditor(Resource.class, new ResourceEditor(resourceLoader, getEnvironment())); initBeanWrapper(bw); bw.setPropertyValues(pvs, true); // 这一步就是设置DispatcherServlet的属性值的操作 } catch (BeansException ex) { if (logger.isErrorEnabled()) { logger.error("Failed to set bean properties on servlet '" + getServletName() + "'