一、环境搭建
- 创建一个web项目。
- 如果是maven项目,则直接在pom中加入springMvc依赖
<dependencies>
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.1.0</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.1.9.RELEASE</version>
</dependency>
</dependencies>
如果不是就从把这些jar包丢到lib里面
二、SpringMVC配置
- 在web中配置servlet,
url-pattern中的/ 和 / * 有区别
<url-pattern> / </url-pattern> 不会匹配到*.jsp,即:*.jsp不会进入spring的 DispatcherServlet类 。
<url-pattern> /* </url-pattern> 会匹配*.jsp,会出现返回jsp视图时再次进入spring的DispatcherServlet 类,导致找不到对应的controller所以报404错。
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<!--SpringMvc开始-->
<servlet>
<servlet-name>springMvc</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>springMvc</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<!--SpringMvc结束-->
</web-app>
- 创建springMvc-servlet.xml文件,放到WEB-INF路径下
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/tool http://www.springframework.org/schema/tool/spring-tool.xsd http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd">
<mvc:annotation-driven />
<context:component-scan base-package="com.tianzeng"/>
<!--通用视图解析器 -->
<bean id="viewResolverCommon"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/views" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
三、测试
创建Controller:
@Controller
public class HelloWorld {
@ResponseBody
@RequestMapping("/helloworld")
public String helloWorld(){
return "Hello world";
}
@RequestMapping("/hello")
public String hello(Model model){
model.addAttribute("msg","hello");
return "/hello";
}
}
创建资源文件:
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${msg}
</body>
</html>
然后把项目丢到tomcat中启动,访问/helloworld就能够看见Hello world了,访问/hello能够看到hello
四、源码分析
SpringMVC的servlet的有三个层次:分别是HttpServletBean、FrameworkServlet、DispatcherServlet。
初始化
- HttpServletBean初始化
HttpServletBean直接继承了java的HttpServlet,创建时自动调用init方法进行初始化
@Override
public final void init() throws ServletException {
if (logger.isDebugEnabled()) {
logger.debug("Initializing servlet '" + getServletName() + "'");
}
// Set bean properties from init parameters.
try {
PropertyValues pvs = new ServletConfigPropertyValues(getServletConfig(), this.requiredProperties);
// 使用BeanWrapper构造DispatcherServlet
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) {
logger.error("Failed to set bean properties on servlet '" + getServletName() + "'", ex);
throw ex;
}
// 子类覆盖此方法,一起给初始化了
initServletBean();
if (logger.isDebugEnabled()) {
logger.debug("Servlet '" + getServletName() + "' configured successfully");
}
}
- FrameworkServlet初始化
FrameworkServlet是HttpServletBean类的子类,所以初始化操作是覆盖initServletBean
@Override
protected final void initServletBean() throws ServletException {
getServletContext().log("Initializing Spring FrameworkServlet '" + getServletName() + "'");
if (this.logger.isInfoEnabled()) {
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization started");
}
long startTime = System.currentTimeMillis();
try {
// initServletBean的主要方法,初始化webApplicationContext
this.webApplicationContext = initWebApplicationContext();
// 这个里面没有任何实现方法
initFrameworkServlet();
}
catch (ServletException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
catch (RuntimeException ex) {
this.logger.error("Context initialization failed", ex);
throw ex;
}
if (this.logger.isInfoEnabled()) {
long elapsedTime = System.currentTimeMillis() - startTime;
this.logger.info("FrameworkServlet '" + getServletName() + "': initialization completed in " +
elapsedTime + " ms");
}
}
protected WebApplicationContext initWebApplicationContext() {
WebApplicationContext rootContext =
WebApplicationContextUtils.getWebApplicationContext(getServletContext()); // 得到根上下文
WebApplicationContext wac = null;
if (this.webApplicationContext != null) {
wac = this.webApplicationContext;
if (wac instanceof ConfigurableWebApplicationContext) {
ConfigurableWebApplicationContext cwac = (ConfigurableWebApplicationContext) wac;
if (!cwac.isActive()) {
if (cwac.getParent() == null) {
cwac.setParent(rootContext);
}
configureAndRefreshWebApplicationContext(cwac);
}
}
}
if (wac == null) {
wac = findWebApplicationContext();
}
if (wac == null) {
wac = createWebApplicationContext(rootContext); // 创建一个WebApplicationContext
}
if (!this.refreshEventReceived) {
onRefresh(wac); // 模板方法,子类DispatcherServlet会覆盖这个方法进行初始化
}
if (this.publishContext) {
String attrName = getServletContextAttributeName();
getServletContext().setAttribute(attrName, wac); // 将新创建的容器设置到ServletContext中去
if (this.logger.isDebugEnabled()) {
this.logger.debug("Published WebApplicationContext of servlet '" + getServletName() +
"' as ServletContext attribute with name [" + attrName + "]");
}
}
return wac;
}
- DispatcherServlet初始化
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
//初始化多媒体解析器
initMultipartResolver(context);
//初始化位置解析器
initLocaleResolver(context);
//初始化主题解析器
initThemeResolver(context);
//初始化HandlerMappings
initHandlerMappings(context);
// 初始化HandlerAdapters
initHandlerAdapters(context);
//初始化异常解析器
initHandlerExceptionResolvers(context);
//初始化请求到视图名转换器
initRequestToViewNameTranslator(context);
//初始化视图解析器
initViewResolvers(context);
//初始化FlashMapManager
initFlashMapManager(context);
}
请求处理
- HttpServletBean请求处理
HttpServletBean没有进行任何请求处理,只是参与了容器的初始化操作 - FrameworkServlet请求处理
service方法是HttpServlet中的方法,servlet容器把所有请求发送到该方法,该方法默认行为是转发http请求到doXXX方法中,如果重载了该方法,默认操作被覆盖,不再进行转发操作
FrameworkServlet重写了service方法,如果Http请求为PATCH则使用processRequest处理,否则使用父类的service去处理
但是除了doOptions、doTrace这两个方法FrameworkServlet用了自己的实现,其他的处理最后都使用的是processRequest
@Override
protected void service(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
if (RequestMethod.PATCH.name().equalsIgnoreCase(request.getMethod())) {
processRequest(request, response);
}
else {
super.service(request, response);
}
}
processRequest是FrameworkServlet这个类中最核心的方法
protected final void processRequest(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
long startTime = System.currentTimeMillis();
Throwable failureCause = null;
LocaleContext previousLocaleContext = LocaleContextHolder.getLocaleContext();
LocaleContext localeContext = buildLocaleContext(request);
RequestAttributes previousAttributes = RequestContextHolder.getRequestAttributes();
ServletRequestAttributes requestAttributes = buildRequestAttributes(request, response, previousAttributes);
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
asyncManager.registerCallableInterceptor(FrameworkServlet.class.getName(), new RequestBindingInterceptor());
initContextHolders(request, localeContext, requestAttributes);
// 上面一大堆都是用于初始化一些乱七八糟的东东,下面的才是处理请求的
try {
// 模板方法,交由子类DispatcherServlet去处理
doService(request, response);
}
catch (ServletException ex) {
failureCause = ex;
throw ex;
}
catch (IOException ex) {
failureCause = ex;
throw ex;
}
catch (Throwable ex) {
failureCause = ex;
throw new NestedServletException("Request processing failed", ex);
}
finally {
resetContextHolders(request, previousLocaleContext, previousAttributes);
if (requestAttributes != null) {
requestAttributes.requestCompleted();
}
if (logger.isDebugEnabled()) {
if (failureCause != null) {
this.logger.debug("Could not complete request", failureCause);
}
else {
if (asyncManager.isConcurrentHandlingStarted()) {
logger.debug("Leaving response open for concurrent processing");
}
else {
this.logger.debug("Successfully completed request");
}
}
}
publishRequestHandledEvent(request, response, startTime, failureCause);
}
}
- DispatcherServlet请求处理
DispatcherServlet对于请求分为两步,第一步是请求处理,第二步是渲染页面
如果请求进来,会调用DispatcherServlet的doService方法去进行处理
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
" processing " + request.getMethod() + " request for [" + getRequestUri(request) + "]");
}
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
attributesSnapshot = new HashMap<String, Object>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
// 上面对请求进行了一些处理,doDispatch才是去处理请求的方法
try {
doDispatch(request, response);
}
finally {
if (!WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
}
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
// 检查是不是上传请求
processedRequest = checkMultipart(request);
multipartRequestParsed = (processedRequest != request);
// 去找对应的请求处理器
mappedHandler = getHandler(processedRequest);
// 如果找不到对应的请求处理器则直接返回404错误
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// 根据请求处理器,获取执行操作的请求适配器
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// 获取请求的方式
String method = request.getMethod();
boolean isGet = "GET".equals(method);
// head请求和get一樣,只是head只会取的HTTP header的信息。
if (isGet || "HEAD".equals(method)) {
// lastModified 属性可返回文档最后被修改的日期和时间。
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
// checkNotModified逻辑判断当前lastModfied值和http header的上次缓存值,如果还没有过期就设置304头并且返回并结束整个请求流程。否则继续。
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
// 实际上执行处理的方法,通过请求访问对应的处理器,并且返回modelandview对象
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
// 如果是异步请求直接返回
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
// 设置默认的视图
applyDefaultViewName(request, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {
dispatchException = ex;
}
// 处理返回结果。包括异常处理、渲染页面、发成完成通知出发Interceptor的afterCompletion
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Error err) {
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
}
finally {
// 判断是否异步
if (asyncManager.isConcurrentHandlingStarted()) {
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
}
else {
// 删除上传请求的资源
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}