Fork me on GitHub

JackLin的博客

当前位置:首页 > 标签

SpringBoot 14 Spring 2 SpringMVC 3 MyBatis 2 Linux 4 阿里云 13 宝塔 1 Docker 3 ElasticSearch 2 Redis 4 Shiro 0 Dubbo 0 Swagger 0 Thymeleaf 6 数据库 11 MySQL 11 外键 2 Gradle 1 Test 0 Tomcat 1 JavaWeb 7 Ajax 1 注解 3 css 2 报错 3 多数据源 1 Java基础 1 源码 2 Servlet 1 JSP 1 环境搭建 8 RabbitMQ 1 七牛云 1 Edit.md 1 图像识别 4 英语 2 Zookeeper 1

(转载)接近8000字的Spring/SpringBoot常用注解总结!安排!

  • 2020-05-18
  • 143
  • SpringBoot
非原创 > 原文作者:SnailClimb 本文转载自:https://www.imooc.com/article/304149 ## 前言 大家好,我是 Guide 哥!这是我的 221 篇优质原创文章。如需转载,请在文首注明地址,蟹蟹! 本文已经收录进我的 75K Star 的 Java 开源项目 JavaGuide:https://github.com/Snailclimb/JavaGuide 可以毫不夸张地说,这篇文章介绍的 Spring/SpringBoot 常用注解基本已经涵盖你工作中遇到的大部分常用的场景。对于每一个注解我都说了具体用法,掌握搞懂,使用 SpringBoot 来开发项目基本没啥大问题了! 整个目录如下,内容有点多: ![](http://img3.sycdn.imooc.com/5eaa987f0001279810802201.jpg) 为什么要写这篇文章? 最近看到网上有一篇关于 SpringBoot 常用注解的文章被转载的比较多,我看了文章内容之后属实觉得质量有点低,并且有点会误导没有太多实际使用经验的人(这些人又占据了大多数)。所以,自己索性花了大概 两天时间简单总结一下了。 因为我个人的能力和精力有限,如果有任何不对或者需要完善的地方,请帮忙指出!Guide 哥感激不尽! ## 1.`@ SpringBootApplication` 这里先单独拎出 `@ SpringBootApplication` 注解说一下,虽然我们一般不会主动去使用它。 Guide 哥:这个注解是 Spring Boot 项目的基石,创建 SpringBoot 项目之后会默认在主类加上。 ``` @SpringBootApplication public class SpringSecurityJwtGuideApplication { public static void main(java.lang.String[] args) { SpringApplication.run(SpringSecurityJwtGuideApplication.class, args); } } ``` 我们可以把 `@ SpringBootApplication`看作是 `@ Configuration`、`@ EnableAutoConfiguration`、`@ ComponentScan` 注解的集合。 ``` package org.springframework.boot.autoconfigure; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Inherited @SpringBootConfiguration @EnableAutoConfiguration @ComponentScan(excludeFilters = { @Filter(type = FilterType.CUSTOM, classes = TypeExcludeFilter.class), @Filter(type = FilterType.CUSTOM, classes = AutoConfigurationExcludeFilter.class) }) public @interface SpringBootApplication { ...... } package org.springframework.boot; @Target(ElementType.TYPE) @Retention(RetentionPolicy.RUNTIME) @Documented @Configuration public @interface SpringBootConfiguration { } ``` 根据 SpringBoot 官网,这三个注解的作用分别是: - `@ EnableAutoConfiguration`:启用 SpringBoot 的自动配置机制 - `@ ComponentScan`: 扫描被`@ Component` (`@ Service`,`@ Controller`)注解的 bean,注解默认会扫描该类所在的包下所有的类。 - `@ Configuration`:允许在 Spring 上下文中注册额外的 bean 或导入其他配置类 ## 2. Spring Bean 相关 ### 2.1. `@ Autowired` 自动导入对象到类中,被注入进的类同样要被 Spring 容器管理比如:Service 类注入到 Controller 类中。 ``` @Service public class UserService { ...... } @RestController @RequestMapping("/users") public class UserController { @Autowired private UserService userService; ...... } ``` ### 2.2. `Component`,`@ Repository`,`@ Service`, `@ Controller` 我们一般使用 `@ Autowired` 注解让 Spring 容器帮我们自动装配 bean。要想把类标识成可用于 `@ Autowired` 注解自动装配的 bean 的类,可以采用以下注解实现: - `@ Component` :通用的注解,可标注任意类为 Spring 组件。如果一个 Bean 不知道属于哪个层,可以使用@Component 注解标注。 - `@ Repository` : 对应持久层即 Dao 层,主要用于数据库相关操作。 - `@ Service` : 对应服务层,主要涉及一些复杂的逻辑,需要用到 Dao 层。 - `@ Controller` : 对应 Spring MVC 控制层,主要用户接受用户请求并调用 Service 层返回数据给前端页面。 ### 2.3. `@ RestController` `@ RestController`注解是`@ Controller`和`@ ResponseBody`的合集,表示这是个控制器 bean,并且是将函数的返回值直 接填入 HTTP 响应体中,是 REST 风格的控制器。 Guide 哥:现在都是前后端分离,说实话我已经很久没有用过`@ Controller`。如果你的项目太老了的话,就当我没说。 单独使用 `@ Controller` 不加 `@ ResponseBody`的话一般使用在要返回一个视图的情况,这种情况属于比较传统的 Spring MVC 的应用,对应于前后端不分离的情况。`@ Controller` +`@ ResponseBody` 返回 JSON 或 XML 形式数据 关于`@ RestController` 和 `@ Controller`的对比,请看这篇文章: [@ RestController vs @ Controller](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247485544&idx=1&sn=3cc95b88979e28fe3bfe539eb421c6d8&chksm=cea247a3f9d5ceb5e324ff4b8697adc3e828ecf71a3468445e70221cce768d1e722085359907&token=1725092312&lang=zh_CN#rd "66") ### 2.4. `@ Scope` 声明 Spring Bean 的作用域,使用方法: ``` @Bean @Scope("singleton") public Person personSingleton() { return new Person(); } ``` 四种常见的 Spring Bean 的作用域: - singleton : 唯一 bean 实例,Spring 中的 bean 默认都是单例的。 prototype : 每次请求都会创建一个新的 bean 实例。 - request : 每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP request 内有效。 - session : 每一次 HTTP 请求都会产生一个新的 bean,该 bean 仅在当前 HTTP session 内有效。 ### 2.5. `Configuration` 一般用来声明配置类,可以使用 @Component注解替代,不过使用Configuration注解声明配置类更加语义化。 ``` @Configuration public class AppConfig { @Bean public TransferService transferService() { return new TransferServiceImpl(); } } ``` ## 3. 处理常见的 HTTP 请求类型 **5 种常见的请求类型: ** - GET :请求从服务器获取特定资源。举个例子:GET /users(获取所有学生) - POST :在服务器上创建一个新的资源。举个例子:POST /users(创建学生) - PUT :更新服务器上的资源(客户端提供更新后的整个资源)。举个例子:PUT /users/12(更新编号为 12 的学生) - DELETE :从服务器删除特定的资源。举个例子:DELETE /users/12(删除编号为 12 的学生) PATCH :更新服务器上的资源(客户端提供更改的属性,可以看做作是部分更新),使用的比较少,这里就不举例子了。 ### 3.1. GET 请求 @ GetMapping("users") 等价于@ RequestMapping(value="/users",method=RequestMethod.GET) ``` @GetMapping("/users") public ResponseEntity<List<User>> getAllUsers() { return userRepository.findAll(); } ``` ### 3.2. POST 请求 @ PostMapping("users") 等价于@ RequestMapping(value="/users",method=RequestMethod.POST) 关于`@ RequestBody`注解的使用,在下面的“前后端传值”这块会讲到。 ``` @PostMapping("/users") public ResponseEntity<User> createUser(@Valid @RequestBody UserCreateRequest userCreateRequest) { return userRespository.save(user); } ``` ### 3.3. PUT 请求 @ PutMapping("/users/{userId}") 等价于@ RequestMapping(value="/users/{userId}",method=RequestMethod.PUT) ``` @PutMapping("/users/{userId}") public ResponseEntity<User> updateUser(@PathVariable(value = "userId") Long userId, @Valid @RequestBody UserUpdateRequest userUpdateRequest) { ...... } ``` ### 3.4. DELETE 请求 @ DeleteMapping("/users/{userId}")等价于@ RequestMapping(value="/users/{userId}",method=RequestMethod.DELETE) ``` @DeleteMapping("/users/{userId}") public ResponseEntity deleteUser(@PathVariable(value = "userId") Long userId){ ...... } ``` ### 3.5. PATCH 请求 一般实际项目中,我们都是 PUT 不够用了之后才用 PATCH 请求去更新数据。 ``` @PatchMapping("/profile") public ResponseEntity updateStudent(@RequestBody StudentUpdateRequest studentUpdateRequest) { studentRepository.updateDetail(studentUpdateRequest); return ResponseEntity.ok().build(); } ``` ## 4. 前后端传值 **掌握前后端传值的正确姿势,是你开始 CRUD 的第一步! ** ### 4.1. `@ PathVariable` 和 `@ RequestParam` `@ PathVariable`用于获取路径参数,`@ RequestParam`用于获取查询参数。 举个简单的例子: ``` @GetMapping("/klasses/{klassId}/teachers") public List<Teacher> getKlassRelatedTeachers( @PathVariable("klassId") Long klassId, @RequestParam(value = "type", required = false) String type ) { ... } ``` 如果我们请求的 url 是:`/klasses/{123456}/teachers?type=web` 那么我们服务获取到的数据就是:`klassId=123456,type=web`。 ### 4.2. @RequestBody 用于读取 Request 请求(可能是 POST,PUT,DELETE,GET 请求)的 body 部分并且Content-Type 为 application/json 格式的数据,接收到数据之后会自动将数据绑定到 Java 对象上去。系统会使用HttpMessageConverter或者自定义的HttpMessageConverter将请求的 body 中的 json 字符串转换为 java 对象。 我用一个简单的例子来给演示一下基本使用! 我们有一个注册的接口: ``` @PostMapping("/sign-up") public ResponseEntity signUp(@RequestBody @Valid UserRegisterRequest userRegisterRequest) { userService.save(userRegisterRequest); return ResponseEntity.ok().build(); } ``` `UserRegisterRequest`对象: ``` @Data @AllArgsConstructor @NoArgsConstructor public class UserRegisterRequest { @NotBlank private String userName; @NotBlank private String password; @FullName @NotBlank private String fullName; } ``` 我们发送 post 请求到这个接口,并且 body 携带 JSON 数据: ``` {"userName":"coder","fullName":"shuangkou","password":"123456"} ``` 这样我们的后端就可以直接把 json 格式的数据映射到我们的 UserRegisterRequest 类上。 ![](http://img1.sycdn.imooc.com/5eaa988200014dbf10780338.jpg) 需要注意的是:一个请求方法只可以有一个`@ RequestBody`,但是可以有多个`@ RequestParam`和`@ PathVariable`。 如果你的方法必须要用两个 `@ RequestBody`来接受数据的话,大概率是你的数据库设计或者系统设计出问题了! ## 5. 读取配置信息 很多时候我们需要将一些常用的配置信息比如阿里云 oss、发送短信、微信认证的相关配置信息等等放到配置文件中。 下面我们来看一下 Spring 为我们提供了哪些方式帮助我们从配置文件中读取这些配置信息。 我们的数据源application.yml内容如下:: ``` wuhan2020: 2020年初武汉爆发了新型冠状病毒,疫情严重,但是,我相信一切都会过去!武汉加油!中国加油! my-profile: name: Guide哥 email: koushuangbwcx@163.com library: location: 湖北武汉加油中国加油 books: - name: 天才基本法 description: 二十二岁的林朝夕在父亲确诊阿尔茨海默病这天,得知自己暗恋多年的校园男神裴之即将出国深造的消息——对方考取的学校,恰是父亲当年为她放弃的那所。 - name: 时间的秩序 description: 为什么我们记得过去,而非未来?时间“流逝”意味着什么?是我们存在于时间之内,还是时间存在于我们之中?卡洛·罗韦利用诗意的文字,邀请我们思考这一亘古难题——时间的本质。 - name: 了不起的我 description: 如何养成一个新习惯?如何让心智变得更成熟?如何拥有高质量的关系? 如何走出人生的艰难时刻? ``` ### 5.1. @value(常用) 使用 `@ Value("${property}")` 读取比较简单的配置信息: ``` @Value("${wuhan2020}") String wuhan2020; ``` ### 5.2. @ ConfigurationProperties(常用) 通过`@ ConfigurationProperties`读取配置信息并与 bean 绑定。 ``` @Component @ConfigurationProperties(prefix = "library") class LibraryProperties { @NotEmpty private String location; private List<Book> books; @Setter @Getter @ToString static class Book { String name; String description; } 省略getter/setter ...... } ``` 你可以像使用普通的 Spring bean 一样,将其注入到类中使用。 ### 5.3. PropertySource(不常用) `@ PropertySource`读取指定 properties 文件 ``` @Component @PropertySource("classpath:website.properties") class WebSite { @Value("${url}") private String url; 省略getter/setter ...... } ``` 更多内容请查看我的这篇文章: [《10 分钟搞定 SpringBoot 如何优雅读取配置文件?》](https://mp.weixin.qq.com/s?__biz=Mzg2OTA0Njk0OA==&mid=2247486181&idx=2&sn=10db0ae64ef501f96a5b0dbc4bd78786&chksm=cea2452ef9d5cc384678e456427328600971180a77e40c13936b19369672ca3e342c26e92b50&token=816772476&lang=zh_CN#rd) ## 6. 参数校验 .... 待完善(后面的不常用)这里就不继续写了,可以看看原文。

