SpringMVC
1 Overview
学习方法 课程说明 秦老师课前说的:
SE 入门, 能看懂代码
Web 框架在精简它
框架 按正常, 这里是要从官网文档自学的, 不能再被老师带. 锻炼自学能力, 锻炼笔记能力, 锻炼项目能力
SSM整合, 相当于在Web阶段的项目
Spring IoC和AOP特别重要, 要再复习
SpringMVC中也有个很重要的东西, 执行流程
博客很重要, 工作时如果没有对应文档, 可能会崩溃
版本: 老师用5.1.9, 我自己选用了5.2.0
1.1 MVC
模型(DAO service) | 视图(jsp) | 控制器(Servlet)
后端很多特别牛批的控制器, 很多底层也都是Servlet
职责
Controller 1. 取得表单数据 2. 调用业务逻辑 3. 转向指定页面
Model 1. 业务逻辑 2. 保存数据状态
View 显示页面
pojo:
- vo: 视图层的实体类对象, 更灵活
- dto: 数据传输时的对象
- ...
面试: 你的项目的架构是设计好的还是演进的??
坑 一般都是演进的. 才合理. 阿里巴巴, MySQL --> AliSQL --> AliRedis
所有项目一般都是All in one --> 微服务
1.2 SpringMVC
为什么要用它呢?
- 轻量, 易学
- 高效地, 基于请求响应的框架. (不是以事件为驱动)
- 可与SpringFramework无缝集成
- 约定优于配置
- 功能强大, 支持: RESTful | 数据合法验证 | 异常处理 | 拦截器 ...
- 市占率高
RESTful, 不需要问号拼接参数
1.3 基本原理 执行流程
A - DispatcherServlet
Spring的web框架围绕 DispatcherServlet 设计
作用: 转发调度 ,将请求分发到不同"处理器"
本质: 还是Servlet, 如下图


