SpringMVC相关配置分析
有关SpringMVC的基础知识及使用可以参考前两篇博客内容,这里简单介绍一下相关配置的源码分析以及具体实现方式。
前端控制器的源码分析
URL进入前端控制器的过程。
前端控制器的实现类为:org.springframework.web.servlet.DispatcherServlet
。
- 在前端控制器类中会执行调用doService方法。
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
//省略无关代码
try {
doDispatch(request, response);
} finally {
//省略无关代码
}
}
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);
//getHandler是前端控制器调用处理器映射器查找Handler
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
logger.debug("Last-Modified value for [" + getRequestUri(request) + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
//调用处理器适配器执行Handler,得到执行结果ModelAndView ,
// Actually invoke the handler.
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
//视图解析,将model数据填充到request域
applyDefaultViewName(request, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
} catch (Exception ex) {
dispatchException = ex;
}
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()) {
// Instead of postHandle and afterCompletion
if (mappedHandler != null) {
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
}
} else {
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
}
前端控制器和各组件的交互过程,是通过doService
方法内部调用不同的方法和其他组件进行交互的。
处理器和映射器的配置
Spring MVC大部分使用都是全注解配置,但是全注解也是由非注解发展来的。下面以实际场景为例介绍一下其具体的实现方式。
- Spring MVC的两种注解性:注解形式和非注解形式。
- 场景需求:用户信息列表查询。
前期准备
- 用户实体(User.java)
public class User {
private int id;
private String userName;
private String sex;
private String address;
//省略get/set方法
}
- 视图JSP(UserList.jsp)
<%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8"%>
<%@ taglib uri="http://java.sun.com/jsp/jstl/core" prefix="c" %>
<%@ taglib uri="http://java.sun.com/jsp/jstl/fmt" prefix="fmt"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN" "http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<meta http-equiv="Content-Type" content="text/html;charset=UTF-8">
<title>用户列表</title>
</head>
<body>
<table border="1" align="center">
<h1 align="center"> 用户列表</h1>
<tr>
<td>用户ID</td>
<td>用户姓名</td>
<td>用户性别</td>
<td>地址</td>
</tr>
<c:forEach items="${users}" var="user">
<tr>
<th>${user.id}</th>
<th>${user.userName}</th>
<th>${user.sex}</th>
<th>${user.address}</th>
</tr>
</c:forEach>
</table>
</body>
</html>
基于XML的形式配置处理器适配器等
在spring-mvc.xml文件中配置处理器(包含处理器映射器、处理器适配器、视图解析器)。
- 配置处理器适配器
<!--配置处理器适配器:所有处理器都需要来实现HandlerAdapter的接口-->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
- 在
SimpleControllerHandlerAdapter
源码中,可以看出适配器只能实现Controller
接口的Handler
,即开发Controller
,才能由SimpleControllerHandlerAdapte
r适配器执行。
Handler实现
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.ArrayList;
/**
* Handler:基于XML配置实现
* 需要实现org.springframework.web.servlet.mvc.Controller接口
* 才能被适配器适配并执行
*/
public class ControllerXML implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
//这个是需要通过数据拿到结果,实现静态数据模拟实现
ArrayList<User> list = new ArrayList <>();
User user = new User();
user.setId(1);
user.setUserName("张三");
user.setSex("男");
user.setAddress("西安");
list.add(user);
User user1 = new User();
user1.setId(2);
user1.setUserName("小明");
user1.setSex("男");
user1.setAddress("陕西西安");
list.add(user1);
//返回ModelAndView结果
ModelAndView modelAndView = new ModelAndView();
//填充数据
modelAndView.addObject("users",list);
//指定视图
modelAndView.setViewName("userList");
return modelAndView;
}
}
处理器配置
- 将配置文件spring-mcv.xml需要加载到web.xml中。
<?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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">
<!--配置处理器映射器-->
<bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
<!--配置处理器适配器:所有处理器都需要来实现HandlerAdapter的接口-->
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
<!--配置Handler实例,将Handler交给Spring 管理-->
<bean name="/userlist" class="com.test.contrller.ControllerXML"/>
<!--配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--配置前缀-->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!--配置后缀-->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
注意:在XML配置文件中处理器适配器和处理器映射器需要使用的两个框架类。
org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter
org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping
执行结果
基于注解的形式配置处理器适配器等
处理器映射器在spring3.1之前使用
org.springframework.web.servlet.mvc.annotation.DefaultAnnotationHandlerMapping
处理器映射器在spring3.1之后使用
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping
处理器适配器在spring3.1之前使用
org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter
处理器适配器在spring3.1之后使用
org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter
配置如下:
<!--处理器适配器-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
<!--配置处理器映射器-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<!--
使用mvc:annotation-driven代替上面处理器映射器和处理器适配器的配置
annotation-driven默认加载很多的参数绑定方法
实际开发中都是使用mvc:annotation-driven
处理器映射器,适配器采用Spring MVC提供的默认的处理器处理,不需要显性的配置
-->
<mvc:annotation-driven/>
Handler的实现
/*
* 处理器
* 基于注解实现的Handler
*/
@Controller
public class ControllerDemo {
@RequestMapping("userList")
public ModelAndView userList() {
//这个是需要通过数据拿到结果,实现静态数据模拟实现
ArrayList<User> list = new ArrayList<>();
User user = new User();
user.setId(1);
user.setUserName("张三");
user.setSex("男");
user.setAddress("西安");
list.add(user);
User user1 = new User();
user1.setId(2);
user1.setUserName("小明");
user1.setSex("男");
user1.setAddress("陕西西安");
list.add(user1);
//返回ModelAndView结果
ModelAndView modelAndView = new ModelAndView();
//填充数据
modelAndView.addObject("users", list);
//指定视图
modelAndView.setViewName("userList");
return modelAndView;
}
}
处理器配置
- 将配置文件spring-mcv.xml需要加载到web.xml中。
<?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:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.1.xsd
http://www.springframework.org/schema/mvc http://www.springframework.org/schema/mvc/spring-mvc-4.1.xsd">
<!--处理器适配器-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter"/>
<!--配置处理器映射器-->
<bean class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping"/>
<!--
使用mvc:annotation-driven代替上面处理器映射器和处理器适配器的配置
annotation-driven默认加载很多的参数绑定方法
实际开发中都是使用mvc:annotation-driven
处理器映射器,适配器采用Spring MVC提供的默认的处理器处理,不需要显性的配置
-->
<mvc:annotation-driven/>
<!--配置Spring MVC的需要扫描注解的包路径-->
<context:component-scan base-package="com.test.contrller"/>
<mvc:annotation-driven/>
<!--配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<!--配置前缀-->
<property name="prefix" value="/WEB-INF/jsp/"/>
<!--配置后缀-->
<property name="suffix" value=".jsp"/>
</bean>
</beans>
执行结果