1. Spring MVC概述
SpringMVC是Spring的一个模块,是一个基于MVC(Model-View-Controller)设计模式的web框架。
2. Spring MVC执行流程
组件分析:
-
前端控制器(默认配置 ):分发请求,统一处理请求和响应
-
处理器映射器(默认配置):根据配置的映射规则(根据请求的URL),找到对应的处理器
-
处理器适配器(默认配置):适配调用具体的处理器,并且执行处理器中处理请求的方法
-
处理器(需要开发)
-
视图解析器:会根据传递过来的ModelAndView对象进行视图解析,根据视图解析名解析称真正的视图View
-
视图:View是一个接口,它的实现类支持不同类型的视图。比如:JSP、freemarker、Thymeleaf等等。
3. Handler
引入SpringMVC依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
3.1 接收请求参数
-
简单类型的参数,Spring MVC可以直接帮我们封装成参数列表中声明的类型,比如String、int、double......
java//http://localhost:8080/login?username=zs&password=123 @RequestMapping("/login") public void login(String username, String password){ System.out.println(username); System.out.println(password); }
-
或者也可以直接接收一个Java Bean
java//http://localhost:8080/login?name=zs&age=23&birthday=2020-02-02 @RequestMapping("/login") public void login(Person person) { System.out.println(person.getName()); System.out.println(person.getAge()); }
-
如果请求参数是一个日期,Spring MVC并不能直接封装到Date中,需要设置一下日期格式。
javapublic class Person{ private String username; private String password; @DateTimeFormat(pattern = "yyyy-MM-dd")//前端到后端 可以把前端string类型转换到后端date类型 //@JsonFormat(pattern = "yyyy-MM-dd")//后端到前端 可以把后端date类型转换到前端string private Date birthday; // getters & setters... }
-
如果请求参数是一个数组类型,我们可以直接通过数组接收。
javahttp://localhost:8080/delSel?ids=1&ids=2 请求参数ids为方法的参数 @RequestMapping("delSel") public String delSel(Integer[] ids) { System.out.println(Arrays.toString(ids)); return "index"; }
-
如果想要用集合类型来接收数组参数,需要加上**@RequestParam**注解
3.2 获取servletAPI
可以在Handler的形参中直接使用以下类型:
-
HttpServletRequest
通过request对象获取请求信息 -
HttpServletResponse
通过response处理响应信息 -
HttpSession
通过session对象得到session中存放的对象
java
@RequestMapping("servletTest")
public void servletTest(
HttpServletRequest request,
HttpServletResponse response,
HttpSession session
) throws IOException
{
System.out.println(request.getContextPath());
response.setContentType("text/html;charset=utf-8");
response.getWriter().write("我是");
System.out.println(session.getId());
}
3.3 返回值
可以为Handler指定两种种返回值类型:
-
void
如果返回值为
void
的时候,可以在Handler形参上定义request
和response,使用
request或
response指定响应结果 -
String
逻辑视图名称
返回的字符串就是逻辑视图。
java@RequestMapping("login2") public void login2(HttpServletRequest request, HttpServletResponse response) throws IOException { String username = request.getParameter("username"); System.out.println(username); response.setContentType("text/html;charset=utf-8"); response.getWriter().println("返回值"); } @RequestMapping("/login3") public String login3() { //return "forward:/login1";//可以请求转发到另一个handler //return "forward:/index.jsp";//可以请求转发到另一个jsp return "redirect:/index.jsp";//可以重定向到另一个jsp 注意观察地址栏 }
3.4 注解
-
@Controller:用于声明一个类为Spring MVC的控制器。控制器负责处理用户请求。
@Controller public class TestController1 { // ... }
-
@RequestMapping:用于将HTTP请求映射到控制器的方法上。可以指定请求路径、请求方法等。
@RequestMapping(value = "/login1", method = RequestMethod.GET) public String login(Person person) { // ... }
-
@RequestParam
@RequestParam:用于将请求参数绑定到控制器方法的参数上。可以指定参数名、是否必须、默认值等。该注解用来标注一个请求参数:
在方法的形参前,可以加可以不加,加了就有特殊含义了
java@RequestMapping(value = "/login3") public String login3(String name) { System.out.println(name); return "index"; } //上述方式在请求 login3 可以不强制传递请求参数,那打印name结果是null @RequestMapping(value = "/login3") public String login3(@RequestParam String name) { System.out.println(name); return "index"; } //上述方式在请求 login3 强制必须传递请求参数,那打印name结果就是请求参数传的值 //http://localhost:8080/login3?name1=tom @RequestMapping(value = "/login3") public String login3(@RequestParam("name1") String name) { System.out.println(name); return "index"; } //required = false,默认是true 代表着必须传递参数,如果设置false代表可以不传递参数 @RequestMapping(value = "/login3") public String login3(@RequestParam(name = "name1",defaultValue = "zs") String name) { System.out.println(name); return "index"; } //设置默认值,这样required就不需要在写了。 public String login(@RequestParam(value = "username", required = true, defaultValue = "noname") String name)
value:@RequestParam(value="username")等同于@RequestParam("username") required:参数是否必填 defaultValue:设置默认值
-
@PathVariable:用于将URL中的占位符参数绑定到控制器方法的参数上。
将路径的一部分作为请求参数,RESTful的基础。
java//http://localhost:8080/findById/1 @RequestMapping("/findById/{id}") public String findById(@PathVariable Integer id) { System.out.println(id); return "index"; } //请求参数就不可以是?号。参数必须是目录形式。
RESTful就是一个url的编写风格。使用RESTful就不需要用❓问号分割请求参数了。
一个严格的RESTful中是不可能存在❓的。
一个URl对应一个资源,请求方式确定一个动作。
ps:有一张person表id有1 2 3 ,name有zs,lisi,wangwu。
POST请求:就是向数据库中添加数据。/person,代表把person对象添加到表中
DELETE请求:就是向数据库中删除一条数据。 /person/1 ,代表把person表中id为1的 删除
PUT请求:就是向数据库修改一条数据。/person/1,代表把person表中id为1的 修改
GET请求:就是向数据库表查询数据。/person/3, 代表把person表中id为3的 查询出来
-
@ResponseBody:用于将控制器方法的返回值作为HTTP响应的正文返回,而不是返回一个视图。
@RequestMapping("/hello") @ResponseBody public String hello() { return "Hello, World!"; }
-
@RequestBody:用于将请求正文绑定到控制器方法的参数上,通常用于处理JSON或XML格式的请求数据。
@RequestMapping(value = "/save", method = RequestMethod.POST) @ResponseBody public String save(@RequestBody Person person) { // ... }
-
@RestController:是@Controller和@ResponseBody的组合注解,用于声明一个类为REST风格的控制器。
@RestController public class TestRestController { // ... }
-
@GetMapping 、@PostMapping 、@PutMapping 、@DeleteMapping:分别用于处理GET、POST、PUT、DELETE请求的快捷方式。
@GetMapping("/users") public List<User> getUsers() { // ... }
4. 静态资源访问
springboot中放置静态资源的目录,常用有static和templates目录,只要把静态资源放到这几个目录下,就能直接访问到
http://localhost:8080/login.html
http://localhost:8080/login1.html
static目录下静态资源默认是可以被访问到的,但是templates目录下资源默认是访问不到的,需要做配置:
spring.web.resources.static-locations=classpath:/templates
也可以直接给所有静态资源添加一个前缀,既可统一拦截,又可统一放开
spring.mvc.static-path-pattern=/res/**
http://localhost:8080/res/login1.html
5. 文件上传
5.1 同步表单上传
添加fileupload依赖
<dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.4</version> </dependency>
配置
在配置文件中添加上传文件最大值
# 开启文件大小配置 默认值true spring.servlet.multipart.enabled=true #单个文件数据大小 spring.servlet.multipart.max-file-size=2048MB #总数据的大小 spring.servlet.multipart.max-request-size=2048MB 1 Byte = 8 Bits 1 Kilobyte (KB) = 1024 Bytes 1 Megabyte (MB) = 1024 KB 1 Gigabyte (GB) = 1024 MB 1 Terabyte (TB) = 1024 GB 1 Petabyte (PB) = 1024 TB 1 Exabyte (EB) = 1024 PB 1 Zettabyte (ZB) = 1024 EB 1 Yottabyte (YB) = 1024 ZB
form表单
java
<form action="/file/fileUpload" method="post" enctype="multipart/form-data">
图片上传: <input type="file" name="img">
<br>
<button>上传</button>
</form>
Controller编写
java
@Controller
public class FileController {
@PostMapping("/file/fileUpload")
public String fileTest(MultipartFile img) {
System.out.println(img.getSize());
System.out.println("文件上传");
return "redirect:/index.html";
}
}
测试
http://localhost:8080/login.html
5.2 高级上传
需求:跨服务器上传图⽚,⻚⾯不刷新,图⽚即时回显。
⻚⾯不刷新:AJAX
图⽚即时回显:
<img src=""/>
先导入依赖
<dependency> <groupId>io.minio</groupId> <artifactId>minio</artifactId> <version>8.4.1</version> </dependency>
编写form表单
java
<head>
<meta charset="UTF-8">
<title>Title</title>
<script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.5.1/jquery.js"></script>
<script src="js/jquery.form.min.js"></script>
</head>
<body>
<form id="form1">
图像:
<input type="file" name="f" onchange="doUpload()" style="display:none">
<br>
<img id="img1" width="100px " src="https://oss.coding-future.com:9000/fileupload/icon.JPG" onclick="selectImg()">
</form>
</body>
<script>
function doUpload() {
console.log("图片被点击了");
//异步提交表单
$('#form1').ajaxSubmit({
url: '/file/fileUploadAdv',
type: 'post',
success: function (data) {
$("#img1").prop("src", data)//修改src属性
}
})
}
function selectImg() {
$('input[name="f"]').click();
}
</script>
</html>
Controller
java
@Controller
public class FileController {
@PostMapping("/file/fileUploadAdv")
public void fileUpload(@RequestParam("f") MultipartFile f) throws Exception {
MinioClient client = MinioClient.builder()
.endpoint("https://oss.coding-future.com:9000")
.credentials("student", "12345678")
.build();
client.putObject(
PutObjectArgs.builder().
bucket("fileupload").
object(f.getOriginalFilename()).
stream(f.getInputStream(), f.getSize(), -1)
.contentType(f.getContentType())
.build());
}
}
6. 拦截器
javaweb三大组件:servlet、filter、listener
6.1创建拦截器
实现HandlerInterceptor
接口
java
/**
* 创建拦截器
*/
public class LoginInterceptor implements HandlerInterceptor {
//该方法在handler处理请求之前被调用
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
//return true;//拦截失败 通过
return false;//拦截成功,不通过
}
//该方法在当前handler处理之后,也就是Controller方法被调用之后执行
@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler,
ModelAndView modelAndView) {
}
// 在页面渲染之后进行处理
@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) {
}
}
6.2 配置拦截器
java
//将配置注入容器
@Configuration
public class WebConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(new LoginInterceptor())
//拦截所有资源
.addPathPatterns("/**")
//将指定资源放行
.excludePathPatterns("/index.html", "/login","/js/**");
}
}
6.3 简单实现
java
public class LoginInterceptor implements HandlerInterceptor {
/**
* 在接口调用前 执行
*
* @param request
* @param response
* @param handler
* @return true 放行 不拦截 否则...
*/
@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) {
HttpSession session = request.getSession();
Object username = session.getAttribute("username");
if (username != null) {
//放行
return true;
}
Map<String, Object> map = new HashMap<>();
map.put("code", 403);
map.put("data", "list");
map.put("message", "success");
String data = JSON.toJSONString(map);
response.getWriter().println(data);
retrn false;
}
}