Spring Web MVC起源
Spring Web MVC 是基于 Servlet API 构建的原始 Web 框架,从⼀开始就包含在 Spring 框架中。它的正式名称“Spring Web MVC”来⾃其源模块的名称(Spring-webmvc),但它通常被称为“SpringMVC”.
1.1 什么是 MVC?
Model View Controller(模型,视图,控制器),他是一种设计模式,这种设计模式将软件(项目)分为三部分: 模型,视图,控制器
MVC思想
MVC 是一种设计思想,而 SpringMVC 是一个 具体的实现框架.
SpringMVC 是一个 基于 MVC 设计模式 和 Servlet API 实现的 Web 框架,同时 SpringMVC 又是 Spring 框架中的一个 WEB 模块,他是随着 Spring 的诞生而存在的一个框架.
Spring 和 SpringMVC 诞生历史比较久远,在他们之后才有了 Spring boot.
SpringMVC流程
为什么需要这么多类注解?
- 让代码的可读性更高,能够让程序员能够直观的判断当前类的用途
@Controller:表示的是业务逻辑层;
@Servie:服务层;
@Repository:持久层;
@Configuration:配置层;
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<modelVersion>4.0.0</modelVersion>
<groupId>org.example</groupId>
<artifactId>springboot_demo</artifactId>
<version>1.0-SNAPSHOT</version>
<parent>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-parent</artifactId>
<version>2.1.5.RELEASE</version>
</parent>
<properties>
<java.version>1.8</java.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.8</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</dependency>
</dependencies>
</project>
UserController.java
package com.itterence.controller;
import com.itterence.model.UserInfo;
import lombok.extern.java.Log;
import lombok.extern.jbosslog.JBossLog;
import lombok.extern.slf4j.Slf4j;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestPart;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.multipart.MultipartFile;
import java.io.File;
import java.io.IOException;
import java.util.UUID;
import java.util.logging.Logger;
@Slf4j
@Controller
@ResponseBody // 表示的是返回一个非静态页面的数据
@RequestMapping("/user") //类上面的 RequestMapping 可以省略
public class UserController {
@RequestMapping("/sayhi")
public String sayHi(){
return "Hello World!";
}
@RequestMapping("/login")
public String login(String username,String password){
return "用户名 : "+username +"密码 : " +password;
}
@RequestMapping("/getuserbyid")
public UserInfo getUserById(Integer id){
UserInfo userInfo = new UserInfo();
userInfo.setUsername("张三");
userInfo.setAge(18);
return userInfo;
}
@RequestMapping("/getinfo/{id}/{name}")
public String getInfo(@PathVariable Integer id,@PathVariable String name){
return "ID: "+id+"| Name: "+name;
}
@RequestMapping("/upload")
public boolean upload(Integer uid, @RequestPart("img")MultipartFile file){
boolean result = false;
try{
file.transferTo(new File("C:\\Users\\U100926\\Desktop\\spt\\img.jpg"));
result = true;
}catch (IOException e){
log.error("上传失败");
}
return result;
}
// 从配置文件中读取图片的保存路径
@Value("${img.path}")
private String imgPath;
@RequestMapping("/upimg")
public boolean upImg(Integer uid, @RequestPart("img") MultipartFile file) {
boolean result = false;
String fileName = file.getOriginalFilename(); // 得到原图片的名称(xxx.png)
fileName = fileName.substring(fileName.lastIndexOf(".")); // 得到图片后缀(png)
fileName = UUID.randomUUID().toString() + fileName;
// 保存图片到本地目录
try {
file.transferTo(new File(imgPath + fileName));
result = true;
} catch (IOException e) {
log.error("上传图片失败:" + e.getMessage());
}
return result;
}
}
TestController.java
package com.itterence.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
@Controller
//@ResponseBody
public class TestController {
@RequestMapping("/hi")
public String hii(){
return "index.html";
}
}
CalcController.java
package com.itterence.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class CalcController {
@RequestMapping("/calc")
public String calc(Integer num1, Integer num2) {
if (num1 == null || num2 == null) {
return "<h1>参数错误</h1><a href = 'javascript:history.go(-1);'>返回</a>";
}
return "<h1>结果: " + (num1 + num2) + "</h1><a href = 'javascript:history.go(-1);'>返回</a>";
}
}
HelloController.java
package com.itterence.controller;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class HelloController {
@GetMapping("hello")
public String helloo(){
return "hello,springboot!";
}
}
UserInfo.java
package com.itterence.model;
import lombok.Data;
@Data
public class UserInfo {
private int id;
private String username;
private String password;
private int age;
}
Application.java
package com.itterence;
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);
}
}
logback-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<configuration>
<!--彩色日志输出格式-->
<property name="CONSOLE_LOG_PATTERN" value="%clr(%d{yyyy-MM-dd HH:mm:ss.SSS}){faint} %clr(%level){blue} %clr(${PID}){magenta} %clr([%thread]){orange} %clr(%logger){cyan} %m%n${LOG_EXCEPTION_CONVERSION_WORD:-%wEx}"/>
<!--非彩色日志输出格式-->
<property name="PATTERN" value="%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n" />
<!--dev文件路径:src同级目录logs,如果上级目录不存在会自动创建-->
<property name="DEV_FILE_PATH" value="./logs" />
<!-- pro文件路径 -->
<property name="PRO_FILE_PATH" value="/opt/prod/logs" />
<!-- 控制台输出 -->
<appender name="consoleAppender" class="ch.qos.logback.core.ConsoleAppender">
<encoder>
<pattern>${CONSOLE_LOG_PATTERN}</pattern>
</encoder>
</appender>
<!-- 按照每天生成输出日志文件 -->
<appender name="fileAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<encoder>
<!--格式化输出:%d表示日期,%thread表示线程,%-5level:级别从左显示五个字符宽度,%logger{36}:logger是class的全名,后面的数字代表限制最长的字符,%msg:日志消息,%n换行符-->
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
<!--滚动策略按照时间滚动-->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!-- rollover daily 文件名称 -->
<fileNamePattern>${DEV_FILE_PATH}/output-%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- each file should be at most 10MB, keep 60 days worth of history, but at most 2GB -->
<!--单个文件大小-->
<maxFileSize>10MB</maxFileSize>
<!--日志文件保留天数-->
<maxHistory>60</maxHistory>
<!--用来指定日志文件的上限大小,到了这个值就会删除旧日志-->a
<totalSizeCap>2GB</totalSizeCap>
</rollingPolicy>
</appender>
<!-- 按照每天生成错误日志文件 -->
<appender name="errorAppender" class="ch.qos.logback.core.rolling.RollingFileAppender">
<!-- 此日志文件只记录ERROR级别的 -->
<filter class="ch.qos.logback.classic.filter.ThresholdFilter">
<level>ERROR</level>
</filter>
<encoder>
<pattern>%d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n</pattern>
</encoder>
<!--输出日志到src同级目录logs中的error.log文件中-->
<rollingPolicy class="ch.qos.logback.core.rolling.SizeAndTimeBasedRollingPolicy">
<!--基于大小和时间的轮转策略,当日志内容超出文件大小限制后,会自动生成一个文件来继续记录和重命名-->
<fileNamePattern>${DEV_FILE_PATH}/error-%d{yyyy-MM-dd}.log</fileNamePattern>
<!-- each file should be at most 10MB, keep 60 days worth of history, but at most 2GB -->
<maxFileSize>10MB</maxFileSize>
<maxHistory>60</maxHistory>
<totalSizeCap>2GB</totalSizeCap>
</rollingPolicy>
</appender>
<!--开发环境:打印控制台-->
<springProfile name="dev">
<root level="INFO">
<appender-ref ref="consoleAppender" />
<appender-ref ref="fileAppender" />
<appender-ref ref="errorAppender" />
</root>
</springProfile>
<!--生产环境:输出到文件-->
<springProfile name="pro">
<!-- root日志以INFO级别输出,指定日志信息输出到哪个地方-->
<root level="INFO">
<appender-ref ref="consoleAppender" />
<appender-ref ref="fileAppender" />
<appender-ref ref="errorAppender" />
</root>
</springProfile>
</configuration>
calc.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<form action="/calc">
<h1>计算器</h1>
数字1 : <input name = "num1" type="text"><br>
数字2 : <input name = "num2" type="text"><br>
<input type="submit" value="点击相加">
</form>
</body>
</html>
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1 style="color: indianred">
hello springMvc
</h1>
</body>
</html>
http://localhost:8080/calc.html
http://localhost:8080/user/getuserbyid
LoginController.java
package com.itterence.controller;
import com.itterence.model.UserInfo;
import org.springframework.stereotype.Controller;
import org.springframework.util.StringUtils;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
@RestController
/*
或者这么写
@Controller
@ResponseBody // 表示的是返回一个非静态页面的数据
*/
public class LoginController {
/**
* 获取前端的 json 数据
*
* @param userInfo
* @return
*/
@RequestMapping("/login")
public HashMap<String, Object> login(@RequestBody UserInfo userInfo) {
HashMap<String, Object> result = new HashMap<String, Object>();
int state = 200;
int data = -1; // 等于 1,登录成功,否则登录失败
String msg = "未知错误";
if (StringUtils.hasLength(userInfo.getUsername()) && StringUtils.hasLength(userInfo.getPassword())) {
if (userInfo.getUsername().equals("admin") && userInfo.getPassword().equals("admin")) {
data = 1;
msg = "";
} else {
msg = "用户名或密码失败!";
}
} else { // 参数为空
msg = "非法参数";
}
result.put("state", state);
result.put("data", data);
result.put("msg", msg);
return result;
}
}
<!doctype html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport"
content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<script src="js/jquery-1.9.1.min.js"></script>
<title>Document</title>
<script>
// ajax 提交
function mysub(){
// 1.判空
var username = jQuery("#username");
var password = jQuery("#password");
if(jQuery.trim(username.val())==""){
alert("请先输入用户名!");
username.focus(); // 光标重制到此元素
return;
}
if(jQuery.trim(password.val())==""){
alert("请先输入密码!");
password.focus(); // 光标重制到此元素
return;
}
jQuery.ajax({
url:"/login",
type:"POST",
contentType:"application/json",
data:JSON.stringify({"username":username.val(),
"password":password.val()}),
success:function(result){
alert(JSON.stringify(result));
}
});
}
</script>
</head>
<body>
<div style="text-align: center;">
<h1>登录</h1>
用户:<input id="username">
<br>
密码:<input id="password" type="password">
<br>
<input type="button" value=" 提交 " onclick="mysub()" style="margin-top: 20px;margin-left: 50px;">
</div>
</body>
</html>
http://localhost:8080/login.html
ForwardController.java
package com.itterence.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@Controller
//RestController不能用这个
public class ForwardController {
@RequestMapping("/fw")
public String myForward(){
return "forward:/hello.html";
}
@RequestMapping("/fw1")
public String myForward1(){
return "hello.html";
}
@RequestMapping("/rd")
public String myRedirect(){
return "redirect:/hi.html";
}
}
hello.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1 style="color: indianred">
hello springmvc forward
</h1>
</body>
</html>
hi.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>Title</title>
</head>
<body>
<h1 style="color: royalblue">
hello springmvc redirect
</h1>
</body>
</html>
http://localhost:8080/fw
http://localhost:8080/rd