RabbitMQ的安装(Docker),基本介绍以及整合SpringBoot

  • 2020-05-05
  • 140
  • SpringBoot
## RabbitMQ的安装,通过Docker安装 #####(1)去到 Docker Hub官网,搜索 RabbitMQ 尽量安装带 management 的,我这里安装的是 3.8 版本的 ![](/upload/2020-5-5 134149.png) 执行安装 `docker pull rabbitmq:3.8-management` ![](/upload/2020-4-30 103308.png) #####(2)安装完成之后启动 RabbitMQ 启动 RabbitMQ `docker run -d -p 5672:5672 -p 15672:15672 --name myrabbitmq fa535c4b51fe` - -d 表示后台启动 - -p 表示端口映射 - 5672 是 RabbitMQ 的默认端口,我们需要使用这个端口连接上 RabbitMQ - 15672 是 RabbitMQ 的UI界面的端口,我们连接之后可以进行可视化操作 - --name 表示为我们的容器起一个名字 - 后面的一串字符是我们的镜像 ID,这里要换成自己的镜像ID,通过 `docker iamges` 命令可以查看 emm~~~ 忘记截图了 #####(3)连接图形化界面 输入地址加15672端口号,进入图形界面 - 记得开放阿里云安全组 - 开放15672端口和5672端口 ---》 `firewall-cmd --add-port=6379/tcp` ## RabbitMQ 的基本介绍 #### 1. 消息服务 (1)JMS(Java Message Service) Java消息服务 - 基于JVM消息代理的规范,ActiveMQ,HornetMQ是JMS的实现 (2)AMQP(Advanced Message Queuing Protocol) - 高级消息队列协议,也是一个消息代理的规范,兼容JMS - RabbitMQ是AMQP的实现 #### 2. RabbitMQ 简介 **RabbitMQ是一个由erlang开发的AMQP(Advanved Message Queue Protocol)的开源实现。** ##### 核心概念 (1)Message 消息,有消息头和消息体组成 (2)Publisher 消息的生产者,也是一个向交换器发布消息的客户端应用程序。 (3)Exchange 交换器,用来接收生产者发送的消息并将这些消息路由给服务器中的队列。 (4)Queue 消息队列,用来保存消息直到发送给消费者。一个消息可投入一个或多个队列。消息一直在队列里面,等待消费者连接到这个队列将其取走。 (5)Binding 绑定,用于消息队列和交换器之间的关联。一个绑定就是基于路由键将交换器和消息队列连接起来的路由规则,所以可以将交换器理解成一个由绑定构成的路由表。 (6)Connection 网络连接,比如一个TCP连接。 (7)Channel 信道,多路复用连接中的一条独立的双向数据流通道。 (8)Consumer 消息的消费者,表示一个从消息队列中取得消息的客户端应用程序。 (9)Virtual Host 虚拟主机,表示一批交换器、消息队列和相关对象。 (10)Broker 表示消息队列服务器实体 **关于这些概念的基本示意图: ** ![](/upload/消息队列图示.png) ## SpringBoot整合RabbitMQ #### 1. 创建工程,导入对应的依赖 如果是新的工程,在初始化时选择 Spring for RabbitMQ ![](/upload/2020-5-5 141337.png) 如果是旧的工程,这添加依赖 可以在 SpringBoot 的官方文档中查找 ``` <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-amqp</artifactId> </dependency> ``` #### 2. 分析自动配置类,进行相关的配置 (1)找到 RabbitAutoConfiguration 类,分析一下 > rabbitConnectionFactory 方法,通过 RabbitProperties 里面的配置,来创建一个 RabbitMQ 的连接工厂。 所以我们可以在 application.yaml 中进行如下配置 ``` spring: rabbitmq: host: 39.98.86.223 username: guest password: guest ``` > 在 RabbitAutoConfiguration 还配置了一个 RabbitTemplate 放入容器中,我们可以通过 RabbitTemplate 来给 RabbitMQ 发送和接收消息。和之前使用的 RedisTemplate,JDBCTemplate 类似 > 还往容器中放置了一个 AmqpAdmin,用来创建和删除 Queue,Exchange,Binding

