From da5a5d13149bd11a30b2f2a972ddf80cff692671 Mon Sep 17 00:00:00 2001 From: Bunny <1319900154@qq.com> Date: Thu, 23 Jan 2025 17:22:42 +0800 Subject: [PATCH] =?UTF-8?q?docs:=20=E7=A7=BB=E5=8A=A8=E6=96=87=E6=A1=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README/SpringMVC笔记.md | 1131 +++++++++++++++++ .../SpringMVC笔记/image-20250122171536808.png | Bin mvc/SpringMVC笔记.md | 433 ------- .../mvc/controller/ThymeleafController.java | 13 + .../templates/thymeleaf/href-test.html | 30 + 5 files changed, 1174 insertions(+), 433 deletions(-) create mode 100644 README/SpringMVC笔记.md rename {mvc => README}/images/SpringMVC笔记/image-20250122171536808.png (100%) delete mode 100644 mvc/SpringMVC笔记.md create mode 100644 mvc/src/main/resources/templates/thymeleaf/href-test.html diff --git a/README/SpringMVC笔记.md b/README/SpringMVC笔记.md new file mode 100644 index 0000000..a25bd60 --- /dev/null +++ b/README/SpringMVC笔记.md @@ -0,0 +1,1131 @@ +# `SpringMVC`笔记 + +- 公共AI网站:https://chatgptplus.cn/ +- vue3官网:https://cn.vuejs.org/ +- SpringBoot官网:https://docs.spring.io/spring-boot/index.html + +## Ant风格访问 + +Spring MVC 支持 **Ant 风格**(Ant-style path matching)主要体现在 URL 路径匹配模式上。这种模式是 Spring Web 框架用来进行 URL 模式匹配的一种方式,借用了 Apache Ant(一个流行的 Java 构建工具)中的路径匹配规则。Ant 风格的路径匹配使得 URL 路径映射更加灵活和方便。 + +- `*` 匹配单一路径层级中的任意字符。 +- `**` 匹配任意多个路径层级。 +- `?` 匹配单个字符。 + +### 1. **Ant 风格路径匹配规则** + +Ant 风格的路径匹配规则中有几个特殊字符,分别是 `*`、`**` 和 `?`,它们具有不同的匹配意义: + +#### (1) `*`(匹配零个或多个字符) + +- `*` 可以匹配路径中的任何部分,但只能匹配单一层级中的路径。 +- 举个例子,`/foo/*` 可以匹配 `/foo/bar` 或 `/foo/abc`,但不能匹配 `/foo/bar/baz`。 +- 示例: + - `/foo/*` 匹配 `/foo/bar`。 + - `/foo/*/bar` 匹配 `/foo/abc/bar`。 + +#### (2) `**`(匹配零个或多个目录) + +- `**` 可以匹配多个目录层级,它比 `*` 更加强大,能够跨越多个层级。 +- 示例: + - `/foo/**/bar` 匹配 `/foo/bar`、`/foo/abc/bar`、`/foo/abc/def/bar` 等。 + - `/foo/**/bar/**/baz` 匹配 `/foo/abc/bar/xyz/baz`。 + +#### (3) `?`(匹配单个字符) + +- `?` 用于匹配单个字符,不是零个或多个字符。它通常用于精确匹配某些路径中的单个字符。 +- 示例: + - `/foo/a?c` 可以匹配 `/foo/abc`,但不能匹配 `/foo/abcc`。 + +### 2. **Ant 风格的路径匹配应用** + +Spring MVC 采用了这种路径匹配方式,使得映射 URL 路径时更加灵活。例如,使用 `@RequestMapping` 注解来定义控制器方法时,可以利用 Ant 风格的路径匹配规则。 + +#### 示例 1:`@RequestMapping` 使用 Ant 风格 + +```java +@Controller +public class MyController { + + @RequestMapping("/foo/*") // 匹配路径 /foo/bar 或 /foo/abc + public String handleFoo() { + return "foo"; + } + + @RequestMapping("/foo/**") // 匹配路径 /foo/bar 或 /foo/abc/xyz + public String handleFooRecursive() { + return "fooRecursive"; + } + + @RequestMapping("/foo/a?c") // 匹配路径 /foo/abc,但不匹配 /foo/abcc + public String handleSpecificPattern() { + return "specificPattern"; + } +} +``` + +在这个例子中: + +- `/foo/*` 只会匹配 `/foo/bar` 或 `/foo/abc` 等简单路径。 +- `/foo/**` 会匹配 `/foo/bar`、`/foo/abc/xyz` 等多层次路径。 +- `/foo/a?c` 会匹配 `/foo/abc`,但不会匹配 `/foo/abcc`。 + +#### 示例 2:`@RequestMapping` 配合请求方法 + +Spring MVC 还支持在映射中结合请求方法(如 `GET`、`POST`)来实现更细粒度的路径匹配: + +```java +@Controller +@RequestMapping("/foo") +public class FooController { + + @RequestMapping(value = "/bar/*", method = RequestMethod.GET) // GET 请求 + public String handleBar() { + return "barGET"; + } + + @RequestMapping(value = "/bar/**", method = RequestMethod.POST) // POST 请求 + public String handleBarPost() { + return "barPOST"; + } +} +``` + +在这个例子中: + +- `/foo/bar/*` 仅在处理 `GET` 请求时匹配。 +- `/foo/bar/**` 仅在处理 `POST` 请求时匹配。 + +### 3. **Ant 风格路径与通配符的结合使用** + +在实际开发中,Spring MVC 支持 Ant 风格路径的同时,还可以与路径变量、正则表达式等功能结合使用。 + +#### 示例 1:路径变量 + Ant 风格 + +```java +@RequestMapping("/user/{id}/**") // 匹配多层路径,id 为路径变量 +public String handleUser(@PathVariable("id") String userId) { + return "User ID: " + userId; +} +``` + +- 这个路径匹配 `/user/123/abc/xyz`,其中 `id` 会捕获为 `123`。 + +#### 示例 2:正则表达式 + Ant 风格 + +```java +@RequestMapping("/product/{id:\\d+}/**") // 正则匹配数字 id +public String handleProduct(@PathVariable("id") String productId) { + return "Product ID: " + productId; +} +``` + +- 这里的路径 `/product/{id:\\d+}/**` 只会匹配数字形式的 `id`,比如 `/product/123/abc/xyz`。 + +### 4. **优先级和匹配规则** + +在使用 Ant 风格路径匹配时,路径匹配的优先级有一定的规则。具体来说,`/**` 会匹配任何路径,所以它的优先级通常较低,避免与其他精确匹配的路径冲突。 + +#### 示例:优先级比较 + +```java +@Controller +@RequestMapping("/foo") +public class FooController { + + @RequestMapping("/foo/{id}") // 精确匹配路径 /foo/{id} + public String handleFoo(@PathVariable String id) { + return "foo:" + id; + } + + @RequestMapping("/foo/**") // 匹配所有以 /foo/ 开头的路径 + public String handleFooCatchAll() { + return "catchAll"; + } +} +``` + +在这个例子中,如果访问 `/foo/bar`,它会首先匹配 `/foo/{id}`,因为它更精确。 + +## 访问控制 + +请求地址时返回对应的网页文件 + +- `@RestController`用于返回对象格式的内容,在后面会使用`ModelAndView`可以返回网页文件 +- `@Controller`用于返回网页文件 + +### 环境要求 + +```xml + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-test + test + + + + org.springframework.boot + spring-boot-starter-thymeleaf + + + + org.springframework.boot + spring-boot-devtools + runtime + true + + + + org.projectlombok + lombok + 1.18.36 + +``` + +### 错误页面设置 + +在这个路径下可以配置错误码要访问的页面,也就是可以自定义页面内容 + +![image-20250122171536808](./images/SpringMVC笔记/image-20250122171536808.png) + +### 使用`@Controller` + +返回需要访问的HTML内容页面,最后返回的字符串就是页面,这个页面位于`templates`目录下 + +```java +@RequestMapping("/use") +@Controller +public class UseController { + + // 带参数访问 + @RequestMapping(value = "hello", method = RequestMethod.GET, params = {"name"}) + public String hello() { + return "hello"; + } + + @GetMapping("jumpPage") + public String jumpPage() { + return "jumpPage"; + } + + @GetMapping("index") + public String quick() { + return "user"; + } + + // 跳转的页面 + @GetMapping("toJump") + public String toJump() { + return "redirect:jumpPage"; + } +} +``` + +如果在使用`@Controller`需要返回JSON内容,需要在控制器方法上加上`@ResponseBody` + +```java +@GetMapping("getJson") +@ResponseBody +public List getJson() { + ArrayList list = new ArrayList<>(); + list.add("a"); + list.add("b"); + list.add("c"); + + return list; +} +``` + +#### 将视图和模型拆开 + +```java +// 将视图和模型拆开 +@GetMapping("page/test3") +public String test3(Model model) { + model.addAttribute("test3", "测试3"); + return "page/test3"; +} +``` + +### 使用`@RestController` + +#### 使用方式1 + +如果使用`@RestController`那么返回的就是JSON对象,但是这时候要想返回网页文件,需要使用`ModelAndView` + +```java +@RequestMapping("userRest") +@RestController +public class UseRestController { + + @GetMapping("page/test") + public ModelAndView test() { + ModelAndView modelAndView = new ModelAndView(); + modelAndView.setViewName("page/test"); + modelAndView.addObject("message", "这是消息内容"); + return modelAndView; + } +} +``` + +我们引入了`thymeleaf`所以有以下内容`

