我是跟着b站狂神说的SpringMVC来进行学习的。这是我的笔记。www.bilibili.com/video/BV1aE…
一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第5天,点击查看活动详情。
通过注解实现 SpringMVC
第一步:导入依赖
第二步:将项目依赖存入 lib 目录下
第三步:配置 DispatchServlet
配置 web.xml 的操作。
前三步都是相同的。
第四步:创建 springmvc-servlet.xml
这是标配模板,直接在 resources 下直接创建复制粘贴就可以了。
-
<context:component-scan base-package="cn.hyz.controller"/>:需要填入自己对应的 controller 的包路径。
-
<mvc:default-servlet-handler />:过滤静态资源 。例如:HTML . JS . CSS . 图片 , 视频 .....
-
<mvc:annotation-driven />:表示开启了处理映射器和处理适配器。通过 url 知道找到对应的 controller 的类。
-
**视图解析器:**必须存在,在 controller 返回的页面需要由解析识别。加上前缀后缀。需要在视图解析器里指定你的视图文件存放的统一位置。放在 WEB-INF 包下可以保证我们的 视图资源的安全。
<?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
https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一管理 -->
<context:component-scan base-package="cn.hyz.controller"/>
<!-- 让Spring MVC不处理静态资源 -->
<mvc:default-servlet-handler />
<!--
支持mvc注解驱动
在spring中一般采用@RequestMapping注解来完成映射关系
要想使@RequestMapping注解生效
必须向上下文中注册DefaultAnnotationHandlerMapping
和一个AnnotationMethodHandlerAdapter实例
这两个实例分别在类级别和方法级别处理。
而annotation-driven配置帮助我们自动完成上述两个实例的注入。
-->
<mvc:annotation-driven />
<!-- 视图解析器 -->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
id="internalResourceViewResolver">
<!-- 前缀 -->
<property name="prefix" value="/WEB-INF/jsp/" />
<!-- 后缀 -->
<property name="suffix" value=".jsp" />
</bean>
</beans>
第五步:创建 Controller
package cn.hyz.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author workplace
* @date 2022/4/10 14:27
*/
@Controller
public class HelloController {
/*
* @Controller 代表这个类被 spring 接管
* 这个被注解的类中的所有方法,如果返回值是 String 且有具体页面可跳转,就会被视图解析器
*
* @RequestMapping("/hello") 表示在 url 中通过 /hello 识别到这个方法,返回对应的数据和视图。
* @RequestMapping("/test") 一般都是写在方法上。这样子如果一个类有太多方法也不会不知道 url 。
*
* model.addAttribute 就是处理数据
*
* return "Hello" 的就是 jsp 的名字
* */
@RequestMapping("/hello")
public String hello(Model model) {
model.addAttribute("msg", "你好程序员");
// 会被视图解析处理
return "Hello";
}
}
- @Controller:代表这个类被 spring 接管了。在这个类中的所有方法,如果返回值是 String 且有具体页面可跳转,就会被视图解析器。从而达到页面跳转的效果
- @RequestMapping("/value"):代表这个方法是通过 url 中的 /value 来识别。从而被调用。@RequestMapping("/value") 一般都是写在方法上。防止一个类有太多方法也不会不知道 url 。
- 方法里返回的 String 类型字符串:会被视图解析处理,从而跳转到对应的页面。
第六步:创建对应的 JSP 页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Title</title>
</head>
<body>
${msg}
</body>
</html>
Controller 的使用
控制器 Controller 的使用
- 控制器复杂提供访问应用程序的行为,通常通过接口定义或注解定义两种方法实现。
- 控制器负责解析用户的请求并将其转换为一个模型。
- 在Spring MVC中一个控制器类可以包含多个方法
- 在Spring MVC中,对于Controller的配置方式有很多种
我们可以通过类继承 Controller 来获取 控制器Controller,通过重写 控制器Controller 的方法来处理请求且返回一个模型与视图对象。
package cn.hyz.controller;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author workplace
* @date 2022/4/10 15:26
*/
public class ModelController implements Controller {
@Override
public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception {
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("msg", "你好程序员");
modelAndView.setViewName("test");
return modelAndView;
}
}
缺点:这是一个比较旧得方法,一个类只能写一个方法。如果需要得方法多了,代码量将大大增加。
@Controller 注解
package cn.hyz.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* @author workplace
* @date 2022/4/10 15:37
*/
@Controller
public class ModelController2 {
@RequestMapping("/test2")
public String test2(Model model) {
model.addAttribute("msg", "test2");
return "test";
}
@RequestMapping("/test3")
public String test3(Model model) {
model.addAttribute("msg", "test3");
return "test";
}
}
- 优点:可以发现,我们的两个请求都可以指向一个视图,但是页面结果的结果是不一样的,从这里可以看出视图是被复用的,而控制器与视图之间是弱偶合关系。
@RequestMapping 使用
@RequestMapping注解用于映射url到控制器类或一个特定的处理程序方法。
就是 @RequestMapping 匹配 url,从而对应的 url 可以匹配到对应的方法。
-
@RequestMapping 在方法上
@RequestMapping("/test3") public String test3(Model model) { model.addAttribute("msg", "test3"); return "test"; }当 url = localhost:8080/test3 便会调用这个方法。
-
@RequestMapping 在类和方法上
package cn.hyz.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; /** * @author workplace * @date 2022/4/10 16:14 */ @Controller @RequestMapping("/c3") public class ModelController3 { /* * @RequestMapping("/test") 一般都是写在方法上。这样子如果一个类有太多方法也不会不知道 url 。 * */ @RequestMapping("/test") public String test3(Model model) { model.addAttribute("msg", "调用了 test3 方法"); return "test"; } }当 url = localhost:8080/c2/test 便会调用这个方法。
@RequestMapping("/test") 一般都是写在方法上。这样子如果一个类有太多方法也不会不知道 url 。
RestFul 风格
概念
Restful就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
接下来分别介绍原始写法和三种 RestFul 风格的写法
方法一:原来的传参风格
@RequestMapping("/add1")
public String test1(int a, int b, Model model) {
int res = a + b;
model.addAttribute("msg", "结果为:" + res);
return "test";
}
这个方法在前端显示的 url 是 http://localhost:8080/add1?a=1&b=2。这样子可能会把数据库或后台的资源泄露,这种写法不太安全。
方法二:RestRul 风格
@RequestMapping("/add2/{a}/{b}")
public String test2(@PathVariable int a, @PathVariable int b, Model model) {
int res = a + b;
model.addAttribute("msg", "结果为:" + res);
return "test";
}
我们对方法的参数前添加 @PathVariable 注解,并且在 @RequestMapping 里定义了 url 格式,通过 ‘{’ ‘}’ 包裹着参数。
这个方法在前端显示的 url 是 http://localhost:8080/add2/1/2。可以看到参数没有参数名,而且使用 ‘/’ 断开。
方法三:改进后的 RestFul 风格
@RequestMapping(name = "/add3/{a}/{b}", method = RequestMethod.GET)
public String test3(@PathVariable int a, @PathVariable int b, Model model) {
int res = a + b;
model.addAttribute("msg", "结果为:" + res);
return "test";
}
-
改进后的 RestFul 风格:name = "/add/{a}/{b}",method = RequestMethod.GET。
可以通过 method 参数来指定请求类型:GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE;
这个方法在前端显示的 url 是
http://localhost:8080/add2/1/2。 -
如果使用方法三,在提交不同类型的参数进行操作,明明出入的参数类型符合要求却显示 500 错误。可以把 name="url" 改为 value = "url" 或 path = "url"
方法四:最常用的 RestFul
@GetMapping("/add4/{a}/{b}")
public String test4(@PathVariable int a, @PathVariable int b, Model model) {
int res = a + b;
model.addAttribute("msg", "结果为:" + res);
return "test";
}
- 最后我们可以通过 @GetMapping、@PostMapping、@PutMapping、@DeleteMapping、@PatchMapping 来指定请求类型
总结
- RestFul 是一种风格。
- 通过对要传递的参数前添加 @PathVariable 并且在 @@RequestMapping / @请求类型Mapping 中规定 url 格式,就可以做到 RestFul 风格。
- RestFul 风格的好处有
- 简洁,让路径更加简洁
- 安全,不会通过 url 来暴露信息。
- 高效,支持缓存
- 如果在使用过程中出现 405 错误可能是请求方式出现问题,可以通过检查路径的格式是否符合请求类型。
package cn.hyz.controller;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
/**
* @author workplace
* @date 2022/4/10 16:27
*/
@Controller
public class RestFulController {
/*
* 方法一:
* 原来的传参风格:http://localhost:8080/add1?a=1&b=2
*
* 方法二:
* RestRul 风格:http://localhost:8080/add2/1/2
*
* 方法三:
* 改进后的 RestFul 风格:name = "/add/{a}/{b}",method = RequestMethod.GET。
* 可以通过 method 参数来指定请求类型:
* GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE;
* http://localhost:8080/add3/1/2
*
* 方法四:
* 最后我们可以通过 @GetMapping、@PostMapping、@PutMapping、@DeleteMapping、@PatchMapping
* 来指定请求类型:http://localhost:8080/add3/1/2
*
* 使用 RestFul 风格可以达到相同的地址实现不同的功能。
*
* 所有的 RestFul 风格都需要在调用的方法里除了 Method 参数之外的所有参数前都添加 @PathVariable 这个注解。
* 并且要在 url 中映射对应的参数
*
* 不同的请求类型可以通过 method 或不同类型的注解来完成。、
*
* 如果使用方法三,想要提交不同类型的参数进行操作却显示 500 错误。
* 可以把 name="url" 改为 value = "url" 或 path = "url"
*
* 出现 405 错误可能是请求方式出现问题
*
* 好处:
* 简介,让路径更加简洁
* 安全,不会通过 url 来暴露信息。
* 高效,支持缓存
*
* */
@RequestMapping("/add1")
public String test1(int a, int b, Model model) {
int res = a + b;
model.addAttribute("msg", "结果为:" + res);
return "test";
}
@RequestMapping("/add2/{a}/{b}")
public String test2(@PathVariable int a, @PathVariable int b, Model model) {
int res = a + b;
model.addAttribute("msg", "结果为:" + res);
return "test";
}
@RequestMapping(value = "/add3/{a}/{b}", method = RequestMethod.GET)
public String test3(@PathVariable int a, @PathVariable int b, Model model) {
int res = a + b;
model.addAttribute("msg", "结果为:" + res);
return "test";
}
@GetMapping("/add4/{a}/{b}")
public String test4(@PathVariable int a, @PathVariable int b, Model model) {
int res = a + b;
model.addAttribute("msg", "结果为:" + res);
return "test";
}
}