运行流程
- 所有请求,前端控制器(DispatcherServlet)收到请求,调用doDispatch进行处理
- 根据HandlerMapping中保存的请求映射信息找到,处理当前请求的处理器执行链(包含拦截器)
- 根据当前处理器找到它的HandlerAdapter(适配器)
- 拦截器的preHandle先执行
- 适配器执行目标方法,并返回ModelAndView
1). ModelAttribute注解标注的方法提前运行
2). 执行目标方法的时候(确定目标方法用的参数):
a. 有注解(如@RequestParam),从请求中获取参数值
b. 没注解:
- 看是否是原生API(如HttpServletRequest),直接传入原生API
- 看是否为Model、Map以及其他的BindingAwareModelMap
- 如果是自定义类型:
* 看隐含模型中有没有,如果有就从隐含模型中拿
* 如果没有,再看是否有SessionAttributes标注的属性,如果是从Session中拿,如果拿不到或抛出异常
* 如果都没有,则通过反射创建对象 - 拦截器的postHandle执行
- 处理结果:(页面渲染流程)
1). 如果有异常,使用异常解析器处理异常,处理完还会返回ModelAndView
2). 调用render进行页面渲染
a. 视图解析器根据视图名得到视图对象
b. 视图对象调用render方法
3). 执行拦截器的afterCompletion
SpringMVC和Spring整合
整合的目标是为了分工明确
SpringMVC的配置文件就用来配置和网站转发逻辑以及网站功能有关的(视图解析器,文件上传解析器,直接AJAX…)
Spring的配置文件就用来配置和事务有关的(事务控制、数据源…),实际上放入 Spring 配置文件对应的 IOC 容器中的还有 Service 和 Dao
推荐的整合方法是:创建两个容器,各自独立
容器在web.xml中创建
容器创建
** Spring容器 **
使用listener来管理spring ioc容器的创建销毁
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:spring.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
** SpringMVC容器 **
在创建了前端控制器、视图解析器后,即创建了SpringMVC容器
<!-- 前端控制器 -->
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<!--
servlet启动加载,servlet原本是第一次访问创建对象
load-on-startup:服务器启动的时候创建对象,值越小优先级越高,越先创建对象
-->
<!-- contextConfigLocation:指定springmvc配置文件位置 -->
<param-name>contextConfigLocation</param-name>
<param-value>classpath:springContext.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!-- 字符编码 -->
<filter>
<filter-name>CharacterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<!-- 指定参数 -->
<init-param>
<param-name>encoding</param-name>
<param-value>utf-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>CharacterEncodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
容器扫描组件
如果在两个容器的配置文件中都直接扫描组件,就会导致同一组件被创建了两次,并且会影响事务控制
解决的办法是在配置文件的扫描标签中,规定只扫描哪些组件(context:include-filter)或排除哪些组件(context:exclude-filter)
例如springmvc只扫controller,spring扫描其他的
springmvc.xml
<!--
springmvc容器只扫描@Controller或@ControllerAdvice注解了的组件
-->
<context:component-scan base-package="com.jax" use-default-filters="false">
<context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
<context:include-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>
<mvc:annotation-driven />
spring.xml
<!--
spring容器只扫描@Controller和@ControllerAdvice以外的组件
-->
<context:component-scan base-package="com.jax" use-default-filters="true">
<context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller" />
<context:exclude-filter type="annotation" expression="org.springframework.web.bind.annotation.ControllerAdvice"/>
</context:component-scan>