SpringBoot中JS代码获取Model中的值

  • 2020-05-01
  • 133
  • SpringBoot
今天总结一下如何在前端的HTML页面中利用JS代码获取Model中的值,主要有2种方法。 ### 利用隐藏域 我们可以先利用Thymeleaf模版引擎将Model里面的值放在一个div标签中,然后设置display属性为hidden ### 直接在JS中利用表达式

JSP运行原理分析

  • 2020-04-24
  • 131
  • JavaWeb
JSP的全名是 Java Server Page,它可以在传统的的HTML文件中插入Java程序段。 正是由于这个特性,在早期的JavaWeb开发中,JSP几乎是神一样的存在。 今天我们就来分析一下JSP的运行原理,以及JSP到底是什么 ### JSP是什么 先抛出结论:JSP就是一个Servlet 需要解释这个的从JSP的工作原理开发分析 ### JSP工作原理 #### 将JSP翻译成java文件 当我们在IDE中创建一个JSP文件之后第一次被访问时,Web容器就会将我们写的JSP文件翻译成为一个 .java 文件以及对应的 .class 文件 如下图所示,我在 web 目录下面创建了一个 test.jsp 文件 ![](/upload/temp.png) 第一次访问之后,我们可以看到在 `C:\Users\用户名\.IntelliJIdea2019.3\system\tomcat\工程名称\work\Catalina\localhost\工程名册\org\apache\jsp` 里面,生成了下面 2 个文件: ![](/upload/2020-4-24 213417.png) 这 2 个文件就是翻译而成的java文件。 #### 翻译成的Java文件就是Servlet **要想找到这几个类,需要导入tomcat里面的jar包 ** 点进这个生成的Java文件里面,这里类开头是这样的 ![](/upload/_202004242212101SS.png) 再来看看 HttpJspBase,重要发现这个类就是继承了 HTTPServlet ![](/upload/2020-4-24 221830.png) **而我们生成的 java 类,继承了 HttpJspBase,所以说生成的 Java 类就是Servlet** #### 生成的Java类干了什么 在这个生成的 Java 类里面,主要干了2件事 - 将 jsp 页面的各种html标签通过流的方式输出 ![](/upload/_2020042422533353SS.png) - 定义了一些内置对象,这些对象我们就可以在jsp页面中使用 ![](/upload/2020-4-24 223535.png)

