一、MVC软件框架与SpringMVC介绍
1.1 MVC模式
MVC模式是一种开发方式,它的主要用途是对组件之间进行隔离分层。
1.1.1 MVC三层架构
- M(odel)模型,包含被传递的数据。在软件项目中,M常常被定义为业务模型,也就是业务/服务层。
- V(ison)视图层,即用什么组件显示数据,常用的有HTML与JSP这些文件。
- C(ontroller)控制层,表示软件大方向的执行流程以及哪个视图对象将数据展示给用户。
1.1.2 MVC的优点
MVC开发方式将不用功能的组件进行隔离,进行分层,有利于代码的后期维护。
1.2 软件框架
软件框架就是软件功能的半成品。框架提供了针对某一个领域所写代码的基本模型,对大多数通用性的功能进行封装。
Spring5MVC框架是现在主流的JavaWeb服务端MVC分层框架,它在功能及代码执行效率上进行了优化和增强。
二、搭建MavenSpring项目
2.1 项目基本环境的搭建与运行
2.1.1 项目基本环境搭建步骤
- 创建maven-webapp项目。
- 在pom.xml中 新增依赖(如下)。
与name、url等同级:
<parent>
<artifactId>spring-boot-starter-parent</artifactId>
<groupId>org.springframework.boot</groupId>
<version>2.5.3</version>
</parent>
dependencies中新增节点:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
<version>2.3.4.RELEASE</version>
</dependency>
-
右键main,new->directory,选择java与resources,新建两个目录。
-
创建controller层的TestController与Spring启动类Application,项目结构如图所示。
-
TestController代码:
package com.wqq.www.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
public class TestController {
@RequestMapping("test1")
@ResponseBody
public String test1(){
System.out.println("public String test1()");
return "test1返回值";
}
}
注意:
- @Controller注解注明该类作用于Controller层。
- @RequestMapping(“test1”),注明匹配方式,即通过根路径+test1进行访问此controller。
- ResponseBody注明返回体。(此案例可以不使用它)
- Application代码:
package com.wqq.www;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class, args);
}
}
2.1.2 项目运行
1、通过idea编译器Application启动类运行
点击如下所示的按钮,等待加载完成便可运行。
输入网址http:/localhost:8080/test1即可出现如下网页,运行成功。
2.1.3 打包运行
1、介绍
将SpringBoot Web项目打包成可自动运行的war/jar,直接运行war/jar就可以启动Web项目。war/jar以及内嵌了tomcat,实际项目发布时,只需要这种war/jar包便可以完成项目的发布。
2、新增plugins节点
将以下代码放到build标签中(注:不是pluginManagement里面,而是与它同级。)
<plugins>
<plugin>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-maven-plugin</artifactId>
</plugin>
</plugins>
3、运行cmd命令
- 打开cmd并进入项目根目录。
- 输入命令
mvn package
- 控制台显示BUILD SUCCESS字样,然后刷新项目目录,便可见到war包文件。
- 若想生成jar包文件只需将pom.xml中的配置更改即可(与groupId、artifactId等节点同级)。其他打包步骤均相同。
<packaging>jar</packaging>
- 输入以下命令启动服务器
java -jar war包的名字.war
(没写错)或java -jar jar包的名字.jar
2.2 创建css-JavaScript-HTML-JSP资源
2.2.1 项目结构
2.2.2 代码
mycss.css:
.myStyle{
color: red;
font-size: large;
}
myjs.js:
setTimeout(function (){
alert("auto run!");
},3000)
test1.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="js/myjs.js"></script>
<link rel="stylesheet" type="text/css" href="css/mycss.css">
</head>
<body>
<p>这是html</p><br>
<h1 class="myStyle">引入css样式</h1>
</body>
</html>
test2.jsp
<%@ page contentType="text/html; UTF-8" language="java" pageEncoding="UTF-8" %>
<%@ taglib prefix="c" uri="http://java.sun.com/jsp/jstl/core" %>
<html>
<head>
<link rel="stylesheet" type="text/css" href="css/mycss.css">
</head>
<body>
<h2>Hello jsp文件</h2>
<br/>
<h1 class="myStyle">引入样式表</h1>
<c:forEach var="item" items="${mylist}">
${item}<br/>
</c:forEach>
</body>
</html>
TestController(jsp页面通过转发获得myList):
package com.wqq.www.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.ArrayList;
@Controller
public class TestController {
@RequestMapping("/")
public String test1(){
System.out.println("public String test1()");
return "test1.html";
}
@RequestMapping("test1")
public String test2(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("public String test2()");
ArrayList<String> list = new ArrayList<>();
list.add("你好 1");
list.add("你好 2");
list.add("你好 3");
request.setAttribute("mylist", list);
request.getRequestDispatcher("test2.jsp").forward(request,response);
return "test2.jsp";
}
}
在地址栏输入http://localhost:8080/test1
即可调用controller进入jsp。
在地址栏输入http://localhost:8080/
即可调用controller进入test1.html。
三、参数传递
3.1 控制层参数传递
3.1.1 控制层无传参
@RequestMapping(“x1”)
public String x1(){
System.out.println(“public String x1()”);
return “x1.html”;
}
3.1.2 控制层有传参
@RequestMapping("x2")
public String x2(@RequestParam("username") String username, HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("public String x2()" + username);
ArrayList<String> list = new ArrayList<>();
list.add("你好 1");
list.add("你好 2");
list.add("你好 3");
request.setAttribute("mylist", list);
request.getRequestDispatcher("test2.jsp").forward(request,response);
return "x2.jsp";
}
3.1.3 控制层有传参简化版
@RequestMapping("x3")
public String x3(String username, String password,HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
System.out.println("username = " + username + "password = " + password);
return "x3.jsp";
}
3.1.4 将url参数封装到实体类
创建实体类dto.Userinfo
package com.wqq.www.dto;
public class Userinfo {
private String username;
private String password;
public Userinfo(String username, String password) {
this.username = username;
this.password = password;
}
public Userinfo() {
}
public String getUsername() {
return username;
}
public void setUsername(String username) {
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
}
controller层获得实体类参数
@RequestMapping("login2")
public String login2(Userinfo userinfo){
if (userinfo.getUsername().equals("a")&&userinfo.getPassword().equals("aa")){
return "ok.jsp";
}
else {
return "no.jsp";
}
}
3.1.5 限制提交method的方式
注解上加入method = RequestMethod.XXX
@RequestMapping(value = "login3",method = RequestMethod.POST)
3.2 参数类型
3.2.1 控制层方法的参数类型
控制层方法参数类型 | 解释 |
---|---|
WebRequest/NativeWebRequest | 可以访问request的parameters,request 和 session的 attributes,而不需要使用Servlet API。 |
javax.servlet.ServletRequest / javax.servlet.ServletResponse / MultipartRequest / MultipartHttpServletRequest | 使用指定的request或response对象。 |
javax.servlet.http.HttpSession | 使用指定的HttpSession对象。 注意:访问HttpSession不是线程安全的,如果有多个请求同时访问HttpSession对象,则需要将RequestMappingHandlerAdapter类的synchronizeOnSession属性设置为true. |
HttpMethod | request请求的method方式。 |
java.util.Locale | 当前请求的区域。 |
java.util.TimeZone +java.time .ZoneId | 当前请求关联的Zone. |
java.io.Inputstream java.io.Reader | 访问request body最原始的数据。 |
java.io.outputStream java.io.writer | 访问response body最原始的数据。 |
@PathVariable | 访问URI模板变量。 |
@MatrixVariable | 访问以name-value形式存在于URI路径中的片段。 |
@RequestParam | 访问request中的parameters,该注解是可选的。 |
@RequestHeader | 访问request headers中的数据。 |
@CookieValue | 访问Cookie。 |
@RequestBody | 访问request body。 |
@HttpEntity< B> | 访问request中的headers和body。 |
@RequestPart | 处理"multipart/form-data"请求中的part。 |
java.util.Map org.springframework.ui.Model org.springframework.ui.ModelMap | 用于View层的交互。 |
RedirectAttributes | 在重定向时添加attributes处理,有2种用法:(1)可以将数据放在query string中。 (2)结合flash attributes将数据存储到临时的空间,当重定向结束后删除临时空间中的数据。 |
@ModelAttribute | 访问已存在的attribute |
Errors / BindingResult | 访问的Errors来自于数据验证和绑定,或者Errors来自于对@RequestBody或@RequestPart的验证。一个Errors或BindingResult参数要声明在验证方法参数之后。 |
类级别的@SessionAttributes | 定义HttpSession中的 attributes,在处理完成后触发清理。 |
@SessionAttribute | 访问session中的attribute。与作为类级的@SessionAttribute结果而存储在绘画中的Model属性形成对比 |
@RequestAttribute | 访问request中的attributes |
3.2.2 控制层方法的返回值类型
控制层方法的返回值类型 | 解释 |
---|---|
@ResponseBody | 通过HttpMessageConverters转换返回值,并写入到response中。 |
HttpEntity< B> / ResponseEntity< B> | Response包括完整的headers和body,并且通过HttpMessageConverters转换返回值,并且谢图到response中 |
HttpHeaders | 返回response headers,但不包括body |
String | 使用ViewResolver解析视图的名称。 |
View | 返回View实例 |
java.util.Map / org.springframework.ui.Mode | 要添加到隐式Model中的属性,并通过RequestToViewNameT确定视图名称。 |
@ModelAttribute | 要添加到隐式Model中的属性,并通过RequestToViewNameT确定视图名称。 |
ModelAndView | 用于确定View和attributes |
void | 如果具有void返回值或返回null值的方法还具有ServletResponse,OutputStream参数或@Response Status注解,则视为已完全处理响应。 |
3.3 取得request-response-session对象
介绍
通过Controller层的参数HttpServletRequest , HttpServletResponse HttpSession来获取request、response、session对象。
代码
控制层代码:
@RequestMapping("y1")
public String y1(HttpServletRequest request, HttpServletResponse response, HttpSession session){
System.out.println(request);
System.out.println(response);
System.out.println(session);
System.out.println(request.getServletContext().getRealPath("/"));
request.setAttribute("my", "requestValue");
session.setAttribute("my", "sessionValue");
return "y1.jsp";
}
view层代码(通过requestScope、sessionScope获取不同范围的值):
request:${my}
request:${requestScope.my}
session:${sessionScope.my}
3.4 实现登录失败后的提示信息
介绍
控制层在处理前进行验证,如果所传数据不符合规范,则传到map类型的容器中,前端通过el表达式显示失败信息。
代码
jsp页面代码:
<form action="login4" method="post">
username:<input type="text" name="username"><br/>
${message.usernameisnull}<br/>
password:<input type="text" name="password"><br/>
${message.passwordisnull}<br/>
<input type="submit" value="登录">
</form>
controller层代码:
private Map validateForm4(Userinfo userinfo ){
Map map = new HashMap();
if (userinfo.getUsername() == null||"".equals(userinfo.getUsername()))
map.put("usernameisnull", "账号为空!");
if (userinfo.getPassword() == null||"".equals(userinfo.getPassword()))
map.put("passwordisnull", "密码为空!");
return map;
}
@RequestMapping("login4")
public String login4(Userinfo userinfo, HttpServletRequest request){
Map map = validateForm4(userinfo);
if (map.size() > 0) {
request.setAttribute("message", map);
return "login4.jsp";
}
else {
if (userinfo.getUsername().equals("a")&&userinfo.getPassword().equals("aa"))
return "ok.jsp";
else
return "no.jsp";
}
}
3.5 控制层的重定向
3.5.1 重定向-无参数传递
(1)通过返回"redirect:/xx"来重定向
@RequestMapping("z2")
public String z2(){
System.out.println("z2");
return "redirect:/z3";
}
(2)通过返回"xx.jsp"来重定向
@RequestMapping("z3")
public String z3(){
System.out.println("z3");
return "no.jsp";
}
3.5.2 重定向-有参数传递
(1)通过返回字符串传递参数
//发送
@RequestMapping("z4")
public String z4() throws UnsupportedEncodingException {
System.out.println("z4");
String username = "中国";
username = URLEncoder.encode(username, "utf-8");
System.out.println(username);
return "redirect:/z5?username="+username;
}
//接收
@RequestMapping("z5")
public String z5(String username) throws UnsupportedEncodingException {
System.out.println("z5");
username = URLDecoder.decode(username, "utf-8");
System.out.println("z5 username = " + username);
return "no.jsp";
}
(2)通过RedirectAttributes.addAttrubute方法传递参数
@RequestMapping("z6")
public String z6(RedirectAttributes attr) throws UnsupportedEncodingException {
System.out.println("z6");
String username = "中国";
String password = "123";
username = URLEncoder.encode(username, "utf-8");
attr.addAttribute("username",username);
attr.addAttribute("password",password);
return "redirect:/z7";
}
@RequestMapping("z7")
public String z7(String username,String password) throws UnsupportedEncodingException {
username = URLDecoder.decode(username, "utf-8");
System.out.println("z7 username = " + username + "password = "+password);
return "no.jsp";
}
(3)通过RedirectAttributes.addFlashAttrubute方法传递参数
注:该种方式与上面方式的区别,此种方式将参数放入HttpSession中,而上种方式存放于url中。这种方式更安全,更透明。且在重定向结束后,HttpSession自动清除数据,进一步增强安全性。
@RequestMapping("z8")
public String z8(RedirectAttributes attr) throws UnsupportedEncodingException {
System.out.println("z8");
String username = "中国";
String password = "123";
username = URLEncoder.encode(username, "utf-8");
attr.addFlashAttribute("username",username);
attr.addFlashAttribute("password",password);
return "redirect:/z9";
}
//注意@ModelAttribute("username")需要加
@RequestMapping("z9")
public String z9(@ModelAttribute("username") String username,@ModelAttribute("password") String password) throws UnsupportedEncodingException {
username = URLDecoder.decode(username, "utf-8");
System.out.println("z9 username = " + username + "password = "+password);
return "no.jsp";
}
3.5.3 在服务端将json字符串转化为多种数据类型(使用AJAX) P35
(1) 概述
前端传送的json字符串可以通过Spring5MVC默认使用的jackson库来将json转化为各种数据类型。
使用@RequestBody注解后,前台只需要向Controller提交一段符合JSON格式的字符串便可完成自动解析。
(2)代码
实体类Userinfo含有username、password两个属性。
控制层代码(包含转化的各种类型示例):
//转化成实体类
@RequestMapping("ajaxTest1")
@ResponseBody
public String ajaxTest1(@RequestBody Userinfo userinfo){
//RequestBody必须加
System.out.println(userinfo.getUsername());
System.out.println(userinfo.getPassword());
return "1";
}
//转化成字符串列表
@RequestMapping("ajaxTest2")
@ResponseBody
public String ajaxTest2(@RequestBody ArrayList<String> list){
//RequestBody必须加
list.forEach(item->{
System.out.println(item);
});
return "1";
}
//转化成实体类列表
@RequestMapping("ajaxTest3")
@ResponseBody
public String ajaxTest3(@RequestBody ArrayList<Userinfo> list){
//RequestBody必须加
list.forEach(item->{
System.out.println(item.getUsername() + item.getPassword());
});
return "1";
}
//转化为Map
@RequestMapping("ajaxTest4")
@ResponseBody
public String ajaxTest4(@RequestBody Map list){
//RequestBody必须加
System.out.println(list.get("username"));
List<HashMap<String,String>> workList = (List<HashMap<String, String>>) list.get("work");
workList.forEach(item->{
System.out.println(item.get("address"));
});
HashMap<String,String> school = (HashMap<String, String>) list.get("school");
System.out.println(school.get("name"));
System.out.println(school.get("address"));
return "1";
}
//复杂object对象的转换
@RequestMapping("ajaxTest5")
@ResponseBody
public String ajaxTest5(@RequestBody HashMap<String,Object> map){
//RequestBody必须加
List list1 = (List) map.get("myArray");
Map map1 = (Map) list1.get(0);
System.out.println(map1.get("username1"));
Map map2 = (Map) list1.get(1);
System.out.println(map2.get("username2"));
List list2 = (List) list1.get(2);
System.out.println(list2.get(0));
System.out.println(list2.get(1));
System.out.println(list2.get(2));
List list3 = (List) list2.get(3);
System.out.println(list3.get(0));
System.out.println(list3.get(1));
Map map3 = (Map) map.get("myObject");
System.out.println(map3.get("username"));
Map map4 = (Map) map.get("myObject1");
List<Map> list4 = (List) map4.get("address");
System.out.println(list4.get(0).get("name"));
System.out.println(list4.get(1).get("name"));
return "1";
}
前端数据传输方法(与controller层对应):
function Userinfo(username,password){
this.username=username;
this.password=password;
}
$(document).ready(function (){
{
let userinfo = new Userinfo("中国","中国人");
let jsonString = JSON.stringify(userinfo);
$("#button1").click(function (){
$.ajax({
"url":"ajaxTest1?t="+new Date().getTime(),
"type":"post",
"data":jsonString,
"contentType":"application/json"
})
})
}
{
let stringArray_ = new Array();
stringArray_[0] = "中国1";
stringArray_[1] = "中国2";
stringArray_[2] = "中国3";
let jsonString_ = JSON.stringify(stringArray_);
$("#button2").click(function (){
$.ajax({
"url":"ajaxTest2?t="+new Date().getTime(),
"type":"post",
"data":jsonString_,
"contentType":"application/json"
})
})
}
{
let stringArray = new Array();
stringArray[0] = new Userinfo("中国1","中国人1");
stringArray[1] = new Userinfo("中国2","中国人2");
stringArray[2] = new Userinfo("中国3","中国人3");
let jsonString = JSON.stringify(stringArray);
$("#button3").click(function (){
$.ajax({
"url":"ajaxTest3?t="+new Date().getTime(),
"type":"post",
"data":jsonString,
"contentType":"application/json"
})
})
}
{
let jsonObject = {
"username": "accp",
"work": [{
"address": "address1"
}, {
"address": "address2"
}],
"school": {
"name": "tc",
"address": "pjy"
}
}
let jsonString = JSON.stringify(jsonObject);
$("#button4").click(function (){
$.ajax({
"url":"ajaxTest4?t="+new Date().getTime(),
"type":"post",
"data":jsonString,
"contentType":"application/json"
})
})
}
{
let userinfo = {
"myArray": [{
"username1": "usernameValue11"
}, {
"username2": "usernameValue22"
}, ["abc", 123, true, [123, 456]]],
"myObject": {
"username": "大中国"
},
"myObject1": {
"address": [{
"name": "name1"
}, {
"name": "name2"
}]
},
};
let jsonString = JSON.stringify(userinfo);
$("#button5").click(function (){
$.ajax({
"url":"ajaxTest5?t="+new Date().getTime(),
"type":"post",
"data":jsonString,
"contentType":"application/json"
})
})
}
P39
@RequestMapping("ajaxTest6")
@ResponseBody
public Userinfo ajaxTest6(@RequestBody Userinfo userinfo){
//RequestBody必须加
System.out.println(userinfo.getUsername());
System.out.println(userinfo.getPassword());
Userinfo returnUser = new Userinfo();
returnUser.setUsername("法国");
returnUser.setPassword("法国人");
return returnUser;
}
@RequestMapping("ajaxTest7")
@ResponseBody
public String ajaxTest7(@RequestBody Userinfo userinfo) throws JsonProcessingException {
//RequestBody必须加
System.out.println(userinfo.getUsername());
System.out.println(userinfo.getPassword());
UserinfoExt returnUser = new UserinfoExt();
returnUser.setUsername("法国");
returnUser.setPassword("法国人");
returnUser.getMyList().add("德国");
returnUser.getMyList().add("土耳其");
String jsonString = new ObjectMapper().writeValueAsString(returnUser);
return jsonString;
}
@RequestMapping("ajaxTest8")
@ResponseBody
public void ajaxTest8(@RequestBody Userinfo userinfo, HttpServletResponse response) throws IOException {
//RequestBody必须加
System.out.println(userinfo.getUsername());
System.out.println(userinfo.getPassword());
UserinfoExt returnUser = new UserinfoExt();
returnUser.setUsername("法国");
returnUser.setPassword("法国人");
returnUser.getMyList().add("德国");
returnUser.getMyList().add("土耳其");
String jsonString = new ObjectMapper().writeValueAsString(returnUser);
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = response.getWriter();
writer.print(jsonString);
writer.flush();
writer.close();
}
}