Spring MVC 开发流程(重要)
轻量级Web MVC框架,基于Spring IoC容器运行,所有对象被IoC管理
- C > Controller控制器 (Servlet) (核心)(作用:处理请求,产生响应)
- M > Model 模型(业务逻辑Service)
- V > View试图(Web)
Spring 官网: https://spring.io/
Spring MVC 文档:https://docs.spring.io/spring-framework/docs/current/reference/html/web.html
Spring MVC 环境配置步骤:
- Maven依赖
<groupId>org.springframework</groupId>
<artifactId>spring-webmvc</artifactId>
<version>5.2.14.RELEASE</version>
- web.xml配置DispatcherServlet(核心)
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<servlet>
<servlet-name>springmvc</servlet-name>
<!-- DispatchServlet是spring-mvc最核心的对象,-->
<!-- 用于拦截http请求,并根据请求的URL调用与值对应的Controller方法,-->
<!-- 来完成Http请求的处理-->
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:applicationContext.xml</param-value>
</init-param>
<!-- 并初始化DispatcherServlet-->
<!-- 在Web应用启动时自动加载Spring IOC容器-->
<load-on-startup>0</load-on-startup>
</servlet>
<!--映射-->
<servlet-mapping>
<servlet-name>springmvc</servlet-name>
<!-- "/"代表拦截所有请求-->
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
- 配置applicationContext的MVC标记
<?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:p="http://www.springframework.org/schema/p"
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/context
http://www.springframework.org/schema/context/spring-context.xsd
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- context:component-scan标签作用-->
<!-- 在Spring IOC初始化过程中,自动创建并管理com.example.firstspringmvc.springmvc及子包中-->
<!-- 拥有以下注解的对象-->
<!-- @Repository-->
<!-- @Servoce-->
<!-- @Controller-->
<!-- @Component-->
<context:component-scan base-package="com.example.firstspringmvc.springmvc"></context:component-scan>
<!-- 请用Spring MVC注解开发-->
<mvc:annotation-driven/>
<!-- 将图片/JS/CSS等静态资源排除在外-->
<mvc:default-servlet-handler/>
</beans>
- 开发Controller控制器
创捷控制器类,并用@Controller进行注解
接收Web数据(重要)
HTML这边不写了,需要接收的数据名有name, course, purpose(复选内容)
使用数组来接受请求
@Controller
public class formController {
// 使用数组来接收数据
// @RequestParam(value = "n", defaultValue = "ALAN"String name 默认值为name=ALAN
@PostMapping("/apply")
@ResponseBody
public String apply(@RequestParam(value = "n", defaultValue = "ALAN") String name, String course, Integer[] purpose) {
System.out.println(name);
System.out.println(course);
for (Integer p : purpose) {
System.out.println(p);
}
return "SUCCESS";
}
使用List接收请求时,将Integer[] 改为List >>>推荐
//使用List来接收数据
@PostMapping("/apply")
@ResponseBody
public String apply(String name, String course, @RequestParam List<Integer> purpose) {
System.out.println(name);
System.out.println(course);
for (Integer p : purpose) {
System.out.println(p);
}
return "SUCCESS";
}
使用对象接收请求 >>>推荐
//使用实体类来接收数据
@PostMapping("/apply")
@ResponseBody
public String apply(Form form) {
System.out.println(form.getName());
System.out.println(form.getCourse());
for (Integer p : form.getPurpose()) {
System.out.println(p);
}
return "SUCCESS";
}
使用Map接收请求,Key值是数据的name, Value是数据的值
需要注意Map不能接收复合数据
响应输出结果
-
@ResponseBody
=> 直接产品响应体的数据,过程不涉及视图
=> 可以生成标准字符串/JSON/XML等格式数据
=> 会被StringHttpMessageConverter所影响 -
ModelAndView“模型(数据)和视图(界面)”对象 ====用于动态数据
=> 利用模板引擎渲染输出
=> 可将包含数据的对象与模板进行绑定
=> SpringMVC默认的View是JSP,也可以配置其它模板引擎
=> mav.addObject()方法设置的属性默认存放在当前请求中(使用重定向时,原本放在请求中的数据会丢失)
=> ModelAndView默认使用请求转发(forward)
请求转发:将当前的请求,直接转发给view.jsp,所以访问地址不会发生改变
=> 页面重定向使用 new ModelAndView(“redirect:/index.jsp”)
使用重定向后,由于是系统通知发送新的请求,访问地址会发生改变
应用:注册后跳转到首页,关联性不大 可以使用重定向
ModelAndView 快速上手
//ModelAndView练习模拟返回动态数据
@GetMapping("/view")
public ModelAndView showView(Integer userId) {
// 方法一
// ModelAndView mav = new ModelAndView("/view.jsp");
// 方法二
ModelAndView mav = new ModelAndView();
// 没有"/"相对路径,有"/"绝对路径
mav.setViewName("/view.jsp");
User user = new User();
if (userId == 1) {
user.setUsername("Jack");
} else if (userId == 2) {
user.setUsername("Kate");
}
mav.addObject("u", user);
return mav;
}
<%-- 创建view.jsp文件,使用EL表达式返回动态数据--%>
<body>
<h1>I'm view page</h1>
<hr>
<%-- 利用EL表达式--%>
<h2>Username:${u.username}</h2>
</body>
利用String与ModelMap代替ModelAndView对象
//String与ModelMap
//Controller方法放回String的情况
//1. 方法被@ResponseBody描述,SpringMVC直接响应String字符转本手
//2. 方法不存在@ResponseBody,则SpringMVC处理String指代的视图
@GetMapping("/gg")
public String showView(Integer userId, ModelMap modelMap) {
String view = "/view.jsp";
User user = new User();
if (userId == 1) {
user.setUsername("Jack");
} else if (userId == 2) {
user.setUsername("Kate");
}
modelMap.addAttribute("u",user);
return view;
}
URL Mapping(重要)
@PostMapping 接收Post请求
@GetMapping 接收Get请求
@RequestMapping 通用 - 忽略get/post
中文乱码问题(重要)
Web应用的中文乱码,Tomcat默认使用ISO-8859-1(西欧字符集),需要将其转换为UTF-8。Controller中的请求和响应都需要转换为UTF-8。
三个步骤:
- Get请求乱码 ⇒ Tomcat中的servlet.xml增加URIEncoding
Tomcat 8.0之后不需要增加
<Connector port="80" protocol="HTTP/1.1"
connectionTimeout="20000"
redirectPort="8443"
URIEncoding="UTF-8"/>
- Post请求乱码 ⇒ web.xml增加CharacterEncodingFilter
<filter>
<filter-name>characterFilter</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>characterFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
- Response响应乱码 ⇒ Spring applicationContext.xml配置StringHttpMessageConverter(消息转换器)
<mvc:annotation-driven conversion-service="conversionService">
<mvc:message-converters>
<bean class="org.springframework.http.converter.StringHttpMessageConverter">
<property name="supportedMediaTypes">
<list>
<!--response.setContextType("text/html:charset=utf-8")-->
<value>text/html;charset=utf-8</value>
</list>
</property>
</bean>
</mvc:message-converters>
</mvc:annotation-driven>
整合Freemarker
三个步骤:
- 添加Maven依赖
- 启用Freemarker模板引擎
- 配置Freemarker参数
<!--pox.xml添加依赖-->
<dependency>
<groupId>org.freemarker</groupId>
<artifactId>freemarker</artifactId>
<version>2.3.29</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context-support</artifactId>
<version>5.2.13.RELEASE</version>
</dependency>
<!-- 在applicationContext.xml中创建-->
<!-- 启用Freemarker-->
<bean id="ViewResolver" class="org.springframework.web.servlet.view.freemarker.FreeMarkerViewResolver">
<property name="contentType" value="text/html;charset=UTF-8"/>
<property name="suffix" value=".ftl"/>
</bean>
<!-- Freemarker配置-->
<bean id="freemarkerConfig" class="org.springframework.web.servlet.view.freemarker.FreeMarkerConfigurer">
<property name="templateLoaderPath" value="/WEB-INF/ftl"/>
<property name="freemarkerSettings">
<props>
<prop key="defaultEncoding">UTF-8</prop>
</props>
</property>
</bean>
//创建一个Controller测试一下
@Controller
@RequestMapping("/fm")
public class FreemarkerController {
@GetMapping("/test")
public ModelAndView showTest(){
ModelAndView mav = new ModelAndView("/test");
User user = new User();
user.setUsername("Jack");
mav.addObject("u", user);
return mav;
}
}
创建test.ftl返回数据
<#--利用Freemarker返回数据-->
<h1>${u.username}</h1>
拦截器Interceptor(理解)
Interceptor底层依赖于Spring AOP,开发流程:
- Maven一再servlet-api
<dependency>
<groupId>javax.servlet</groupId>
<artifactId>javax.servlet-api</artifactId>
<version>4.0.1</version>
<scope>provided</scope>
</dependency>
- 实现HandlerInterceptor接口
然后实现三个方法:
preHandle
postHandle
afterCompletion - applicationContext配置过滤地址
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<mvc:exclude-mapping path="/**.ico"/>
<mvc:exclude-mapping path="/**.jpg"/>
<mvc:exclude-mapping path="/**.gif"/>
<mvc:exclude-mapping path="/**.js"/>
<mvc:exclude-mapping path="/**.css"/>
<bean class="com.example.interceptor.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptros>
Restful(重要)
REST和Restfull
- REST 表现层状态转换,资源在网络中以某种表现形式进行状态转移
- RESTful是基于REST理念的一套开发风格,是具体的开发规则
RESTful开发规范
- 使用URL作为用户交互入口
- 明确的语义规范(GET/POST/PUT/DELETE)
- 只返回数据(JSON/XML),不包含任何展现 >> 优先使用JSON
RESTful命名要求
URI 必须要有语义,必须使用名词,扁平化不超两级,名词区分单复数
GET /articles?au=jack
简单请求:GET/POST 直接发给服务器
非简单请求:PUT/DELETE 先发预检请求
<!--非简单请求需要增加过滤器 web.xml-->
<filter>
<filter-name>formContentFilter</filter-name>
<filter-class>org.springframework.web.filter.FormContentFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>formContentFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
RESTful 控制器
//使用@RestController避免每个方法增加@ResponseBody
@RestController
@RequestMapping("/restful")
public class RestfulController {
@GetMapping("/request")
// @ResponseBody
public String doGetRequest() {
return "{\"message\":\"返回查询结果\"}";
}
// POST /artical/1
// POST /restful/request/100 路径遍历,通常指向具体的ID号
@PostMapping("/request/{rid}")
// @ResponseBody
public String doPostRequest(@PathVariable("rid") Integer requestId, Person person) {
System.out.println(person.getName() + ":" + person.getAge());
return "{\"message\":\"数据新建成功\",\"id\":" + requestId + "}";
}
@PutMapping("/request")
// @ResponseBody
public String doPutRequest(Person person) {
System.out.println(person.getName() + ":" + person.getAge());
return "{\"message\":\"数据更新成功\"}";
}
@DeleteMapping("/request")
// @ResponseBody
public String doDeleteRequest() {
return "{\"message\":\"数据删除成功\"}";
}
}
创建测试HTML,利用AJAX接收数据
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>RESTful_Test</title>
<script src="jquery-3.5.1.min.js"></script>
<script>
$(function () {
$("#btnGet").click(function () {
$.ajax({
url: "/restful/request",
type: "get",
dataType: "json",
success: function (json) {
$("#message").text(json.message);
}
})
})
})
</script>
<script>
$(function () {
$("#btnPost").click(function () {
$.ajax({
url: "/restful/request/100",
type: "post",
data:"name=jack&age=23",
dataType: "json",
success: function (json) {
$("#message").text(json.message+":"+json.id);
}
})
})
})
</script>
<script>
$(function () {
$("#btnDelete").click(function () {
$.ajax({
url: "/restful/request",
type: "delete",
dataType: "json",
success: function (json) {
$("#message").text(json.message);
}
})
})
})
</script>
<script>
$(function () {
$("#btnPut").click(function () {
$.ajax({
url: "/restful/request",
type: "put",
data:"name=jack&age=23",
dataType: "json",
success: function (json) {
$("#message").text(json.message);
}
})
})
})
</script>
</head>
<body>
<input type="button" id="btnGet" value="发送Get请求">
<input type="button" id="btnPost" value="发送Post请求">
<input type="button" id="btnPut" value="发送Put请求">
<input type="button" id="btnDelete" value="发送Delete请求">
<h1 id="message"></h1>
</body>
</html>
Spring MVC 浏览器跨域访问CORS
注解模式:
增加Access-Control,在控制器(Controller增加配置项目)
//maxAge=3600 在缓存中存放一个小时,在这边时间内可以直接访问
@CrossOrigin(origins=("https://xxxxx","https://允许多个域名"),maxAge=3600)
xml配置模式:
在applicationContext.xml
<mvc:cors>
<mvc:mapping path="/restful/**" allowed-origins="https://xxxxxx","https://允许多个域名",max-age="3600"/>
</mvc:cors>
JSON序列化(重要)
- 添加Maven依赖
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-core</artifactId>
<version>2.12.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-databind</artifactId>
<version>2.12.3</version>
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.core</groupId>
<artifactId>jackson-annotations</artifactId>
<version>2.12.3</version>
</dependency>
- 只需要返回单个对象和数组
@RestController
@RequestMapping("/restful")
public class RestfulController {
@GetMapping("/person")
public Person findByPersonId(Integer id) {
Person person = new Person();
if (id == 1) {
person.setName("jack");
person.setAge(30);
} else if (id == 2) {
person.setName("Kate");
person.setAge(30);
}
return person;
}
@GetMapping("/persons")
public List<Person> findPersons(){
List list = new ArrayList();
Person p1 = new Person();
p1.setName("jack");
p1.setAge(30);
Person p2 = new Person();
p2.setName("Kate");
p2.setAge(30);
list.add(p1);
list.add(p2);
return list;
}
- HTML利用AJAX获取JSON数据
<script>
$(function () {
$("#btnPersons").click(function () {
$.ajax({
url: "/restful/persons",
type: "get",
dataType: "json",
success: function (json) {
console.info(json)
for (var i = 0; i < json.length; i++) {
var p = json[i];
$("#divPerson").append("<h2>" + p.name + "-" + p.age + "</h2>")
}
}
})
})
})
</script>
<body>
<!--获取JSON数据-->
<input type="button" id="btnPersons" value="查询所有人员"/>
<div id="divPerson"></div>
</body>