Servlet3.0注解分析

  • 2020-04-22
  • 132
  • JavaWeb
在Servlet3.0出来之后,推出了几个常用的注解来帮助我们简化 web.xml 中的配置 今天我们就来总结一下这些注解的基本使用,以及和 web.xml 原来配置的对应关系。 ### 都有哪些注解 要想系统的了解都有哪些注解,最好的方法就是看源码呗。 我们知道,注解时使用在Servlet类上面的,那么我们就直接点进我们导入与Servlet相关的 jar 包里面看一看。 ![](/upload/annotation4.png) 找到 javax.servlet-api-4.0.1.jar 这个 jar 包,发现里面就有我们需要找的注解,下面我们来一一分析一下。 ### @ WebServlet - 这个注解用来标注一个 Servlet,该类需继承自 HttpServlet - 和我们在 web.xml 中配置的 <servlet> 功能一样 > Java代码 ``` @WebServlet(name = "test", urlPatterns = {"/user"}) public class UserServlet extends HttpServlet { // 逻辑代码 } ``` > web.xml配置 ``` <servlet> <servlet-name>test</servlet-name> <servlet-class>com.linkai.servlet.UserServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>test</servlet-name> <url-pattern>/user</url-pattern> </servlet-mapping> ``` ### @ WebFilter - 用来配置过滤器的注解 ### @ WebListener - 用来配置监听器的注解 ### @ WebInitParam - 用来给Servlet传递参数的注解 在 @ WebServlet 中,我们看到这样一个方法 ``` WebInitParam [] initParams() default {}; ``` 显然@ WebInitParam应该是给Servlet初始化传递参数的,而且这个参数要放在@ WebServlet里面 未完,待续。。。666

