一、MVC 思想
1. 回顾 Servlet 和 JSP
- Servlet:后端逻辑处理 视图转发控制(JSON )
- 缺陷:视图内容输出比较复杂
- 优点:适合做逻辑处理
- Jsp:Java Server Page
- 优点:适合做视图输出(Html)
- 缺点:不适合做业务逻辑
- Servlet+Jsp:Servlet 负责逻辑处理+视图转发;Jsp 负责页面数据展示(html、css、js)
- 包含一套开发思想:MVC
2. MVC思想 概念
- M-Model:数据模型(JavaBean | List | Map)
- V-View:视图(Jsp | Freemarker | …)
- C-Controller:控制器(Servlet | SpringMvc Handler)
- 以用户登录为例分析:
- V-login.jsp
- C-LoginServlet.java
- Controller(参数接收/校验/业务逻辑处理/jdbc 代)–>Model(请求域)–>V(result.jsp)
- 以用户登录为例分析:
二、SpringMVC 概念解释
1. SpringMVC 是什么
Spring MVC 是 Spring 家族中的一个 web 成员, 它是一种基于 Java 的实现了 WebMVC 设计思想的请求驱动类型的轻量级 Web 框架,即使用了 MVC 架构模式的思想,将web 层进行职责解耦,基于请求驱动指的就是使用请求-响应模型,框架的目的就是帮助我们简化开发,Spring MVC 也是要简化我们日常 Web 开发的。
Spring MVC 是服务到工作者思想的实现。前端控制器是 DispatcherServlet;应用控制器拆为处理器映射器(Handler Mapping)进行处理器管理和视图解析器(View Resolver)进行视图管理;支持本地化/国际化(Locale)解析及文件上传等;提供了非常灵活的数据验证、格式化和数据绑定机制;提供了强大的约定大于配置(惯例优先原则)的契约式编程支持。
2. SpringMVC 优点
- 让我们能非常简单的设计出干净的 Web 层
- 进行更简洁的 Web 层的开发
- 天生与 Spring 框架集成(如 IoC 容器、AOP 等)
- 提供强大的约定大于配置的契约式编程支持
- 能简单的进行 Web 层的单元测试
- 支持灵活的 URL 到页面控制器的映射
- 非常容易与其他视图技术集成,如 jsp、Velocity、FreeMarker 等等,因为模型数据不放在特定的 API 里,而是放在一个 Model 里(Map 数据结构实现,因此很容易被其他框架使用)
- 非常灵活的数据验证、格式化和数据绑定机制,能使用任何对象进行数据绑定,不必实现特定框架的 API
- 支持灵活的本地化等解析
- 更加简单的异常处理
- 对静态资源的支持
- 支持 Restful 风格
三、Spring MVC 请求流程
1. Spring MVC 请求处理流程分析
具体执行步骤说明
- 用户通过前端页面发送请求,请求被 SpringMVC 前端控制器(DispatchServlet)捕获;
- DispatchServlet 对请求的 URL 解析获取 URI,根据URI 请求控制器映射器(HandlerMapping);
- HandlerMapping 返回 HandlerExcutionChain(包括 Handler 对象以及 Handler 对象对应的拦截器)给 DispatchServlet;
- DispatchServlet 根据返回的 HandlerExcutionChain 选择合适的 控制器适配器(HandlerAdaptor)(附注:如果成功获得 HandlerAdapter 后,此时将开始执行拦截器的 preHandler(…)方法);
- HandlerAdapter 根据请求的 Handler 对象 适配并执行对应的 Handler (Controller);
- Handler 执行完毕,返回一个ModelAndView 对象(模型和视图)给HandlerAdaptor;
- HandlerAdaptor 将执行结果ModelAndView 返回给 DispatchServlet;
- DispatchServlet 接收到 ModelAndView 后请求对应的 视图解析器(ViewResolver);
- ViewResolver 解析 ModelAndView 后返回 视图 view;
- DispatchServlet 请求View 模块渲染视图;
- 最终 DispatchServlet 将渲染后的页面响应给前端。
2. Spring MVC 优势
- 清晰的角色划分:前端控制器(DispatcherServlet)、请求到处理器映射(HandlerMapping)、处理器适配器(HandlerAdapter)、视图解析器(ViewResolver)、处理器或页面控制器(Controller)、验证器( Validator)、命令对象(Command 请求参数绑定到的对象就叫命令对象)、表单对象(Form Object 提供给表单展示和提交到的对象就叫表单对象)
- 分工明确,而且扩展点相当灵活,可以很容易扩展,虽然几乎不需要
- 和 Spring 其他框架无缝集成,是其它 Web 框架所不具备的
- 可适配,通过 HandlerAdapter 可以支持任意的类作为处理器
- 可定制性,HandlerMapping、ViewResolver 等能够非常简单的定制
- 功能强大的数据验证、格式化、绑定机制
- 利用 Spring 提供的 Mock 对象能够非常简单的进行 Web 层单元测试
- 本地化、主题的解析的支持,使我们更容易进行国际化和主题的切换
- 强大的 JSP 标签库,使 JSP 编写更容易
- 还有比如 RESTful(一种软件架构风格,设计风格而不是标准,只是提供了一组设计原则和约束条件。它主要用于客户端和服务器交互类的软件,目前了解即可)风格的支持
- 简单的文件上传
- 约定大于配置的契约式编程支持
- 基于注解的零配置支持等等
四、Spring MVC 环境搭建
1. 创建 Maven webApp 工程
2. SpringMVC 环境 jar包依赖
<!-- spring web -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<!-- spring mvc -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>4.3.2.RELEASE</version>
</dependency>
<!-- web servlet -->
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>3.0.1</version>
</dependency>
3. jetty 插件配置
<!-- jetty 插件 -->
<resources>
<resource>
<directory>src/main/resources</directory>
</resource>
</resources>
<plugins>
<!-- 编译环境插件 -->
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>2.3.2</version>
<configuration>
<source>1.8</source>
<target>1.8</target>
<encoding>UTF-8</encoding>
</configuration>
</plugin>
<plugin>
<groupId>org.mortbay.jetty</groupId>
<artifactId>maven-jetty-plugin</artifactId>
<version>6.1.25</version>
<configuration>
<scanIntervalSeconds>10</scanIntervalSeconds>
<contextPath>/springmvc</contextPath>
</configuration>
</plugin>
</plugins>
4. 配置 web.xml (前端控制器配置)
<?xml version="1.0" encoding="UTF-8"?>
<web-app id="WebApp_ID" version="3.0"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_3_0.xsd">
<!-- 编码过滤 utf-8 -->
<filter>
<description>char encoding filter</description>
<filter-name>encodingFilter</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>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- servlet 请求分发器 -->
<servlet>
<servlet-name>springMvc</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:servlet-context.xml</param-value>
</init-param>
<!-- 表示启动容器时初始化该 Servlet -->
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>springMvc</servlet-name>
<!-- 这是拦截请求, /代表拦截所有请求,拦截所有.do 请求 -->
<url-pattern>*.do</url-pattern>
</servlet-mapping>
</web-app>
5. servlet-context.xml 配置
<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/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd
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.xsd">
<!-- 扫描 com.shsxt.controller 下包 -->
<context:component-scan base-package="com.shsxt.controller" />
<!-- mvc 请求映射 处理器与适配器配置 -->
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter" />
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
</mvc:message-converters>
</mvc:annotation-driven>
<!-- 配置视图解析器 默认的视图解析器 -->
<bean id="defaultViewResolver"
class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="viewClass" value="org.springframework.web.servlet.view.JstlView" />
<property name="contentType" value="text/html" />
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>
</beans>
6. 页面控制器的编写
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
/**
* 采用注解扫描形式
*/
@Controller
public class HelloController {
/**
* 请求映射地址 /hello.do
* @return
*/
@RequestMapping("/hello")
public ModelAndView hello() {
ModelAndView mv=new ModelAndView();
mv.addObject("hello", "hello spring mvc");
mv.setViewName("hello");
return mv;
}
}
7. 添加视图页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${hello }
</body>
</html>
五、URL 地址映射配置
@RequestMapping
-
声明级别:方法 和 类
- 如果类级别使用@RequestMapping,url 地址拼接:类级别url + “/” + 方法级别url
-
属性:
- value:配置url 地址
- 可以配置多个映射地址
- 地址前的 “/” 可写可不写,没有影响
- method:请求类型设置
- 没有配置method 默认配置(get post)
- 可以配置多个请求类型(put get post delete head)
- params:url 参数名称配置
- 可以配置多个
import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestMethod; import org.springframework.web.servlet.ModelAndView; @Controller @RequestMapping("/url") public class UrlController { @RequestMapping(value={"/u01_01", "u01_02"}) public ModelAndView u01() { ModelAndView mv = new ModelAndView(); mv.addObject("hello", "Hello Spring MVC"); mv.setViewName("hello"); return mv; } @RequestMapping(value="u02", method= {RequestMethod.GET, RequestMethod.POST, RequestMethod.PUT, RequestMethod.DELETE}) public ModelAndView u02() { ModelAndView mv = new ModelAndView(); mv.addObject("hello", "Hello Spring MVC"); mv.setViewName("hello"); return mv; } // url: http://localhost:8080/springmvc/url/u03.do?uName=admin&uPwd=123 @RequestMapping(value="u03", params = {"uName=admin", "uPwd=123"}) public ModelAndView u03() { ModelAndView mv = new ModelAndView(); mv.addObject("hello", "Hello Spring MVC"); mv.setViewName("hello"); return mv; } }
- value:配置url 地址
六、Spring MVC 的请求域对象
ModelAndView、ModelMap、Model、Map、HttpServletRequest
@Controller
@RequestMapping("/model")
public class ModelController {
@RequestMapping("/m01")
public ModelAndView m01() {
ModelAndView mv = new ModelAndView();
// 将数据放入request 域
mv.addObject("m01", "Spring");
mv.addObject("m02", "Spring MVC");
mv.addObject("m03", "Mybatis");
// 设置视图
mv.setViewName("m01");
return mv;
}
@RequestMapping("/m02")
public ModelAndView m02(HttpServletRequest request) {
ModelAndView mv = new ModelAndView();
request.setAttribute("m01", "Spring");
request.setAttribute("m02", "Spring MVC");
request.setAttribute("m03", "Mybatis");
mv.setViewName("m01");
return mv;
}
@RequestMapping("/m03")
public String m03(ModelMap modelMap) {
modelMap.addAttribute("m01", "Spring");
modelMap.addAttribute("m02", "Spring MVC");
modelMap.addAttribute("m03", "Mybatis");
return "m01";
}
@RequestMapping("/m04")
public String m04(Model model) {
model.addAttribute("m01", "Spring");
model.addAttribute("m02", "Spring MVC");
model.addAttribute("m03", "Mybatis");
return "m01";
}
@RequestMapping("/m05")
public String m05(Map map) {
map.put("m01", "Spring");
map.put("m02", "Spring MVC");
map.put("m03", "Mybatis");
return "m01";
}
}
七、参数绑定
请求参数到处理器功能处理方法的方法参数上的绑定,对于参数绑定非常灵活
-
基本类型 (4类8种)
-
包装类型
-
String | 日期
-
JavaBean
-
数组
-
集合(List | Set | Map)
- List 参数绑定 类似数组参数传入
- Set | Map 了解即可
import com.xyz.query.AccountQuery; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; /** * 参数绑定 * 基本类型 (4类8种) * 包装类型 * String | 日期 * JavaBean * 数组 * 集合(List Set Map) * Set Map 了解 * List 参数绑定 类似数组参数传入 */ @Controller public class ParamsController { /** * 基本类型参数绑定 * url 参数名与形参名相同 即可实现参数值绑定操作 * url 必须提供参数值 否则500错误 * @param a * @param grade */ @RequestMapping("p01") public void p01(int a,float grade){ System.out.println(a + "---" + grade); } /** * 基本类型参数绑定 * url 参数名与形参名相同 即可实现参数值绑定操作 * url 必须提供参数值 否则500错误 * 使用 @RequestParam 注解 配置defaultValue 默认值 消除500错误 配置name值 给形参取别名 * @param a * @param grade */ @RequestMapping("p02") public void p02(@RequestParam(name ="pageNum" ,defaultValue = "10") int a, @RequestParam(defaultValue = "80.5") float grade){ System.out.println(a + "---" + grade); } /** * 包装类型参数绑定 * url 参数名与形参名相同 即可实现参数值绑定操作 * 形参值可空 默认值 null * @param pageNum * @param salary */ @RequestMapping("p03") public void p03(Integer pageNum,Double salary){ System.out.println(pageNum+"---"+salary); } /** * 包装类型参数绑定 推荐使用包装类型 * url 参数名与形参名相同 即可实现参数值绑定操作 * 形参值可空 默认值 null * 使用 @RequestParam 注解 配置defaultValue 默认值 配置name值 给形参取别名 * @param pageNum * @param pageSize */ @RequestMapping("p04") public void p04(@RequestParam(name ="pn" ,defaultValue = "1") Integer pageNum, @RequestParam(name ="ps" ,defaultValue = "10")Integer pageSize){ System.out.println(pageNum + "---" + pageSize); } /** * 字符串类型参数绑定 * url 参数名与形参名相同 即可实现参数值绑定操作 * 形参值可空 默认值 null * 使用 @RequestParam 注解 配置defaultValue 默认值 配置name值 给形参取别名 * @param userName * @param userPwd */ @RequestMapping("p05") public void p05(String userName,String userPwd){ System.out.println("userName-->"+userName+"-->userPwd-->"+userPwd); } /** * JavaBean类型参数绑定 * url 参数名与JavaBean成员变量名相同 即可实现参数值绑定操作 * JavaBean成员变量值可空 默认值 null 推行使用包装类型 字符串 日期 * @param accountQuery */ @RequestMapping("p06") public void p06(AccountQuery accountQuery){ System.out.println(accountQuery); } /** * 数组类型参数绑定 * url 以相同的参数名(与形参名一致)中间以& 符合进行分割传入 * ids=10&ids=20&ids=30&userName=admin&userPwd=123456 * @param ids */ @RequestMapping("p07") public void p07(Integer[] ids,String userName,String userPwd){ for (int i = 0; i < ids.length; i++) { System.out.println(ids[i]); } } /** * 集合类型参数绑定 * 集合本身不能作为方法形参绑定 需要通过JavaBean 包装(作为JavaBean 成员变量) * List 参数传入类似数组参数传入 */ @RequestMapping("p08") public void p07(AccountQuery accountQuery){ accountQuery.getIds().forEach(id->{ System.out.println(id); }); } /** * 集合类型参数绑定 * 集合本身不能作为方法形参绑定 需要通过JavaBean 包装(作为JavaBean 成员变量) * Map 参数传入需要通过表单请求传递 */ @RequestMapping("p09") public void p09(AccountQuery accountQuery){ accountQuery.getParams().forEach((k,v)->{ System.out.println(k+"--"+v); }); } /** * 不推荐!!!! * @param request */ @RequestMapping("p10") public void p10(HttpServletRequest request){ String userId= request.getParameter("userId"); String aname= request.getParameter("aname"); String type= request.getParameter("type"); System.out.println(userId+"--"+aname+"--"+type); } }
八、请求转发与重定向
- 默认跳转方式请求转发
- 设置返回值字符串内容
2.1 添加 redirect:资源路径 --> 重定向
2.2 添加 forward:资源路径 或省略 forward: --> 转发
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.support.RedirectAttributes;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* 转发 & 重定向
* 转发:服务端 页面跳转 浏览器地址栏不变
* 重定向:客户端多次请求服务端页面 浏览器地址栏改变
*/
@Controller
public class DispatcherRedirectController {
/**
* 重定向 redirect 关键字 redirect:页面地址
*
* @return
*/
@RequestMapping("dr01")
public String dr01() {
return "redirect:test.jsp";
}
@RequestMapping("dr02")
public String dr02() {
return "redirect:test.jsp?a=10";
}
@RequestMapping("dr03")
public String dr03() {
return "redirect:test.jsp?a=10&b=尚学堂";
}
/**
* 重定向 有可能出现中文乱码
* 可以使用RedirectAttributes 作为方法形参
* 使用RedirectAttributes.addAttribute 添加重定向参数 避免中文乱码
*
* @return
*/
@RequestMapping("dr04")
public String dr04(RedirectAttributes redirectAttributes) {
redirectAttributes.addAttribute("a", 10);
redirectAttributes.addAttribute("b", "尚学堂");
return "redirect:test.jsp";
}
/**
* 重定向 跳转到后台地址
*
* @return
*/
@RequestMapping("dr05")
public String dr05() {
return "redirect:test.do?a=20";
}
/**
* 重定向 跳转到后台地址并设置域对象
*
* @return
*/
@RequestMapping("dr06")
public ModelAndView dr06() {
ModelAndView mv = new ModelAndView();
mv.setViewName("redirect:test.do");
mv.addObject("a", 20);
return mv;
}
/**
* SpringMvc 默认使用页面转发
* ModelAndView Request ModelMap Map Model 均可以设置请求域参数值
*/
@RequestMapping("dr07")
public ModelAndView dr07() {
// 设置视图名
ModelAndView mv = new ModelAndView("m01");
mv.addObject("m01", "spring");
mv.addObject("m02", "springMvc");
return mv;
}
@RequestMapping("dr08")
public String dr08(Model model) {
model.addAttribute("m01", "spring");
model.addAttribute("m02", "springMvc");
return "m01";
}
/**
* SpringMvc 使用Request转发地址需要写绝对路径,如:WEB-INF/jsp/m01.jsp
*/
@RequestMapping("dr09")
public void dr09(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
request.setAttribute("m01", "spring");
request.setAttribute("m02", "springmvc");
request.getRequestDispatcher("WEB-INF/jsp/m01.jsp").forward(request, response);
return;
}
/**
* 请求转发 跳转到后台地址并设置域对象
*/
@RequestMapping("dr10")
public ModelAndView dr10() {
ModelAndView mv = new ModelAndView("forward:test02.do");
mv.addObject("m01", "springmvc");
return mv;
}
@RequestMapping("dr11")
public String dr11(Model model) {
model.addAttribute("m01", "springmvc");
return "forward:test02.do";
}
}
九、Json 数据开发
1. 基本概念
@ResponseBody
该注解用于将 Controller 的方法返回的对象,通过适当的 HttpMessageConverter 转换为指定格式后,写入到 Response 对象的 body 数据区。
返回的数据不是 html 标签的页面,而是其他某种格式的数据时(如 json、xml 等)使用(通常用于 ajax 请求)
@RequestBody
该 注 解 用 于 读 取 Request 请 求 的 body 部 分 数 据 , 使 用 系 统 默 认 配 置 的HttpMessageConverter 进行解析,然后把相应的数据绑定到要返回的对象上 ,再把HttpMessageConverter 返回的对象数据绑定到 controller 中方法的参数上
2. 配置使用
1. 添加 json 依赖 jar 包
<!-- 添加 json 依赖 jar 包 -->
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.7.0</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.7.0</version>
</dependency>
2. 修改 servlet-context.xml
<!-- mvc 请求映射 处理器与适配器配置 -->
<mvc:annotation-driven>
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter" />
<bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" />
</mvc:message-converters>
</mvc:annotation-driven>
3. 注解使用
import com.xyz.vo.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.Arrays;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
/**
* Json
*
* @ResponseBody: 响应体 默认json 方法级别 || 方法返回值前
* @RequestBody:请求体参数 声明级别 方法形参类型前 参数类型为json
*/
@Controller
@RequestMapping("json")
public class JsonController {
// javaBean
@RequestMapping("j01")
@ResponseBody
public User j01() {
User user = new User();
user.setUserId(111);
user.setUserName("zhangsan");
return user;
}
// map
@RequestMapping("j02")
@ResponseBody
public Map<String, User> j02() {
User user = new User();
user.setUserId(111);
user.setUserName("zhangsan");
Map<String, User> map = new HashMap<>();
map.put("user", user);
return map;
}
// list
@RequestMapping("j03")
@ResponseBody
public List<Map<String, User>> j03() {
User user = new User();
user.setUserId(111);
user.setUserName("zhangsan");
Map<String, User> map = new HashMap<>();
map.put("user", user);
return Arrays.asList(map);
}
// list
@RequestMapping("j04")
public @ResponseBody List<Map<String, User>> j04() {
User user = new User();
user.setUserId(111);
user.setUserName("zhangsan");
Map<String, User> map = new HashMap<>();
map.put("user", user);
return Arrays.asList(map);
}
// javaBean
@RequestMapping("j05")
@ResponseBody
public User j05(@RequestBody User user) {
return user;
}
}