` + +```html + + + + + 使用RestController返回页面信息 + + +

使用RestController返回页面信息

+

+ + +``` + +> 其中`modelAndView.addObject("message", "这是消息内容");`是可选的 + +#### 使用方式2 + +在控制器方法上使用`ModelAndView` + +```java +@GetMapping("page/test2") +public ModelAndView test2(ModelAndView modelAndView) { + modelAndView.addObject("hello", "你好"); + modelAndView.setViewName("page/test2"); + return modelAndView; +} +``` + +## 向session共享数据 + +在 Spring MVC 中,**Session** 是用于存储用户会话期间的数据的一种机制。每个用户访问的应用程序都将拥有一个唯一的会话。通过 `HttpSession`,可以在用户的会话中存储一些数据,直到用户关闭浏览器或会话过期。 + +Spring MVC 提供了多种方式来与 `HttpSession` 进行交互,下面详细介绍如何通过 `HttpSession` 向 Session 共享数据。 + +### 1. **通过 `HttpSession` 操作 Session 数据** + +在 Spring MVC 控制器中,您可以通过 `HttpSession` 对象来存储和读取会话数据。 + +#### 示例:将数据添加到 Session + +```java +@Controller +public class SessionController { + + @RequestMapping("/setSessionData") + public String setSessionData(HttpSession session) { + // 将数据存储到 Session 中 + session.setAttribute("username", "JohnDoe"); + session.setAttribute("age", 30); + return "sessionSet"; // 返回视图名 + } + + @RequestMapping("/getSessionData") + public String getSessionData(HttpSession session, Model model) { + // 从 Session 中获取数据 + String username = (String) session.getAttribute("username"); + Integer age = (Integer) session.getAttribute("age"); + model.addAttribute("username", username); + model.addAttribute("age", age); + return "sessionData"; // 返回视图名 + } +} +``` + +**URL 请求:** + +- `GET /setSessionData` 会将数据 `"username": "JohnDoe"` 和 `"age": 30` 存储到 Session 中。 +- `GET /getSessionData` 会从 Session 中获取并显示存储的值。 + +### 2. **使用 `@SessionAttributes` 注解** + +`@SessionAttributes` 注解用于将控制器中的某些模型属性放入 Session 中。这种方式比直接操作 `HttpSession` 更为方便和简洁,特别是当需要共享多个模型属性时。 + +#### 示例:使用 `@SessionAttributes` + +```java +@Controller +@SessionAttributes("user") +public class UserController { + + // 在模型中添加用户对象 + @RequestMapping("/setUser") + public String setUser(Model model) { + User user = new User("John", 30); + model.addAttribute("user", user); + return "userSet"; + } + + // 从 Session 中获取用户对象 + @RequestMapping("/getUser") + public String getUser(@ModelAttribute("user") User user, Model model) { + model.addAttribute("user", user); + return "userDetails"; + } +} +``` + +**URL 请求:** + +- `GET /setUser` 会将 `user` 对象放入 Session 中。 +- `GET /getUser` 会从 Session 中获取 `user` 对象。 + +`@SessionAttributes` 注解不仅可以放入 Session 中,还可以与 `@ModelAttribute` 注解结合使用,确保模型数据保持在 Session 中。 + +### 3. **使用 `@ModelAttribute` 注解** + +`@ModelAttribute` 注解允许将数据放入模型中,并且在方法调用前通过 `Model` 传递给视图。如果和 `@SessionAttributes` 一起使用,它可以将属性直接添加到 `HttpSession`。 + +#### 示例:使用 `@ModelAttribute` 和 `@SessionAttributes` + +```java +@Controller +@SessionAttributes("cart") +public class CartController { + + // 在模型中创建并存储购物车 + @ModelAttribute("cart") + public Cart createCart() { + return new Cart(); // 创建一个空的购物车对象 + } + + // 添加商品到购物车 + @RequestMapping("/addToCart") + public String addToCart(@ModelAttribute("cart") Cart cart, @RequestParam("item") String item) { + cart.addItem(item); // 将商品添加到购物车 + return "cartUpdated"; + } + + // 显示购物车内容 + @RequestMapping("/viewCart") + public String viewCart(@ModelAttribute("cart") Cart cart, Model model) { + model.addAttribute("cart", cart); + return "viewCart"; + } +} +``` + +**URL 请求:** + +- `GET /addToCart?item=Apple` 会将 `Apple` 添加到 `cart` 中。 +- `GET /viewCart` 会显示购物车中的内容。 + +### 4. **通过 `@RequestParam` 或 `@PathVariable` 获取 Session 数据** + +如果在请求中需要通过路径变量或请求参数传递数据并存储到 Session 中,可以结合 `@RequestParam` 或 `@PathVariable` 来实现。 + +#### 示例:使用 `@RequestParam` 存储 Session 数据 + +```java +@Controller +public class SessionController { + + @RequestMapping("/setSession/{username}") + public String setSessionData(@PathVariable("username") String username, HttpSession session) { + session.setAttribute("username", username); + return "sessionSet"; + } + + @RequestMapping("/getSession") + public String getSessionData(HttpSession session, Model model) { + String username = (String) session.getAttribute("username"); + model.addAttribute("username", username); + return "sessionData"; + } +} +``` + +**URL 请求:** + +- `GET /setSession/JohnDoe` 会将 `"username": "JohnDoe"` 存储到 Session 中。 +- `GET /getSession` 会从 Session 中获取并显示 `username`。 + +### 5. **删除 Session 数据** + +如果希望在某个操作后清除 Session 中的某些数据,可以使用 `HttpSession` 提供的 `removeAttribute` 方法。 + +#### 示例:删除 Session 数据 + +```java +@Controller +public class SessionController { + + @RequestMapping("/removeSessionData") + public String removeSessionData(HttpSession session) { + session.removeAttribute("username"); // 删除指定的属性 + return "sessionRemoved"; + } +} +``` + +**URL 请求:** + +- `GET /removeSessionData` 会从 Session 中删除 `"username"` 属性。 + +### 6. **Session 过期与清理** + +默认情况下,Spring MVC 的 `HttpSession` 会话会在用户关闭浏览器后过期,或者会话超时(默认30分钟)。可以在 `web.xml` 或应用的配置类中设置会话超时: + +#### 示例:设置 Session 超时 + +```xml + + 30 + +``` + +### 7. **通过 Spring 配置 Session** + +通过 Spring 配置文件或 Java 配置类,还可以控制 Session 的相关行为(如会话过期时间、session 的持久化等)。 + +#### 示例:Java 配置类 + +```java +@Configuration +@EnableWebMvc +public class WebConfig implements WebMvcConfigurer { + + @Override + public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(new SessionInterceptor()).addPathPatterns("/**"); + } +} +``` + +`SessionInterceptor` 可以用于监控和管理 Session 数据。 + +### 8. **总结** + +在 Spring MVC 中,向 Session 共享数据主要有以下几种方式: + +- **`HttpSession`**:通过 `HttpSession` 对象存储和读取 Session 数据。 +- **`@SessionAttributes`**:通过 `@SessionAttributes` 注解将模型属性添加到 Session 中。 +- **`@ModelAttribute`**:结合 `@SessionAttributes` 使用,将模型数据持久化到 Session 中。 +- **`@RequestParam` 和 `@PathVariable`**:将请求参数或路径变量存储到 Session 中。 +- **Session 过期与清理**:可以通过配置控制会话超时,或手动清除 Session 数据。 + +Spring MVC 提供的 Session 管理机制非常灵活,能够满足各种需求。 + +## 向application域共享数据 + +在 Spring MVC 中,`Application` 域(也称为 **ServletContext**)是一个全局范围,用于在整个应用程序中共享数据。不同于 **Session** 域和 **Request** 域,`Application` 域中的数据对整个 Web 应用的所有用户都是可见的,因此适合存储全局共享的配置信息、常量、初始化数据等。 + +在 Spring MVC 中,我们可以通过 `ServletContext` 来向 **Application** 域共享数据,通常使用 `setAttribute` 和 `getAttribute` 方法来进行操作。 + +### 1. **通过 `ServletContext` 共享数据** + +`ServletContext` 是与整个 Web 应用程序相关联的对象,它允许你在多个请求和多个会话之间共享数据。在 Spring MVC 中,可以通过以下两种方式来访问 `ServletContext`: + +- 直接使用 `HttpServletRequest.getSession().getServletContext()` 获取 `ServletContext`。 +- 使用 `@Autowired` 注解注入 `ServletContext` 对象。 + +#### 示例 1:通过 `ServletContext` 共享数据 + +##### 1.1 通过 `HttpServletRequest` 获取 `ServletContext` + +```java +@Controller +public class ApplicationController { + + @RequestMapping("/setApplicationData") + public String setApplicationData(HttpServletRequest request) { + // 获取 ServletContext + ServletContext servletContext = request.getServletContext(); + // 向 Application 域存储数据 + servletContext.setAttribute("appName", "SpringMVCApp"); + servletContext.setAttribute("version", "1.0.0"); + return "applicationDataSet"; + } + + @RequestMapping("/getApplicationData") + public String getApplicationData(HttpServletRequest request, Model model) { + // 获取 ServletContext + ServletContext servletContext = request.getServletContext(); + // 从 Application 域获取数据 + String appName = (String) servletContext.getAttribute("appName"); + String version = (String) servletContext.getAttribute("version"); + + model.addAttribute("appName", appName); + model.addAttribute("version", version); + return "applicationData"; + } +} +``` + +**URL 请求:** + +- `GET /setApplicationData` 会向 `Application` 域中存储 `"appName": "SpringMVCApp"` 和 `"version": "1.0.0"`。 +- `GET /getApplicationData` 会从 `Application` 域中读取数据,并返回给视图。 + +##### 1.2 通过 `@Autowired` 注入 `ServletContext` + +```java +@Controller +public class ApplicationController { + + @Autowired + private ServletContext servletContext; + + @RequestMapping("/setApplicationData") + public String setApplicationData() { + // 向 Application 域存储数据 + servletContext.setAttribute("appName", "SpringMVCApp"); + servletContext.setAttribute("version", "1.0.0"); + return "applicationDataSet"; + } + + @RequestMapping("/getApplicationData") + public String getApplicationData(Model model) { + // 从 Application 域获取数据 + String appName = (String) servletContext.getAttribute("appName"); + String version = (String) servletContext.getAttribute("version"); + + model.addAttribute("appName", appName); + model.addAttribute("version", version); + return "applicationData"; + } +} +``` + +在这个例子中,`@Autowired` 注解会自动将 `ServletContext` 注入到控制器中。 + +### 2. **应用场景** + +将数据放到 `Application` 域中通常用于存储以下类型的数据: + +- **应用级别的数据**:例如应用名称、版本号、初始化配置等,这些数据是全局共享的。 +- **常量和初始化信息**:如果有一些需要在多个请求中共享的常量或初始化信息,可以将它们放到 `Application` 域中。 +- **数据库连接池或常用资源**:对于一些全局共享的资源(如数据库连接池),可以在 `Application` 域中进行配置并在不同的请求中共享。 + +### 3. **注意事项** + +- **全局共享**:与 `Session` 和 `Request` 域不同,`Application` 域中的数据对所有用户和请求都可见。因此,要特别小心在 `Application` 域中存储敏感数据,避免泄漏用户个人信息等。 +- **生命周期**:`Application` 域中的数据在整个应用程序生命周期内有效,直到应用服务器重新启动。因此,放入 `Application` 域的数据一般是全局的、不会频繁变化的。 +- **线程安全**:`Application` 域的数据是共享的,因此在并发访问时要考虑线程安全问题。如果有多个线程访问同一数据,可能需要进行同步。 + +### 4. **清理 Application 域中的数据** + +如果不再需要某个共享数据,可以使用 `removeAttribute` 方法从 `Application` 域中移除该数据。 + +#### 示例:删除 `Application` 域中的数据 + +```java +@Controller +public class ApplicationController { + + @RequestMapping("/removeApplicationData") + public String removeApplicationData(HttpServletRequest request) { + // 获取 ServletContext + ServletContext servletContext = request.getServletContext(); + // 从 Application 域移除数据 + servletContext.removeAttribute("appName"); + servletContext.removeAttribute("version"); + return "applicationDataRemoved"; + } +} +``` + +**URL 请求:** + +- `GET /removeApplicationData` 会从 `Application` 域中移除 `appName` 和 `version` 属性。 + +### 5. **使用 `@ApplicationScope`(Spring 方式)** + +如果使用 Spring 框架进行开发,也可以使用 Spring 提供的 **`@ApplicationScope`** 注解来定义在整个应用范围内共享的 Bean。这种方法通常用于 Spring 组件,而不是直接操作 `ServletContext`。 + +#### 示例:使用 `@ApplicationScope` + +```java +@Component +@Scope("application") +public class AppConfig { + private String appName = "SpringMVCApp"; + + public String getAppName() { + return appName; + } +} +``` + +在这种情况下,Spring 管理的 Bean 会在应用级别共享,类似于 `ServletContext` 中存储的数据。 + +### 6. **总结** + +- `Application` 域(即 `ServletContext`)用于在整个应用程序范围内共享数据,适合存储全局共享的信息、配置和常量。 +- 通过 `HttpServletRequest.getServletContext()` 或 `@Autowired` 注解可以访问 `ServletContext` 并向 `Application` 域中共享数据。 +- 数据存储在 `Application` 域中可以在整个应用程序生命周期内有效,适用于共享全局性的、无需频繁更新的数据。 +- 应谨慎存储敏感数据,并注意线程安全和数据的生命周期。 + +通过合理地使用 `Application` 域,你可以实现全局共享的数据管理,并且不需要担心会话或请求的生命周期问题。 + +## 重定向和转发使用 + +在 Spring MVC 中,**重定向(Redirect)**和**转发(Forward)**是两种常见的请求处理方式,它们分别用于不同的场景。Spring 提供了灵活的 API 来实现这两种请求方式。 + +### 1. **转发(Forward)** + +**转发**是指服务器将请求转发到另一个资源(如 JSP 页面或另一个控制器方法),并且请求和响应都不会发生改变。即,URL 不会发生变化,客户端仍然看到原始的 URL。 + +#### 转发的实现 + +在 Spring MVC 中,可以通过以下两种方式实现转发: + +- 使用 `RequestDispatcher.forward()` 方法。 +- 使用 `ModelAndView` 中的 `"forward:"` 前缀来指定转发的路径。 + +##### 示例 1:使用 `RequestDispatcher` 转发 + +```java +@Controller +public class ForwardController { + + @RequestMapping("/forwardExample") + public void forward(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { + // 使用 Servlet 的 RequestDispatcher 进行转发 + request.getRequestDispatcher("/forwardedPage").forward(request, response); + } + + @RequestMapping("/forwardedPage") + public String forwardedPage() { + return "forwardedPage"; // 返回视图 + } +} +``` + +##### 示例 2:使用 `ModelAndView` 转发 + +```java +@Controller +public class ForwardController { + + @RequestMapping("/forwardExample") + public ModelAndView forwardExample() { + // 使用 ModelAndView 进行转发 + return new ModelAndView("forward:/forwardedPage"); + } + + @RequestMapping("/forwardedPage") + public String forwardedPage() { + return "forwardedPage"; // 返回视图 + } +} +``` + +**解释:** + +- `forward:/forwardedPage` 表示将请求转发到 `/forwardedPage`,且客户端的 URL 不会发生变化。 +- 在转发的过程中,控制器方法返回的视图会直接被渲染。 + +#### 转发的特点 + +- **URL 不改变**:用户的浏览器地址栏不会发生变化,仍然显示原始的请求路径。 +- **共享同一个请求**:请求数据(如请求参数)会被转发到目标资源,可以通过 `request` 对象共享。 +- **适用于内部请求**:转发是服务端内部的操作,适用于控制器之间的跳转或者从控制器到视图的跳转。 + +### 2. **重定向(Redirect)** + +**重定向**是指服务器告诉客户端(浏览器)重新发起一个新的请求。重定向会导致浏览器地址栏的 URL 更新为新的地址,因此会导致一次新的 HTTP 请求。 + +#### 重定向的实现 + +在 Spring MVC 中,重定向可以通过以下几种方式实现: + +- 使用 `redirect:` 前缀返回视图。 +- 使用 `HttpServletResponse.sendRedirect()` 方法。 + +##### 示例 1:使用 `redirect:` 前缀 + +```java +@Controller +public class RedirectController { + + @RequestMapping("/redirectExample") + public String redirectExample() { + // 使用 redirect: 前缀进行重定向 + return "redirect:/redirectedPage"; + } + + @RequestMapping("/redirectedPage") + public String redirectedPage() { + return "redirectedPage"; // 返回视图 + } +} +``` + +##### 示例 2:使用 `HttpServletResponse.sendRedirect()` + +```java +@Controller +public class RedirectController { + + @RequestMapping("/redirectExample") + public void redirectExample(HttpServletResponse response) throws IOException { + // 使用 HttpServletResponse.sendRedirect() 进行重定向 + response.sendRedirect("/redirectedPage"); + } + + @RequestMapping("/redirectedPage") + public String redirectedPage() { + return "redirectedPage"; // 返回视图 + } +} +``` + +**解释:** + +- `redirect:/redirectedPage` 会发出一个 HTTP 302 重定向响应,浏览器会发起一次新的请求到 `/redirectedPage`,并更新地址栏的 URL。 +- 使用 `HttpServletResponse.sendRedirect()` 方法也会发送重定向响应,同样会导致浏览器重新发起新的请求。 + +#### 重定向的特点 + +- **URL 改变**:浏览器的地址栏会更新为重定向的目标 URL。 +- **不同的请求**:重定向会产生一次新的 HTTP 请求,原请求的参数不会自动携带到新请求中,除非在重定向 URL 中显式传递。 +- **适用于跨请求跳转**:重定向适合用于不同请求之间的跳转,特别是当需要在多个请求中传递数据时。 + +### 3. **重定向与转发的区别** + +| 特性 | 重定向(Redirect) | 转发(Forward) | +| ------------ | ----------------------------------------------------------- | ---------------------------------------------- | +| **URL 变化** | 浏览器地址栏会更新为新的 URL | 浏览器地址栏不会改变,仍然是原始请求 URL | +| **请求次数** | 会产生两次请求:第一次请求重定向,第二次是新的目标请求 | 只会产生一次请求(内部请求) | +| **数据传递** | 重定向后,数据不能自动传递到新请求中,除非通过 URL 参数传递 | 可以直接传递请求数据,如请求参数和请求属性 | +| **适用场景** | 当需要跨请求或跨控制器传递数据时,或者需要跳转到外部 URL | 当请求处理完成后,内部跳转到另一个视图或资源时 | +| **性能** | 因为需要进行两次请求,所以相比转发略有性能开销 | 因为只需要一次请求,性能开销较小 | + +### 4. **常见使用场景** + +#### (1) **重定向的使用场景** + +- **表单提交后的重定向**:表单数据提交后,为了防止表单重复提交(用户刷新页面时),常常会使用重定向到另一个页面,这种方法叫做 **Post/Redirect/Get (PRG)** 模式。 + + 例如,用户提交了一个订单数据后,页面显示 "订单已提交",并重定向到订单查看页面。 + + ```java + @RequestMapping("/submitOrder") + public String submitOrder(Order order) { + orderService.save(order); + // 提交订单后重定向到订单详情页 + return "redirect:/orderDetails?orderId=" + order.getId(); + } + ``` + +- **外部 URL 重定向**:在用户登录后重定向到外部系统或第三方网站。 + + ```java + @RequestMapping("/redirectToExternalSite") + public String redirectToExternal() { + // 重定向到外部 URL + return "redirect:http://www.example.com"; + } + ``` + +#### (2) **转发的使用场景** + +- **内部资源的转发**:当需要在同一应用程序内部跳转时(如从控制器到视图),或者从一个控制器跳转到另一个控制器时,可以使用转发。 + + 例如,在处理用户请求后,将其转发到 JSP 页面进行显示: + + ```java + @RequestMapping("/showUser") + public String showUserDetails(Model model) { + User user = userService.getUser(); + model.addAttribute("user", user); + return "forward:/userDetails.jsp"; // 内部转发到 userDetails.jsp 页面 + } + ``` + +- **从一个控制器跳转到另一个控制器**:在处理完业务逻辑后,可能需要跳转到另一个控制器来处理某些逻辑。 + + ```java + @RequestMapping("/processOrder") + public String processOrder(Order order) { + orderService.process(order); + return "forward:/orderConfirmation"; // 内部跳转到订单确认页面 + } + ``` + +### 5. **总结** + +- **重定向(Redirect)**:客户端浏览器会发起一次新的请求,地址栏 URL 会发生变化,适用于需要跨请求跳转或外部跳转的场景。 +- **转发(Forward)**:请求在服务器内部被转发,地址栏 URL 不变,适用于同一请求的内部跳转。 + +通过合理使用转发和重定向,你可以灵活地控制请求流转和用户体验。 + +## Thymeleaf快速入门 + +Thymeleaf 是一种现代化的 Java 模板引擎,广泛用于生成 HTML、XML、JavaScript 等内容。它有许多内置的指令和功能,用于渲染动态内容、条件渲染、循环、处理表达式等。以下是 Thymeleaf 中常见的指令和属性的详细介绍: + +### 1. **`th:text`** + +用于替换元素的文本内容。 + +```html + +``` + +- `${message}` 的值会替换 `span` 元素的文本。 + +> 如果需要格式化日期,需要注意,使用`temporals`进行操作 +> +> ```html +> +> ``` + +### 2. **`th:utext`** + +用于替换元素的文本内容,并允许处理 HTML 标签(不会转义 HTML)。 + +```html + +``` + +- `${htmlContent}` 的内容直接插入,并解析其中的 HTML。 + +### 3. **`th:value`** + +设置表单元素的 `value` 属性,通常用于输入框或选择框。 + +```html + +``` + +- 将 `${user.name}` 的值赋给该输入框的 `value` 属性。 + +### 4. **`th:each`** + +用于循环遍历集合或数组。 + +```html + +``` + +- 遍历 `people` 集合,输出每个 `person.name`。 + +### 5. **`th:if`** + +用于条件渲染,只有满足条件时才渲染元素。 + +```html +
+

Welcome, admin!

+
+``` + +- 如果 `user.isAdmin` 为 `true`,渲染该 `div`。 + +### 6. **`th:unless`** + +与 `th:if` 相反,只有条件为 `false` 时才渲染元素。 + +```html +
+

You are not an admin!

+
+``` + +- 如果 `user.isAdmin` 为 `false`,渲染该 `div`。 + +### 7. **`th:attr`** + +用于设置元素的多个属性。 + +```html + +``` + +- 设置 `img` 元素的 `src` 和 `alt` 属性。 + +### 8. **`th:src` / `th:href`** + +用于动态设置 `src` 或 `href` 属性。 + +```html +Image +Click Here +``` + +- `th:src` 用于设置图片的 `src` 属性,`th:href` 用于设置链接的 `href` 属性。 + +### 9. **`th:class`** + +动态设置 `class` 属性,支持条件表达式。 + +```html +
...
+``` + +- 如果 `isActive` 为 `true`,设置 `class="active"`,否则为 `inactive`。 + +### 10. **`th:classappend` / `th:classprepend`** + +分别在现有的 `class` 属性上追加或前置新类。 + +```html +
...
+
...
+``` + +- `th:classappend` 会将新的类追加到现有类的后面。 +- `th:classprepend` 会将新的类添加到现有类的前面。 + +### 11. **`th:id`** + +设置元素的 `id` 属性。 + +```html + +``` + +- 设置 `input` 元素的 `id` 为 `${elementId}` 的值。 + +### 12. **`th:action`** + +设置表单的 `action` 属性。 + +```html +
+ +
+``` + +- 设置表单的 `action` 为 `/submitForm`。 + +### 13. **`th:style`** + +设置元素的 `style` 属性。 + +```html +
+``` + +- 动态设置 `style` 属性,`${color}` 的值会成为 `color` 样式的值。 + +### 14. **`th:fragment`** + +定义一个可重用的片段,通常在模板中调用。 + +```html +
+

Welcome,

+
+``` + +- 定义一个 `userFragment` 片段,可以在其他模板中引用。 + +### 15. **`th:replace`** + +替换当前元素,并将一个片段或其他模板插入其中。 + +```html +
+``` + +- `th:replace` 会将 `userFragment` 片段的内容插入到当前 `div` 中。 + +### 16. **`th:include`** + +将另一个模板的内容插入当前模板中,但不会替换当前元素。 + +```html +
+``` + +- 插入 `userFragment` 的内容,但保留当前 `div` 元素。 + +### 17. **`th:with`** + +局部变量声明,用于在模板中定义临时变量。 + +```html +
+

+
+``` + +- `th:with` 用于在当前元素的上下文中定义变量,类似于局部变量。 + +### 18. **`th:block`** + +在模板中定义一个不会渲染任何 HTML 标签的块元素。用于组合多个元素。 + +```html + +

+
+``` + +- `th:block` 不会渲染任何标签,但可以用来包装多个元素进行条件判断或循环。 + +### 19. **`th:switch` / `th:case`** + +类似于 Java 中的 `switch` 语句,用于条件选择。 + +```html +
+ Active + Inactive + Unknown +
+``` + +- 根据 `${status}` 的值,渲染对应的 `span` 元素。 + +### 20. **`th:object`** + +用来为表单元素绑定一个对象。 + +```html +
+ + + +
+``` + +- `th:object` 绑定整个表单到 `user` 对象。 +- `th:field` 用于绑定每个表单字段到对象的属性。 + +### 21. **`th:href` / `th:src`** + +用于动态设置 URL 值。 + +```html +Profile + +``` + +- 动态生成 URL,支持路径变量的替换。 + +### 22. **`th:placeholder`** + +设置表单输入框的 `placeholder` 属性。 + +```html + +``` + +- 设置 `input` 的 `placeholder` 为 `${placeholderText}` 的值。 + +------ + +### 总结 + +Thymeleaf 提供了许多强大的指令来处理模板中的动态内容、条件渲染、迭代和属性绑定。常见的指令包括: + +- `th:text`、`th:utext`:用于设置文本内容。 +- `th:each`:用于循环遍历。 +- `th:if`、`th:unless`:用于条件判断。 +- `th:attr`、`th:id`、`th:class`:用于设置 HTML 属性。 +- `th:replace`、`th:include`:用于片段包含。 +- `th:switch`、`th:case`:用于类似 `switch` 的条件语句。 \ No newline at end of file diff --git a/mvc/images/SpringMVC笔记/image-20250122171536808.png b/README/images/SpringMVC笔记/image-20250122171536808.png similarity index 100% rename from mvc/images/SpringMVC笔记/image-20250122171536808.png rename to README/images/SpringMVC笔记/image-20250122171536808.png diff --git a/mvc/SpringMVC笔记.md b/mvc/SpringMVC笔记.md deleted file mode 100644 index 7a1926d..0000000 --- a/mvc/SpringMVC笔记.md +++ /dev/null @@ -1,433 +0,0 @@ -# `SpringMVC`笔记 - -- 公共AI网站:https://chatgptplus.cn/ -- vue3官网:https://cn.vuejs.org/ -- SpringBoot官网:https://docs.spring.io/spring-boot/index.html - -## 访问控制 - -请求地址时返回对应的网页文件 - -- `@RestController`用于返回对象格式的内容,在后面会使用`ModelAndView`可以返回网页文件 -- `@Controller`用于返回网页文件 - -### 环境要求 - -```xml - - org.springframework.boot - spring-boot-starter-web - - - org.springframework.boot - spring-boot-starter-test - test - - - - org.springframework.boot - spring-boot-starter-thymeleaf - - - - org.springframework.boot - spring-boot-devtools - runtime - true - - - - org.projectlombok - lombok - 1.18.36 - -``` - -### 错误页面设置 - -在这个路径下可以配置错误码要访问的页面,也就是可以自定义页面内容 - -![image-20250122171536808](./images/SpringMVC笔记/image-20250122171536808.png) - -### 使用`@Controller` - -返回需要访问的HTML内容页面,最后返回的字符串就是页面,这个页面位于`templates`目录下 - -```java -@RequestMapping("/use") -@Controller -public class UseController { - - // 带参数访问 - @RequestMapping(value = "hello", method = RequestMethod.GET, params = {"name"}) - public String hello() { - return "hello"; - } - - @GetMapping("jumpPage") - public String jumpPage() { - return "jumpPage"; - } - - @GetMapping("index") - public String quick() { - return "user"; - } - - // 跳转的页面 - @GetMapping("toJump") - public String toJump() { - return "redirect:jumpPage"; - } -} -``` - -如果在使用`@Controller`需要返回JSON内容,需要在控制器方法上加上`@ResponseBody` - -```java -@GetMapping("getJson") -@ResponseBody -public List getJson() { - ArrayList list = new ArrayList<>(); - list.add("a"); - list.add("b"); - list.add("c"); - - return list; -} -``` - -#### 将视图和模型拆开 - -```java -// 将视图和模型拆开 -@GetMapping("page/test3") -public String test3(Model model) { - model.addAttribute("test3", "测试3"); - return "page/test3"; -} -``` - -### 使用`@RestController` - -#### 使用方式1 - -如果使用`@RestController`那么返回的就是JSON对象,但是这时候要想返回网页文件,需要使用`ModelAndView` - -```java -@RequestMapping("userRest") -@RestController -public class UseRestController { - - @GetMapping("page/test") - public ModelAndView test() { - ModelAndView modelAndView = new ModelAndView(); - modelAndView.setViewName("page/test"); - modelAndView.addObject("message", "这是消息内容"); - return modelAndView; - } -} -``` - -我们引入了`thymeleaf`所以有以下内容`

` - -```html - - - - - 使用RestController返回页面信息 - - -

使用RestController返回页面信息

-

- - -``` - -> 其中`modelAndView.addObject("message", "这是消息内容");`是可选的 - -#### 使用方式2 - -在控制器方法上使用`ModelAndView` - -```java -@GetMapping("page/test2") -public ModelAndView test2(ModelAndView modelAndView) { - modelAndView.addObject("hello", "你好"); - modelAndView.setViewName("page/test2"); - return modelAndView; -} -``` - -## Thymeleaf快速入门 - -Thymeleaf 是一种现代化的 Java 模板引擎,广泛用于生成 HTML、XML、JavaScript 等内容。它有许多内置的指令和功能,用于渲染动态内容、条件渲染、循环、处理表达式等。以下是 Thymeleaf 中常见的指令和属性的详细介绍: - -### 1. **`th:text`** - -用于替换元素的文本内容。 - -```html - -``` - -- `${message}` 的值会替换 `span` 元素的文本。 - -> 如果需要格式化日期,需要注意,使用`temporals`进行操作 -> -> ```html -> -> ``` - -### 2. **`th:utext`** - -用于替换元素的文本内容,并允许处理 HTML 标签(不会转义 HTML)。 - -```html - -``` - -- `${htmlContent}` 的内容直接插入,并解析其中的 HTML。 - -### 3. **`th:value`** - -设置表单元素的 `value` 属性,通常用于输入框或选择框。 - -```html - -``` - -- 将 `${user.name}` 的值赋给该输入框的 `value` 属性。 - -### 4. **`th:each`** - -用于循环遍历集合或数组。 - -```html -
    -
  • - -
  • -
-``` - -- 遍历 `people` 集合,输出每个 `person.name`。 - -### 5. **`th:if`** - -用于条件渲染,只有满足条件时才渲染元素。 - -```html -
-

Welcome, admin!

-
-``` - -- 如果 `user.isAdmin` 为 `true`,渲染该 `div`。 - -### 6. **`th:unless`** - -与 `th:if` 相反,只有条件为 `false` 时才渲染元素。 - -```html -
-

You are not an admin!

-
-``` - -- 如果 `user.isAdmin` 为 `false`,渲染该 `div`。 - -### 7. **`th:attr`** - -用于设置元素的多个属性。 - -```html - -``` - -- 设置 `img` 元素的 `src` 和 `alt` 属性。 - -### 8. **`th:src` / `th:href`** - -用于动态设置 `src` 或 `href` 属性。 - -```html -Image -Click Here -``` - -- `th:src` 用于设置图片的 `src` 属性,`th:href` 用于设置链接的 `href` 属性。 - -### 9. **`th:class`** - -动态设置 `class` 属性,支持条件表达式。 - -```html -
...
-``` - -- 如果 `isActive` 为 `true`,设置 `class="active"`,否则为 `inactive`。 - -### 10. **`th:classappend` / `th:classprepend`** - -分别在现有的 `class` 属性上追加或前置新类。 - -```html -
...
-
...
-``` - -- `th:classappend` 会将新的类追加到现有类的后面。 -- `th:classprepend` 会将新的类添加到现有类的前面。 - -### 11. **`th:id`** - -设置元素的 `id` 属性。 - -```html - -``` - -- 设置 `input` 元素的 `id` 为 `${elementId}` 的值。 - -### 12. **`th:action`** - -设置表单的 `action` 属性。 - -```html -
- -
-``` - -- 设置表单的 `action` 为 `/submitForm`。 - -### 13. **`th:style`** - -设置元素的 `style` 属性。 - -```html -
-``` - -- 动态设置 `style` 属性,`${color}` 的值会成为 `color` 样式的值。 - -### 14. **`th:fragment`** - -定义一个可重用的片段,通常在模板中调用。 - -```html -
-

Welcome,

-
-``` - -- 定义一个 `userFragment` 片段,可以在其他模板中引用。 - -### 15. **`th:replace`** - -替换当前元素,并将一个片段或其他模板插入其中。 - -```html -
-``` - -- `th:replace` 会将 `userFragment` 片段的内容插入到当前 `div` 中。 - -### 16. **`th:include`** - -将另一个模板的内容插入当前模板中,但不会替换当前元素。 - -```html -
-``` - -- 插入 `userFragment` 的内容,但保留当前 `div` 元素。 - -### 17. **`th:with`** - -局部变量声明,用于在模板中定义临时变量。 - -```html -
-

-
-``` - -- `th:with` 用于在当前元素的上下文中定义变量,类似于局部变量。 - -### 18. **`th:block`** - -在模板中定义一个不会渲染任何 HTML 标签的块元素。用于组合多个元素。 - -```html - -

-
-``` - -- `th:block` 不会渲染任何标签,但可以用来包装多个元素进行条件判断或循环。 - -### 19. **`th:switch` / `th:case`** - -类似于 Java 中的 `switch` 语句,用于条件选择。 - -```html -
- Active - Inactive - Unknown -
-``` - -- 根据 `${status}` 的值,渲染对应的 `span` 元素。 - -### 20. **`th:object`** - -用来为表单元素绑定一个对象。 - -```html -
- - - -
-``` - -- `th:object` 绑定整个表单到 `user` 对象。 -- `th:field` 用于绑定每个表单字段到对象的属性。 - -### 21. **`th:href` / `th:src`** - -用于动态设置 URL 值。 - -```html -Profile - -``` - -- 动态生成 URL,支持路径变量的替换。 - -### 22. **`th:placeholder`** - -设置表单输入框的 `placeholder` 属性。 - -```html - -``` - -- 设置 `input` 的 `placeholder` 为 `${placeholderText}` 的值。 - ------- - -### 总结 - -Thymeleaf 提供了许多强大的指令来处理模板中的动态内容、条件渲染、迭代和属性绑定。常见的指令包括: - -- `th:text`、`th:utext`:用于设置文本内容。 -- `th:each`:用于循环遍历。 -- `th:if`、`th:unless`:用于条件判断。 -- `th:attr`、`th:id`、`th:class`:用于设置 HTML 属性。 -- `th:replace`、`th:include`:用于片段包含。 -- `th:switch`、`th:case`:用于类似 `switch` 的条件语句。 \ No newline at end of file diff --git a/mvc/src/main/java/cn/bunny/mvc/controller/ThymeleafController.java b/mvc/src/main/java/cn/bunny/mvc/controller/ThymeleafController.java index 817742c..fedbe37 100644 --- a/mvc/src/main/java/cn/bunny/mvc/controller/ThymeleafController.java +++ b/mvc/src/main/java/cn/bunny/mvc/controller/ThymeleafController.java @@ -9,6 +9,7 @@ import io.swagger.v3.oas.annotations.Operation; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.util.StringUtils; +import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; @@ -39,4 +40,16 @@ public class ThymeleafController { return "thymeleaf/bill"; } + + // 不能包含age,header必须匹配header + @GetMapping(value = "href-test", params = {"!age"}, headers = {"host=localhost:8080"}) + public String hrefTest() { + return "thymeleaf/href-test"; + } + + // ?表示可以任意字符 + @RequestMapping("a?e") + public String test() { + return "test/test"; + } } diff --git a/mvc/src/main/resources/templates/thymeleaf/href-test.html b/mvc/src/main/resources/templates/thymeleaf/href-test.html new file mode 100644 index 0000000..84b754e --- /dev/null +++ b/mvc/src/main/resources/templates/thymeleaf/href-test.html @@ -0,0 +1,30 @@ + + + + + + + 标签跳转测试 + + +

+ 链接自动转成?格式 +

+

+ 请求跳转链接包含age,包含age错误码:400 +

+

+ 匹配ant路径 +

+

Danger link

+

Warning link

+

Info link

+

Light link

+

Dark link

+

Emphasis link

+ + \ No newline at end of file