SpringBoot中Controller返回路径问题

  • 2020-04-15
  • 138
  • SpringBoot
### SpringBoot是如何根据路径寻址文件的 要想知道Controller中的返回路径究竟如何填写,我们就必须知道SpringBoot是如何解析我们写的路径的。 关于这部分内容,我们得首先从SpringMVC开始说起。 #### (1)SpringMVC中路径问题 熟悉SpringMVC的小伙伴都知道SpringMVC的基本访问流程,如何不熟悉的话可以参考我的这篇博客:xxx #### (2)SpringBoot中解析路径 和SpringMVC类似,我们也是需要找到他的视图解析器相关的配置文件。 `WebMvcAutoConfiguration` `ResourceProperties` 这类配置文件再SpringBoot中一般是xxxProperties.java 的格式;所以我们找到 `ThymeleafProperties.java` 在这个类里面我们可以很清楚的看到如下代码: ``` @ConfigurationProperties(prefix = "spring.thymeleaf") public class ThymeleafProperties { private static final Charset DEFAULT_ENCODING = StandardCharsets.UTF_8; public static final String DEFAULT_PREFIX = "classpath:/templates/"; public static final String DEFAULT_SUFFIX = ".html"; // 剩余代码... } ``` 是不是有种似曾相识的感觉,这里的 prefix = "spring.thymeleaf" 表示我们在 application.yaml 文件中进行相关的配置,需要以 spring.thymeleaf 开头。 接下来有3个常量: - DEFAULT_ENCODING 设置了一下Thymeleaf的默认编码 - DEFAULT_PREFIX 设置了解析时的前缀 - DEFAULT_SUFFIX 设置了解析时的后缀 就是说如果我们在Controller中直接返回一个字符串,例如 return "main"; 那么SpringBoot就会去 templates/static 目录下面去寻找以 main 开头, .html 结尾的文件,也就是去寻找 main.html 文件。 #### (3)关于重定向 如果在Controller中需要重定向,那么在开头尽量添加上 / ,避免出现不必要的麻烦,因为 / 代码根目录。 如果不加的话,而且在Controller类上面又添加l @ ResponeBody 注解,如下所示,可能出现资源找不到情况。 ``` @Controller @RequestMapping("/admin") public class BlogController { @RequestMapping("/addBlog") public String addBlog() { return "redirect:/admin/Blog"; } } ``` - 如果是 `return "redirect:/admin/Blog";` 那么地址栏将会显示 http://localhost/admin/Blog - 如果是 `return "redirect:admin/Blog";` 那么地址栏将会显示 http://localhost/admin/admin/Blog 前面多了一个 amdin,就是因为我们在类上面添加了 @ RequestMapping("/admin") 造成的原因 #### (4)关于部署到服务器上(本地可以运行,服务器上报500错误) ##### 1. 部署到服务器上的时候, return 语句前面不需要加上 /,否则会报错 ``` @RequestMapping("/Blog") public String blog(Model model) { // 逻辑代码 return "main/blog"; } ``` - 如上面代码所示,直接返回 "main/blog" 就行了,如果返回 "/main/blog" 就或报错 ##### 2. 需要严格的遵守Thymeleaf的相关规范 **例如,使用Thymeleaf抽取公共页面时,前面也不能加 / ** ``` <!-- 文章页面:头部导航栏--> <div th:replace="~{common/commonsfile::article-topbar}"></div> ``` - 第一个common前面不能加 /