中文版
秦老师的图
基本原理 - 简述: (based on 老师公众号)
DispatcherServlet
, 代表FrontController
, 是整个SpirngMVC的"控制中心". 用户发出request, DS接收请求. 解析URL:- 例如:
http://localhost:8080/SpringMVC/hello
http://localhost:8080
- 服务器域名端口/SpringMVC
- 部署在服务器上的具体web站点/hello
- 控制器- 综述: 请求位于服务器上"SpringMVC站点"的"hello"控制器
- 例如:
HandlerMapping
根据url中信息, 中众多注册过的Handler们中找具体的HandlerExecution
(理解不太深刻), 主要作用是根据url查找控制器helloHandlerAdapter
按特定规则执行Handler, Handler再让具体的Controller执行Controller
调用执行我们写的业务代码, 得到数据, 存入ModelAndView
对象, 并把该对象返回给Adapter
, 再给DS- DS调用
ViewResolver
解析mv对象中的"逻辑视图名", 同时将数据渲染给这个具体的View - DS通过逻辑视图名, 找到渲染好的View, 呈现
摘自老师: 没有笨人只有懒人
基本原理 - 个人理解:
HandlerMapping
是众多Handler们的映射器 注册中心, 通过它找到具体的Handler的信息, 把信息告诉给DS, DS再通过Adapter调用具体的handler, 也就是我们写的Controller
ModelAndView
是Controller方法的返回值, 理解可以认为"给前端带个需要的值, 并且指定哪个页面文件"
ViewResolver
作用
- 提取mv中model中的数据
- 解析mv中指定的view名字信息
- 拼接前缀路径+视图名字+后缀, 找到对应view
- 将数据渲染到view中
- 个人理解: VR是从MandV中提取出数据和指定view信息, 再把两者结合(渲染), 丢给DS, DS展现..ok
B - 补充: Maven资源过滤问题
Maven存在资源过滤问题, 经常出现xml文件编辑后不能正常out到target中, 可以在parent module的pom.xml中添加以下代码来解决:
<build>
<resources>
<resource>
<directory>src/main/java</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
<resource>
<directory>src/main/resources</directory>
<includes>
<include>**/*.properties</include>
<include>**/*.xml</include>
</includes>
<filtering>false</filtering>
</resource>
</resources>
</build>
1.4 RESTful
(已经比较熟悉了, 这里只补充一些)
作用:
- 简洁, 显而易见
- 高效 (支持缓存)
- 可能有一定安全性, 因为url上可以避免暴露太多后台代码信息 (秦老师看法)
注意点:
- requestMapping有相同url, 但接收请求的method不同的 (一个get 一个post), 则会根据实际请求方式不同自动匹配
- 别忘了
@PathVariable
1.5 小黄鸭调试法
向自己, 如同向他人一样, 解释一下代码的作用 流程. 说到一半可能就明白了
2 Hello SpringMVC
2.1 通过代码理解: XML
手动配置各个组件, 用Spring + SpringMVC实现一次HelloWorld!
-
确保Maven中webmvc的依赖正确导入, 确保projectStructure > artifact > 中依赖都正确
-
准备好一个SpringFramework的核心配置文件: springmvc-servlet.xml, in resources目录(classpath:)
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> </beans>
-
配置DispatcherServlet: in web.xml
<?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"> <!--配置DS--> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!--DS需要绑定Spring的核心配置文件--> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc-servlet.xml</param-value> </init-param> <!--启动级别: 1--> <load-on-startup>1</load-on-startup> </servlet> <!--斜杠和斜杠* 有区别: 斜杠只匹配请求,斜杠*匹配请求+也包括jsp文件--> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
注意:
<url-pattern>
/
- 只匹配请求, 不匹配.jsp文件/*
- 不光匹配请求, 也匹配文件
-
配置HandlerMapping, HandlerAdapter and ViewResolver, 交给Spring管理, in springmvc-servlet.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" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd"> <!--HandlerMapping--> <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"> </bean> <!--HandlerAdapter--> <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"> </bean> <!--ViewResolver --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver"> <!--前缀--> <property name="prefix" value="/WEB-INF/jsp"></property> <!--后缀--> <property name="suffix" value=".jsp"></property> </bean> </beans>
-
Controller: HelloController.java
package com.kuang.controller; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.Controller; public class HelloController implements Controller { public ModelAndView handleRequest(javax.servlet.http.HttpServletRequest httpServletRequest, javax.servlet.http.HttpServletResponse httpServletResponse) throws Exception { ModelAndView mv = new ModelAndView(); /*这里写业务代码*/ String result = "HelloSpringMVC"; mv.addObject("msg",result); /*视图跳转*/ mv.setViewName("test"); // 在视图解析器中拼接 return mv; } }
-
配置Tomcat部署/s1并启动测试http://localhost:8080/s1/hello, 通过.
2.2 通过代码理解: 注解
-
确保依赖都正常, 必须手动在projectStructure>artifact中添加lib目录, 并且把依赖也能输出进去!否则会404!
-
准备Spring的核心配置文件springmvc-servlet.xml. 这里和XML方式稍微有区别
别忘了准备页面文件 web > jsp > hello.jsp
<?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 https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd"> <!--自动扫描包,使指定的包下的注解生效--> <context:component-scan base-package="com.kuang.controller"></context:component-scan> <!--SpringMVC固定要写的 #######################开始--> <!--过滤静态资源的处理, 使S MVC不处理静态资源--> <mvc:default-servlet-handler></mvc:default-servlet-handler> <!--注解引擎--> <mvc:annotation-driven></mvc:annotation-driven> <!--ViewResolver--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver"> <property name="prefix" value="/WEB-INF/jsp/"></property> <property name="suffix" value=".jsp"></property> </bean> <!--SpringMVC固定要写的 #######################结束--> </beans>
-
web.xml中配置DServlet
<?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"> <!--配置DServlet--> <servlet> <servlet-name>springmvc</servlet-name> <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> <!--DS需要绑定Spring的核心配置文件--> <init-param> <param-name>contextConfigLocation</param-name> <param-value>classpath:springmvc-servlet.xml</param-value> </init-param> <!--启动级别: 1--> <load-on-startup>1</load-on-startup> </servlet> <!--斜杠和斜杠* 有区别: 斜杠只匹配请求,斜杠*匹配请求+也包括jsp文件--> <servlet-mapping> <servlet-name>springmvc</servlet-name> <url-pattern>/</url-pattern> </servlet-mapping> </web-app>
-
Controller.java
package com.kuang.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; @Controller // 被包扫描配置扫描到, 被Spring接管 public class HelloSpring { @RequestMapping("/hello") public String hello(Model model){ // 处理业务, 封装数据 model.addAttribute("msg","Hello, SrpingMVC-annotaion!"); return "hello"; // 会自动被视图解析器解析 } }
2.3 结果跳转
A - ModelAndView方式
-
配置视图解析器
页面 = [视图解析器前缀 + ] viewName [ + 视图解析器后缀]
-
对应Controller
- 具体的controller是实现接口的. 返回的结果需要用MV对象封装
// 伪代码 public class HelloController implements Controller{ public ModelAndView handleRequest(req, resp){ mv.addObject(...); return mv; } }
-
controller如果是注解, 则要用
Model.addAttribute()
还有个
ModelMap
|ModelAndView
, Model与之很像, 但更精简! 更常用Model
@Controller // 被包扫描配置扫描到 public class HelloController { @RequestMapping("/hello") public String hello(Model model){ // 处理业务, 封装数据 model.addAttribute("msg","Hello, SrpingMVC-annotaion!"); return "hello"; // 会自动被视图解析器解析 } }
B - HttpServletResponse方式
使用Servlet的API, 比较原始
如果不用视图解析器, resp和req可以直接声明入方法形参, 直接可以用
-
输出
resp.getWriter().print("Hello world!");
-
重定向
resp.sendRedirect("/index.jsp");
-
转发
resp.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req, resp);
C 重定向 转发
-
无视图解析器
return "redirect:/index.jsp"; // 重定向 return "forward:/index.jsp"; // 转发
-
有视图解析器
return "redirect:/index.jsp"; // 与重定向, 与没有视图解析器相同 return "index"; //转发
2.4 请求数据处理
- 如果前端name = 后端参数名name
- 直接就可以了
- 如果前端username != 后端参数名name
- 借助@RequestParam("username") String name
- 老师建议都加这个注解, 可以有效区分前端参数和普通参数
- 如果前端正好符合后端的一个对象
- 后端可以直接用对象当参数
- 前提: 参数名要一致
2.5 POST中文乱码
手写Filter
可解决, 注意过滤器的配置<url-pattern>
要配置/*
过滤器拦截路径配置 (重要, 易忘)
/
- 所有请求, 不包含.jsp页面/*
- 所有请求, 包含.jsp页面
Spring的Filter
(与上类似)
3 JSON
JSON <--> JS 互转
<script type="text/javascript">
var v1 = {
id:1001,
name:"张三",
age:18
};
var j = JSON.stringify(v1);
console.log(j)
var obj = JSON.parse(j);
console.log(obj)
</script>
3.1 Java中生成JSON: Jackson
可以借助Json解析工具, 如: Jackson | fastjson
如何使用Jackson?
-
依赖jar
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.10.0</version> </dependency>
-
Controller层
package com.kuang.controller; import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.kuang.pojo.User; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; @Controller public class JsonController { @RequestMapping(value="/json",produces = "application/json;charset=utf8") //解决乱码问题 @ResponseBody // 不经过视图解析器解析 或者用类上@RestController public String testJson() throws JsonProcessingException { User user = new User(1001, "张三", 18); ObjectMapper mapper = new ObjectMapper(); String stringJson = mapper.writeValueAsString(user); return stringJson; } }
3.2 List to JSON
// 输出结果是
[{name:"dfkjdk",id:213},{},{}]
3.3 Date to JSON
-
传统手艺
String dateSDF = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss").format(new Date()); return mapper.writeValueAsString(dateSDF); // 结果 样式 "2011-11-11 11:11:11"
-
Jackson
的API- 停用默认时间戳
- sdf格式传入
objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false); mapper.setDateFormat(sdf); objectMapper.writeValueAsString(new Date());
3.4 乱码问题
-
可以如上述3.1中一个一个解决
-
也可以统一解决
统一解决, 在Spring的核心配置文件中配置
3.5 FastJson
from Alibaba
String str = JSON.toJSONString(userList user);
User user = JSON.parseObject(str, User.class);
JSONObject jsonObject = (JSONObject) JSON.toJSON(user);
jsonObject.getString("name");
User user = JSON.toJavaObject(jsonObject, User.class);
4 AJAX
4.1 试试
jQuery.ajax(...)
部分参数
-
url: 请求地址
-
type: 请求方式 GET POST 1.9.0之后用method
-
headers: 请求头
-
data: 要发送的数据
-
contenType: 即将发送给服务器的内容的编码类型(默认: application/x-www-form-urlencode)
-
async: 是否异步
-
timeout: 设置请求超时时间
-
beforeSend: 发送请求前执行的函数 全局
-
complete: 完成之后的回调函数
-
success: 成功之后执行的回调函数
-
error: 失败之后执行的回调函数
-
accepts: 通过请求头发送给服务器, 告诉服务器当前客户端可接受的数据类型
-
dataType: 将服务器返回的数据转换成指定类型
- "xml": 将服务器返回的内容换成xml格式
- "text": 普通文本
- "html"
- "script"
- "json"
- "jsonp"
代码示例
test.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
<script src="https://ajax.aspnetcdn.com/ajax/jQuery/jquery-3.4.1.js"></script>
<script type="text/javascript">
function a(){
$.post({
url:"${pageContext.request.contextPath}/a1",
data:{"name":$("#username").val()},
success:function(data){
alert(data);
}
});
};
</script>
</head>
<body>
<%--失去焦点时发起一个请求--%>
用户名:<input type="text" id="username" onblur="a()">
</body>
</html>
package com.kuang.controller;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.kuang.pojo.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
@Controller
public class JsonController {
@RequestMapping(value = "/a1", produces="application/json;charset=utf8")
@ResponseBody
public void a1(String name, HttpServletResponse resp) throws IOException { // 故意写成name
System.out.println("a1:param-->" + name);
if ("kuangshen".equals(name)){
resp.getWriter().print("yes");
} else {
resp.getWriter().print("no");
}
}
@RequestMapping(value = "/index2")
public String index(){
return "test2";
}
}
4.2 结合JSON
X BUG
Artifact中一定要确保所有依赖都手动添加过! 别忘了中途添加的依赖!