diff --git a/spring/springmvc-base-annotation/README.md b/spring/springmvc-base-annotation/README.md index a74658d..466479d 100644 --- a/spring/springmvc-base-annotation/README.md +++ b/spring/springmvc-base-annotation/README.md @@ -1,80 +1,68 @@ -# springmvc基础(基于注解) - -## 目录
-一、搭建hello spring工程
-    1.1 项目搭建
-    1.2 相关注解说明
+# Spring MVC 基础(基于注解) + -## 一、搭建hello spring工程 + + +## 一、搭建 Hello Spring 工程 ### 1.1 项目搭建 -1.新建 maven web 工程,并引入相应的依赖 +1.新建 maven web 工程,并引入相应的依赖: ```xml - - 5.1.3.RELEASE - + + 5.1.3.RELEASE + - - org.springframework - spring-context - ${spring-base-version} - - - org.springframework - spring-beans - ${spring-base-version} - - - org.springframework - spring-core - ${spring-base-version} - - - org.springframework - spring-web - ${spring-base-version} - - - org.springframework - spring-webmvc - ${spring-base-version} - - - javax.servlet - javax.servlet-api - 4.0.1 - provided - - + + org.springframework + spring-context + ${spring-base-version} + + + org.springframework + spring-beans + ${spring-base-version} + + + org.springframework + spring-core + ${spring-base-version} + + + org.springframework + spring-web + ${spring-base-version} + + + org.springframework + spring-webmvc + ${spring-base-version} + + + javax.servlet + javax.servlet-api + 4.0.1 + provided + + ``` -2.得益于 servlet3.0 和 spring 的支持,我们可以在没有 web.xml 的情况下完成关于 servlet 配置。 - - 新建 DispatcherServletInitializer.java 文件,这个类的作用相当于我们在 xml 方式下 web.xml 中配置的 DispatcherServlet +2.得益于 servlet3.0 和 Spring 的共同支持,我们可以在没有 `web.xml` 的情况下完成关于 servlet 配置。新建 DispatcherServletInitializer,这个类的作用相当于我们在 xml 方式下 web.xml 中配置的 DispatcherServlet: ```java -package com.heibaiying.config; - -import org.springframework.web.servlet.support.AbstractAnnotationConfigDispatcherServletInitializer; - -/** - * @author : heibaiying - */ - public class DispatcherServletInitializer extends AbstractAnnotationConfigDispatcherServletInitializer { protected Class[] getRootConfigClasses() { @@ -92,30 +80,9 @@ public class DispatcherServletInitializer extends AbstractAnnotationConfigDispat ``` -3.新建 ServletConfig.java,文件内容如下 (这个类相当于我们在 xml 配置方式中的 springApplication.xml) +3.新建 ServletConfig ,文件内容如下 (这个类相当于我们在 xml 配置方式中的 `springApplication.xml` ): ```java -package com.heibaiying.config; - -import com.heibaiying.exception.NoAuthExceptionResolver; -import com.heibaiying.interceptors.MyFirstInterceptor; -import com.heibaiying.interceptors.MySecondInterceptor; -import org.springframework.context.annotation.Bean; -import org.springframework.context.annotation.ComponentScan; -import org.springframework.context.annotation.Configuration; -import org.springframework.web.servlet.HandlerExceptionResolver; -import org.springframework.web.servlet.ViewResolver; -import org.springframework.web.servlet.config.annotation.DefaultServletHandlerConfigurer; -import org.springframework.web.servlet.config.annotation.EnableWebMvc; -import org.springframework.web.servlet.config.annotation.InterceptorRegistry; -import org.springframework.web.servlet.config.annotation.WebMvcConfigurer; -import org.springframework.web.servlet.view.InternalResourceViewResolver; - -import java.util.List; - -/** - * @author : heibaiying - */ @Configuration @EnableWebMvc @ComponentScan(basePackages = {"com.heibaiying.controller"}) @@ -144,20 +111,9 @@ public class ServletConfig implements WebMvcConfigurer { ``` -4.在 src 下新建 controller 用于测试 +4.新建 Controller 用于测试整体配置是否成功: ```java -package com.heibaiying.controller; - -import com.heibaiying.exception.NoAuthException; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; - -/** - * @author : heibaiying - * @description : hello spring - */ - @Controller @RequestMapping("mvc") public class HelloController { @@ -166,12 +122,10 @@ public class HelloController { private String hello() { return "hello"; } - } - ``` -5.在 WEB-INF 下新建 jsp 文件夹,新建 hello.jsp 文件 +5.在 WEB-INF 下新建 jsp 文件夹,并创建一个简单的 hello.jsp 文件: ```jsp <%@ page contentType="text/html;charset=UTF-8" language="java" %> @@ -185,13 +139,13 @@ public class HelloController { ``` -6.启动 tomcat 服务,访问 localhost:8080/mvc/hello +6.启动 tomcat 服务,访问的服务地址为:localhost:8080/mvc/hello ### 1.2 相关注解说明 **1.@Configuration** -@Configuration 用于定义配置类,可替换 xml 配置文件,被注解的类内部包含有一个或多个被@Bean 注解的方法,这些方法将会被 AnnotationConfigApplicationContext 或 AnnotationConfigWebApplicationContext 类进行扫描,并用于构建 bean 定义,初始化 Spring 容器。 +@Configuration 用于配置类的定义,等价于xml 配置文件,配置类内部可以包含一个或多个被 @Bean 注解的方法,这些方法将会被 AnnotationConfigApplicationContext 或 AnnotationConfigWebApplicationContext 类进行扫描,方法返回的实体会被直接注册为容器内的 Bean。 **2.@EnableWebMvc** @@ -201,21 +155,9 @@ public class HelloController { ## 二、配置自定义拦截器 -1.创建自定义拦截器,实现接口 HandlerInterceptor(这里我们创建两个拦截器,用于测试拦截器方法的执行顺序) +1.创建自定义拦截器,实现接口 HandlerInterceptor(这里我们创建两个拦截器,用于测试拦截器的执行顺序): ```java -package com.heibaiying.interceptors; - -import org.springframework.web.servlet.HandlerInterceptor; -import org.springframework.web.servlet.ModelAndView; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -/** - * @author : heibaiying - * @description : spring5 中 preHandle,postHandle,afterCompletion 在接口中被声明为默认方法 - */ public class MyFirstInterceptor implements HandlerInterceptor { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { @@ -235,18 +177,6 @@ public class MyFirstInterceptor implements HandlerInterceptor { ``` ```java -package com.heibaiying.interceptors; - -import org.springframework.web.servlet.HandlerInterceptor; -import org.springframework.web.servlet.ModelAndView; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -/** - * @author : heibaiying - * @description : spring5 中 preHandle,postHandle,afterCompletion 在接口中被声明为默认方法 - */ public class MySecondInterceptor implements HandlerInterceptor { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { @@ -265,16 +195,13 @@ public class MySecondInterceptor implements HandlerInterceptor { ``` -2.在 ServletConfig.java 中注册自定义拦截器 +2.在 ServletConfig 中注册自定义拦截器: ```java - /** - * 添加自定义拦截器 - */ - public void addInterceptors(InterceptorRegistry registry) { - registry.addInterceptor(new MyFirstInterceptor()).addPathPatterns("/mvc/**").excludePathPatterns("mvc/login"); - registry.addInterceptor(new MySecondInterceptor()).addPathPatterns("/mvc/**"); - } +public void addInterceptors(InterceptorRegistry registry) { + registry.addInterceptor(new MyFirstInterceptor()).addPathPatterns("/mvc/**").excludePathPatterns("mvc/login"); + registry.addInterceptor(new MySecondInterceptor()).addPathPatterns("/mvc/**"); +} ``` 3.关于多个拦截器方法执行顺序的说明 @@ -285,15 +212,9 @@ public class MySecondInterceptor implements HandlerInterceptor { ## 三、全局异常处理 -1.定义自定义异常 +1.定义自定义异常: ```java -package com.heibaiying.exception; - -/** - * @author : heibaiying - * @description : 自定义无权限异常 - */ public class NoAuthException extends RuntimeException { public NoAuthException() { @@ -311,26 +232,12 @@ public class NoAuthException extends RuntimeException { public NoAuthException(Throwable cause) { super(cause); } - } - ``` -2.实现自定义异常处理器 +2.实现自定义异常处理器: ```java -package com.heibaiying.exception; - -import org.springframework.web.servlet.HandlerExceptionResolver; -import org.springframework.web.servlet.ModelAndView; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -/** - * @author : heibaiying - * @description : 无权限异常处理机制 - */ public class NoAuthExceptionResolver implements HandlerExceptionResolver { public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { @@ -345,21 +252,17 @@ public class NoAuthExceptionResolver implements HandlerExceptionResolver { return "XMLHttpRequest".equalsIgnoreCase(request.getHeader("X-Requested-With")); } } - ``` -3.在 ServletConfig.java 注册自定义异常处理器 +3.在 ServletConfig 中注册自定义异常处理器: ```java -/** -* 添加全局异常处理器 -*/ public void configureHandlerExceptionResolvers(List resolvers) { resolvers.add(new NoAuthExceptionResolver()); } ``` -4.定义测试 controller,抛出自定义异常 +4.定义测试 controller,抛出自定义异常: ```java @Controller @@ -371,7 +274,6 @@ public class HelloController { return "hello"; } - @RequestMapping("auth") private void auth() { throw new NoAuthException("没有对应的访问权限!"); @@ -379,7 +281,7 @@ public class HelloController { } ``` -注:调用这个 controller 时,同时也可以验证在拦截器部分提到的:如果对应的程序报错,拦截器不一定会进入 postHandle 这个方法 但一定会进入 afterCompletion 这个方法 +调用这个 Controller 时,同时可以验证在拦截器部分提到的:如果对应的程序报错,拦截器不一定会进入 postHandle 这个方法 但一定会进入 afterCompletion 这个方法。 @@ -387,17 +289,9 @@ public class HelloController { ### 4.1 参数绑定 -1.新建 Programmer.java +1.新建 Programmer.java 作为测试实体类: ```java -package com.heibaiying.bean; - -import lombok.Data; - -/** - * @author : heibaiying - * @description : - */ @Data public class Programmer { @@ -409,32 +303,11 @@ public class Programmer { private String birthday; } - ``` -注:@Data 是 lombok 包下的注解,用来生成相应的 set、get 方法,使得类的书写更为简洁。 - -2.新建 ParamBindController.java 文件 +2.新建 ParamBindController.java 文件,跳转到指定视图: ```java -package com.heibaiying.controller; - -import com.heibaiying.bean.Programmer; -import org.springframework.format.annotation.DateTimeFormat; -import org.springframework.format.datetime.DateFormatter; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.WebDataBinder; -import org.springframework.web.bind.annotation.InitBinder; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; - -import java.util.Date; - -/** - * @author : heibaiying - * @description :参数绑定 - */ @Controller public class ParamBindController { @@ -472,10 +345,9 @@ public class ParamBindController { } } - ``` -3.新建 param.jsp 文件 +3.新建 param.jsp 文件,用于测试数据在视图中的绑定情况: ```jsp <%@ page contentType="text/html;charset=UTF-8" language="java" %> @@ -492,42 +364,32 @@ public class ParamBindController { - ``` -4.启动 tomcat,用[postman](https://www.getpostman.com/) 软件发送请求进行测试 +4.启动 tomcat,接着可以使用 [postman](https://www.getpostman.com/) 等接口测试软件发送测试请求。 -### 4.2 关于日期格式转换的三种方法 +### 4.2 日期格式转换 -1.如上实例代码所示,在对应的 controller 中初始化绑定 +Spring 支持使用以下三种方法来对参数中的日期格式进行转换: + +**方法一**:如上面的实例代码所示,在对应的 Controller 中初始化绑定: ```java @InitBinder - protected void initBinder(WebDataBinder binder) { - binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd HH:mm:ss")); - } +protected void initBinder(WebDataBinder binder) { + binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd HH:mm:ss")); +} ``` -2.利用@DateTimeFormat 注解,如果是用实体类去接收参数,则在对应的属性上用@DateTimeFormat 和@JsonFormat 声明 +**方法二**:利用 @DateTimeFormat 注解,如果是用实体类去接收参数,则在对应的实体类的属性上用 @DateTimeFormat 和 @JsonFormat 进行声明: ```java public String param(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date birthday) ``` -3.使用全局的日期格式绑定,新建自定义日期格式转化类,之后在 ServletConfig.java 中进行注册 +**方法三**:使用全局的日期格式绑定,新建自定义日期格式转化类,之后在 `springApplication.xml` 中进行注册,采用这种方式会对全局范围内的日期格式转换生效: ```java -package com.heibaiying.convert; - -import org.springframework.core.convert.converter.Converter; - -import java.text.SimpleDateFormat; -import java.util.Date; - -/** - * @author : heibaiying - * @description : - */ public class CustomDateConverter implements Converter { public Date convert(String s) { @@ -540,15 +402,11 @@ public class CustomDateConverter implements Converter { return null; } } - ``` -ServletConfig.java +在 ServletConfig 中进行注册: ```java -/** - * 添加全局日期处理 - */ public void addFormatters(FormatterRegistry registry) { registry.addConverter(new CustomDateConverter()); } @@ -558,82 +416,41 @@ public void addFormatters(FormatterRegistry registry) { ## 五、数据校验 -1.spring 支持的数据校验是 JSR303 的标准,需要引入依赖的 jar 包 +1.Spring 支持 JSR303 标准的校验,需要引入相关的依赖: -```java - - - org.hibernate.validator - hibernate-validator - 6.0.13.Final - - - javax.validation - validation-api - 2.0.1.Final - +```xml + + + org.hibernate.validator + hibernate-validator + 6.0.13.Final + + + javax.validation + validation-api + 2.0.1.Final + ``` -2.新建测试 ParamValidController.java,主要是在需要校验的参数前加上@Validated,声明参数需要被校验,同时加上 bindingResult 参数,这个参数中包含了校验的结果 +2.新建测试类 ParamValidController.java,在需要校验的参数前加上 @Validated 注解,表明该参数需要被校验。同时在方法声明中加上 bindingResult 参数,可以从这个参数中获取最终校验的结果: ```java -package com.heibaiying.controller; - -import com.heibaiying.bean.Programmer; -import org.hibernate.validator.constraints.Length; -import org.springframework.format.annotation.DateTimeFormat; -import org.springframework.format.datetime.DateFormatter; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.validation.BindingResult; -import org.springframework.validation.ObjectError; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.WebDataBinder; -import org.springframework.web.bind.annotation.InitBinder; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import javax.validation.constraints.Min; -import javax.validation.constraints.NotNull; -import java.util.Date; -import java.util.List; - -/** - * @author : heibaiying - * @description :数据校验 - */ @RestController public class ParamValidController { - @PostMapping("validate") - public void valid(@Validated Programmer programmer, - BindingResult bindingResult) { + public void valid(@Validated Programmer programmer,BindingResult bindingResult) { List allErrors = bindingResult.getAllErrors(); for (ObjectError error : allErrors) { System.out.println(error.getDefaultMessage()); } } - } - ``` -3.在 Programmer.java 的对应属性上加上注解约束 (支持的注解可以在 javax.validation.constraints 包中查看) +3.在 Programmer.java 的对应属性上加上注解约束,用于声明每个参数的校验规则: ```java -package com.heibaiying.bean; - -import lombok.Data; - -import javax.validation.constraints.Min; -import javax.validation.constraints.NotNull; - -/** - * @author : heibaiying - * @description : - */ @Data public class Programmer { @@ -648,19 +465,17 @@ public class Programmer { private String birthday; } - ``` +其他支持的注解可以到 javax.validation.constraints 包下进行查看。 + ## 六、文件上传与下载 #### 6.1 文件上传 -1.在 ServletConfig.java 中进行配置,使之支持文件上传 +1.在 ServletConfig 中进行配置,开启文件上传: ```java -/** -* 配置文件上传 -*/ @Bean public CommonsMultipartResolver multipartResolver(){ CommonsMultipartResolver resolver = new CommonsMultipartResolver(); @@ -671,35 +486,9 @@ public CommonsMultipartResolver multipartResolver(){ } ``` -2.新建测试上传的 FileController.java +2.新建上传测试类: ```java -package com.heibaiying.controller; - -import com.heibaiying.utils.FileUtil; -import org.apache.commons.io.FileUtils; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.multipart.MultipartFile; - -import javax.servlet.http.HttpSession; -import java.io.File; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; - -/** - * @author : heibaiying - * @description : 文件上传 - */ - @Controller public class FileController { @@ -745,22 +534,11 @@ public class FileController { return "success"; } } - ``` -3.其中工具类 FileUtil.java 代码如下 +3.其中工具类 FileUtil.java 代码如下: ```java -package com.heibaiying.utils; - -import org.springframework.web.multipart.MultipartFile; -import java.io.*; - -/** - * @author : heibaiying - * @description : 文件上传工具类 - */ - public class FileUtil { public static String saveFile(MultipartFile file, String path) { @@ -803,7 +581,7 @@ public class FileUtil { } ``` -4.新建用于上传的 jsp 页面,上传文件时表单必须声明 enctype="multipart/form-data" +4.新建用于上传的 jsp 页面,上传文件时表单必须声明 `enctype="multipart/form-data"` : ```jsp <%@ page contentType="text/html;charset=UTF-8" language="java" %> @@ -833,40 +611,39 @@ public class FileUtil { - ``` #### 6.2 文件下载 -1.在 fileController.java 中加上方法: +1.在 fileController.java 中增加下载方法: ```java - /*** - * 上传用于下载的文件 - */ - @PostMapping("upFileForDownload") - public String upFileForDownload(MultipartFile file, HttpSession session, Model model) throws UnsupportedEncodingException { - String path = FileUtil.saveFile(file, session.getServletContext().getRealPath("/image")); - model.addAttribute("filePath", URLEncoder.encode(path,"utf-8")); - model.addAttribute("fileName", file.getOriginalFilename()); - return "fileDownload"; - } +/*** + * 上传用于下载的文件 + */ +@PostMapping("upFileForDownload") +public String upFileForDownload(MultipartFile file, HttpSession session, Model model) throws UnsupportedEncodingException { + String path = FileUtil.saveFile(file, session.getServletContext().getRealPath("/image")); + model.addAttribute("filePath", URLEncoder.encode(path,"utf-8")); + model.addAttribute("fileName", file.getOriginalFilename()); + return "fileDownload"; +} - /*** - * 下载文件 - */ - @GetMapping("download") - public ResponseEntity downloadFile(String filePath) throws IOException { - HttpHeaders headers = new HttpHeaders(); - File file = new File(filePath); - // 解决文件名中文乱码 - String fileName=new String(file.getName().getBytes("UTF-8"),"iso-8859-1"); - headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); - headers.setContentDispositionFormData("attachment", fileName); +/*** + * 下载文件 + */ +@GetMapping("download") +public ResponseEntity downloadFile(String filePath) throws IOException { + HttpHeaders headers = new HttpHeaders(); + File file = new File(filePath); + // 解决文件名中文乱码 + String fileName=new String(file.getName().getBytes("UTF-8"),"iso-8859-1"); + headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); + headers.setContentDispositionFormData("attachment", fileName); - return new ResponseEntity(FileUtils.readFileToByteArray(file), - headers, HttpStatus.CREATED); - } + return new ResponseEntity(FileUtils.readFileToByteArray(file), + headers, HttpStatus.CREATED); +} ``` 2.其中 fileDownload.jsp 如下: @@ -885,20 +662,11 @@ public class FileUtil { -## 七、Restful风格的请求 +## 七、RESTful 风格的请求 -1.新建 Pet.java 实体类 +1.新建测试实体类: ```java -package com.heibaiying.bean; - -import lombok.Data; - -/** - * @author : heibaiying - * @description :测试 restful 风格的实体类 - */ - @Data public class Pet { @@ -906,30 +674,11 @@ public class Pet { private String petId; } - ``` -2.新建 RestfulController.java,用@PathVariable 和@ModelAttribute 注解进行参数绑定。 - -注: 在 REST 中,资源通过 URL 进行识别和定位。REST 中的行为是通过 HTTP 方法定义的。在进行不同行为时对应 HTTP 方法和 Spring 注解分别如下: - -- 创建资源时:POST(PostMapping) -- 读取资源时:GET( @GetMapping) -- 更新资源时:PUT 或 PATCH(PutMapping、PatchMapping) -- 删除资源时:DELETE(DeleteMapping) +2.新建 RestfulController.java,用 @PathVariable 和 @ModelAttribute 注解进行参数绑定: ```java -package com.heibaiying.controller; - -import com.heibaiying.bean.Pet; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -/** - * @author : heibaiying - * @description : Restful 风格的请求 - */ - @RestController public class RestfulController { @@ -944,8 +693,12 @@ public class RestfulController { System.out.println("ownerId:" + pet.getOwnerId()); System.out.println("petId:" + pet.getPetId()); } - } - ``` +在 RESTful 风格的请求中,资源通过 URL 进行标识和定位,而操作行为是通过 HTTP 方法进行定义,在进行不同行为时对应 HTTP 方法和 Spring 注解分别如下: + +- 创建资源时:POST(@PostMapping) +- 读取资源时:GET( @GetMapping) +- 更新资源时:PUT 或 PATCH(@PutMapping、@PatchMapping) +- 删除资源时:DELETE(@DeleteMapping) \ No newline at end of file diff --git a/spring/springmvc-base/README.md b/spring/springmvc-base/README.md index 1a75fbc..ffc3374 100644 --- a/spring/springmvc-base/README.md +++ b/spring/springmvc-base/README.md @@ -1,69 +1,63 @@ -# springmvc基础(基于xml配置) - -## 目录
-一、搭建hello spring工程
-    1.1 项目搭建
-    1.2 相关配置讲解
+# Spring MVC 基础(基于 Xml 配置) + -## 正文
+## 一、搭建 Hello Spring 工程 -## 一、搭建hello spring工程 +### 1.1 构建 Web 项目 -### 1.1 项目搭建 - -1.新建 maven web 工程,并引入相应的依赖 +1.新建 maven web 工程,并引入相应的依赖: ```xml - - 5.1.3.RELEASE - + + 5.1.3.RELEASE + - - org.springframework - spring-context - ${spring-base-version} - - - org.springframework - spring-beans - ${spring-base-version} - - - org.springframework - spring-core - ${spring-base-version} - - - org.springframework - spring-web - ${spring-base-version} - - - org.springframework - spring-webmvc - ${spring-base-version} - - - javax.servlet - javax.servlet-api - 4.0.1 - provided - - + + org.springframework + spring-context + ${spring-base-version} + + + org.springframework + spring-beans + ${spring-base-version} + + + org.springframework + spring-core + ${spring-base-version} + + + org.springframework + spring-web + ${spring-base-version} + + + org.springframework + spring-webmvc + ${spring-base-version} + + + javax.servlet + javax.servlet-api + 4.0.1 + provided + + ``` -2.配置 web.xml +2.在 `web.xml` 中配置前端控制器: ```xml @@ -92,7 +86,7 @@ ``` -3.在 resources 下新建 springApplication.xml 文件,文件内容如下: +3.在 resources 下新建 `springApplication.xml` 文件,文件内容如下: ```xml @@ -125,20 +119,9 @@ ``` -4.在 src 下新建 controller 用于测试 +4.新建 Controller 用于测试整体配置是否成功: ```java -package com.heibaiying.controller; - -import com.heibaiying.exception.NoAuthException; -import org.springframework.stereotype.Controller; -import org.springframework.web.bind.annotation.RequestMapping; - -/** - * @author : heibaiying - * @description : hello spring - */ - @Controller @RequestMapping("mvc") public class HelloController { @@ -147,12 +130,10 @@ public class HelloController { private String hello() { return "hello"; } - } - ``` -5.在 WEB-INF 下新建 jsp 文件夹,新建 hello.jsp 文件 +5.在 WEB-INF 下新建 jsp 文件夹,并创建一个简单的 hello.jsp 文件: ```jsp <%@ page contentType="text/html;charset=UTF-8" language="java" %> @@ -166,17 +147,17 @@ public class HelloController { ``` -6.启动 tomcat 服务,访问 localhost:8080/mvc/hello +6.启动 tomcat 服务,访问的服务地址为:localhost:8080/mvc/hello ### 1.2 相关配置讲解 -**1.\** +#### 1.\ -在 web.xml 配置中,我们将 DispatcherServlet 的拦截路径设置为“\”,则 spring 会捕获所有 web 请求,包括对静态资源的请求,为了正确处理对静态资源的请求,spring 提供了两种解决方案: +在上面的 web.xml 配置中,我们将 DispatcherServlet 的拦截路径设置为 `\`,此时 spring 会捕获所有 web 请求,包括对静态资源的请求,此时对 `.css` , `.js` 等文件的请求都会被拦截,为了正确处理静态资源的请求,spring 提供了两种解决方案: -- 配置\ : 配置后,会在 Spring MVC 上下文中定义一个 org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler,它会对进入 DispatcherServlet 的 URL 进行筛查,如果发现是静态资源的请求,就将该请求转由 Web 应用服务器默认的 Servlet 处理,如果不是静态资源的请求,才由 DispatcherServlet 继续处理。 +- **配置 \** : 配置后 Spring 会自动在上下文中定义一个 DefaultServletHttpRequestHandler,它会对所有进入 DispatcherServlet 的 URL 进行筛查,如果发现是静态资源的请求,就将该请求转发给 Web 应用服务器默认的 Servlet 处理,如果不是静态资源的请求,则仍由 DispatcherServlet 继续处理。 -- 配置\ :指定静态资源的位置和路径映射: +- **配置 \** :通过指定静态资源的位置和路径映射来避免其被拦截,配置示例如下: ```xml @@ -184,30 +165,17 @@ public class HelloController { ``` -**2.\** +#### 2.\ - 会自动注册DefaultAnnotationHandlerMapping与AnnotationMethodHandlerAdapter -两个 bean,用以支持@Controllers 分发请求。并提供了数据绑定、参数转换、json 转换等功能,所以必须加上这个配置。 +该配置会自动注册 DefaultAnnotationHandlerMapping 与 AnnotationMethodHandlerAdapter 两个 bean,用以支持 @Controller 注解,并额外提供了数据绑定、参数转换、json 转换等功能。 ## 二、配置自定义拦截器 -1.创建自定义拦截器,实现接口 HandlerInterceptor(这里我们创建两个拦截器,用于测试拦截器方法的执行顺序) +1.创建自定义拦截器,实现接口 HandlerInterceptor(这里我们创建两个拦截器,用于测试拦截器的执行顺序): ```java -package com.heibaiying.interceptors; - -import org.springframework.web.servlet.HandlerInterceptor; -import org.springframework.web.servlet.ModelAndView; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -/** - * @author : heibaiying - * @description : spring5 中 preHandle,postHandle,afterCompletion 在接口中被声明为默认方法 - */ public class MyFirstInterceptor implements HandlerInterceptor { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { @@ -227,18 +195,6 @@ public class MyFirstInterceptor implements HandlerInterceptor { ``` ```java -package com.heibaiying.interceptors; - -import org.springframework.web.servlet.HandlerInterceptor; -import org.springframework.web.servlet.ModelAndView; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -/** - * @author : heibaiying - * @description : spring5 中 preHandle,postHandle,afterCompletion 在接口中被声明为默认方法 - */ public class MySecondInterceptor implements HandlerInterceptor { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) { @@ -257,24 +213,24 @@ public class MySecondInterceptor implements HandlerInterceptor { ``` -2.在 springApplication.xml 中注册自定义拦截器 +2.在 `springApplication.xml` 中注册自定义拦截器: ```xml - - - - - - - - - - - - + + + + + + + + + + + + ``` -3.关于多个拦截器方法执行顺序的说明 +**3. 关于多个拦截器方法执行顺序的说明** 拦截器的执行顺序是按声明的先后顺序执行的,先声明的拦截器中的 preHandle 方法会先执行,然而它的 postHandle 方法和 afterCompletion 方法却会后执行。 @@ -282,15 +238,9 @@ public class MySecondInterceptor implements HandlerInterceptor { ## 三、全局异常处理 -1.定义自定义异常 +1.定义自定义异常: ```java -package com.heibaiying.exception; - -/** - * @author : heibaiying - * @description : 自定义无权限异常 - */ public class NoAuthException extends RuntimeException { public NoAuthException() { @@ -310,24 +260,11 @@ public class NoAuthException extends RuntimeException { } } - ``` -2.实现自定义异常处理器 +2.实现自定义异常处理器: ```java -package com.heibaiying.exception; - -import org.springframework.web.servlet.HandlerExceptionResolver; -import org.springframework.web.servlet.ModelAndView; - -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -/** - * @author : heibaiying - * @description : 无权限异常处理机制 - */ public class NoAuthExceptionResolver implements HandlerExceptionResolver { public ModelAndView resolveException(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) { @@ -342,17 +279,16 @@ public class NoAuthExceptionResolver implements HandlerExceptionResolver { return "XMLHttpRequest".equalsIgnoreCase(request.getHeader("X-Requested-With")); } } - ``` -3.在 springApplication.xml 注册自定义异常处理器 +3.在 `springApplication.xml` 注册自定义的异常处理器: ```xml ``` -4.定义测试 controller,抛出自定义异常 +4.创建测试 Controller,抛出自定义异常: ```java @Controller @@ -364,7 +300,6 @@ public class HelloController { return "hello"; } - @RequestMapping("auth") private void auth() { throw new NoAuthException("没有对应的访问权限!"); @@ -372,7 +307,7 @@ public class HelloController { } ``` -注:调用这个 controller 时,同时也可以验证在拦截器部分提到的:如果对应的程序报错,拦截器不一定会进入 postHandle 这个方法 但一定会进入 afterCompletion 这个方法 +调用这个 Controller 时,同时可以验证在拦截器部分提到的:如果对应的程序报错,拦截器不一定会进入 postHandle 这个方法 但一定会进入 afterCompletion 这个方法。 @@ -380,16 +315,11 @@ public class HelloController { ### 4.1 参数绑定 -1.新建 Programmer.java +1.新建 Programmer.java 作为测试实体类: ```java -package com.heibaiying.bean; - -import lombok.Data; - /** - * @author : heibaiying - * @description : + * @Data 是 lombok 包下的注解,用来生成相应的 set、get 方法,使得类的书写更为简洁。 */ @Data public class Programmer { @@ -402,32 +332,11 @@ public class Programmer { private String birthday; } - ``` -注:@Data 是 lombok 包下的注解,用来生成相应的 set、get 方法,使得类的书写更为简洁。 - -2.新建 ParamBindController.java 文件 +2.新建 ParamBindController.java 文件,跳转到指定视图: ```java -package com.heibaiying.controller; - -import com.heibaiying.bean.Programmer; -import org.springframework.format.annotation.DateTimeFormat; -import org.springframework.format.datetime.DateFormatter; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.WebDataBinder; -import org.springframework.web.bind.annotation.InitBinder; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; - -import java.util.Date; - -/** - * @author : heibaiying - * @description :参数绑定 - */ @Controller public class ParamBindController { @@ -465,10 +374,9 @@ public class ParamBindController { } } - ``` -3.新建 param.jsp 文件 +3.新建 param.jsp 文件,用于测试数据在视图中的绑定情况: ```jsp <%@ page contentType="text/html;charset=UTF-8" language="java" %> @@ -488,39 +396,30 @@ public class ParamBindController { ``` -4.启动 tomcat,用[postman](https://www.getpostman.com/) 软件发送请求进行测试 +4.启动 tomcat,接着可以使用 [postman](https://www.getpostman.com/) 等接口测试软件发送测试请求。 -### 4.2 关于日期格式转换的三种方法 +### 4.2 日期格式转换 -1.如上实例代码所示,在对应的 controller 中初始化绑定 +Spring 支持使用以下三种方法来对参数中的日期格式进行转换: + +**方法一**:如上面的实例代码所示,在对应的 Controller 中初始化绑定: ```java @InitBinder - protected void initBinder(WebDataBinder binder) { - binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd HH:mm:ss")); - } +protected void initBinder(WebDataBinder binder) { + binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd HH:mm:ss")); +} ``` -2.利用@DateTimeFormat 注解,如果是用实体类去接收参数,则在对应的属性上用@DateTimeFormat 和@JsonFormat 声明 +**方法二**:利用 @DateTimeFormat 注解,如果是用实体类去接收参数,则在对应的实体类的属性上用 @DateTimeFormat 和 @JsonFormat 进行声明: ```java public String param(@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") Date birthday) ``` -3.使用全局的日期格式绑定,新建自定义日期格式转化类,之后在 springApplication.xml 中进行注册 +**方法三**:使用全局的日期格式绑定,新建自定义日期格式转化类,之后在 `springApplication.xml` 中进行注册,采用这种方式会对全局范围内的日期格式转换生效: ```java -package com.heibaiying.convert; - -import org.springframework.core.convert.converter.Converter; - -import java.text.SimpleDateFormat; -import java.util.Date; - -/** - * @author : heibaiying - * @description : - */ public class CustomDateConverter implements Converter { public Date convert(String s) { @@ -533,103 +432,60 @@ public class CustomDateConverter implements Converter { return null; } } - ``` -springApplication.xml +在 `springApplication.xml` 中进行注册: ```xml - + - + ``` ## 五、数据校验 -1.spring 支持的数据校验是 JSR303 的标准,需要引入依赖的 jar 包 +1.Spring 支持 JSR303 标准的校验,需要引入相关的依赖: -```java - - - org.hibernate.validator - hibernate-validator - 6.0.13.Final - - - javax.validation - validation-api - 2.0.1.Final - +```xml + + + org.hibernate.validator + hibernate-validator + 6.0.13.Final + + + javax.validation + validation-api + 2.0.1.Final + ``` -2.新建测试 ParamValidController.java,主要是在需要校验的参数前加上@Validated,声明参数需要被校验,同时加上 bindingResult 参数,这个参数中包含了校验的结果 +2.新建测试类 ParamValidController.java,在需要校验的参数前加上 @Validated 注解,表明该参数需要被校验。同时在方法声明中加上 bindingResult 参数,可以从这个参数中获取最终校验的结果: ```java -package com.heibaiying.controller; - -import com.heibaiying.bean.Programmer; -import org.hibernate.validator.constraints.Length; -import org.springframework.format.annotation.DateTimeFormat; -import org.springframework.format.datetime.DateFormatter; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.validation.BindingResult; -import org.springframework.validation.ObjectError; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.WebDataBinder; -import org.springframework.web.bind.annotation.InitBinder; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; - -import javax.validation.constraints.Min; -import javax.validation.constraints.NotNull; -import java.util.Date; -import java.util.List; - -/** - * @author : heibaiying - * @description :数据校验 - */ @RestController public class ParamValidController { - @PostMapping("validate") - public void valid(@Validated Programmer programmer, - BindingResult bindingResult) { + public void valid(@Validated Programmer programmer,BindingResult bindingResult) { List allErrors = bindingResult.getAllErrors(); for (ObjectError error : allErrors) { System.out.println(error.getDefaultMessage()); } } - } - ``` -3.在 Programmer.java 的对应属性上加上注解约束 (支持的注解可以在 javax.validation.constraints 包中查看) +3.在 Programmer.java 的对应属性上加上注解约束,用于声明每个参数的校验规则: ```java -package com.heibaiying.bean; - -import lombok.Data; - -import javax.validation.constraints.Min; -import javax.validation.constraints.NotNull; - -/** - * @author : heibaiying - * @description : - */ -@Data public class Programmer { @NotNull @@ -643,55 +499,30 @@ public class Programmer { private String birthday; } - ``` +其他支持的注解可以到 javax.validation.constraints 包下进行查看。 + ## 六、文件上传与下载 #### 6.1 文件上传 -1.在 springApplication.xml 中进行配置,使之支持文件上传 +1.在 `springApplication.xml` 中进行配置,开启文件上传: ```xml - - - - - - - - + + + + + + + + ``` -2.新建测试上传的 FileController.java +2.新建上传测试类: ```java -package com.heibaiying.controller; - -import com.heibaiying.utils.FileUtil; -import org.apache.commons.io.FileUtils; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpStatus; -import org.springframework.http.MediaType; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Controller; -import org.springframework.ui.Model; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestParam; -import org.springframework.web.multipart.MultipartFile; - -import javax.servlet.http.HttpSession; -import java.io.File; -import java.io.IOException; -import java.io.UnsupportedEncodingException; -import java.net.URLEncoder; - -/** - * @author : heibaiying - * @description : 文件上传 - */ - @Controller public class FileController { @@ -700,7 +531,6 @@ public class FileController { return "file"; } - /*** * 单文件上传 */ @@ -737,22 +567,11 @@ public class FileController { return "success"; } } - ``` -3.其中工具类 FileUtil.java 代码如下 +3.其中工具类 FileUtil.java 的代码实现如下: ```java -package com.heibaiying.utils; - -import org.springframework.web.multipart.MultipartFile; -import java.io.*; - -/** - * @author : heibaiying - * @description : 文件上传工具类 - */ - public class FileUtil { public static String saveFile(MultipartFile file, String path) { @@ -795,7 +614,7 @@ public class FileUtil { } ``` -4.新建用于上传的 jsp 页面,上传文件时表单必须声明 enctype="multipart/form-data" +4.新建用于上传的 jsp 页面,上传文件时表单必须声明 `enctype="multipart/form-data"` : ```jsp <%@ page contentType="text/html;charset=UTF-8" language="java" %> @@ -830,38 +649,38 @@ public class FileUtil { #### 6.2 文件下载 -1.在 fileController.java 中加上方法: +1.在 fileController.java 中增加下载方法: ```java - /*** - * 上传用于下载的文件 - */ - @PostMapping("upFileForDownload") - public String upFileForDownload(MultipartFile file, HttpSession session, Model model) throws UnsupportedEncodingException { - String path = FileUtil.saveFile(file, session.getServletContext().getRealPath("/image")); - model.addAttribute("filePath", URLEncoder.encode(path,"utf-8")); - model.addAttribute("fileName", file.getOriginalFilename()); - return "fileDownload"; - } +/*** + * 上传用于下载的文件 + */ +@PostMapping("upFileForDownload") +public String upFileForDownload(MultipartFile file, HttpSession session, Model model) throws UnsupportedEncodingException { + String path = FileUtil.saveFile(file, session.getServletContext().getRealPath("/image")); + model.addAttribute("filePath", URLEncoder.encode(path,"utf-8")); + model.addAttribute("fileName", file.getOriginalFilename()); + return "fileDownload"; +} - /*** - * 下载文件 - */ - @GetMapping("download") - public ResponseEntity downloadFile(String filePath) throws IOException { - HttpHeaders headers = new HttpHeaders(); - File file = new File(filePath); - // 解决文件名中文乱码 - String fileName=new String(file.getName().getBytes("UTF-8"),"iso-8859-1"); - headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); - headers.setContentDispositionFormData("attachment", fileName); +/*** + * 下载文件 + */ +@GetMapping("download") +public ResponseEntity downloadFile(String filePath) throws IOException { + HttpHeaders headers = new HttpHeaders(); + File file = new File(filePath); + // 解决文件名中文乱码 + String fileName=new String(file.getName().getBytes("UTF-8"),"iso-8859-1"); + headers.setContentType(MediaType.APPLICATION_OCTET_STREAM); + headers.setContentDispositionFormData("attachment", fileName); - return new ResponseEntity(FileUtils.readFileToByteArray(file), - headers, HttpStatus.CREATED); - } + return new ResponseEntity(FileUtils.readFileToByteArray(file), + headers, HttpStatus.CREATED); +} ``` -2.其中 fileDownload.jsp 如下: +2.其中 fileDownload.jsp 的内容如下: ```jsp <%@ page contentType="text/html;charset=UTF-8" language="java" %> @@ -877,20 +696,11 @@ public class FileUtil { -## 七、Restful风格的请求 +## 七、RESTful 风格的请求 -1.新建 Pet.java 实体类 +1.新建测试实体类: ```java -package com.heibaiying.bean; - -import lombok.Data; - -/** - * @author : heibaiying - * @description :测试 restful 风格的实体类 - */ - @Data public class Pet { @@ -898,30 +708,11 @@ public class Pet { private String petId; } - ``` -2.新建 RestfulController.java,用@PathVariable 和@ModelAttribute 注解进行参数绑定。 - -注: 在 REST 中,资源通过 URL 进行识别和定位。REST 中的行为是通过 HTTP 方法定义的。在进行不同行为时对应 HTTP 方法和 Spring 注解分别如下: - -- 创建资源时:POST(PostMapping) -- 读取资源时:GET( @GetMapping) -- 更新资源时:PUT 或 PATCH(PutMapping、PatchMapping) -- 删除资源时:DELETE(DeleteMapping) +2.新建 RestfulController.java,用 @PathVariable 和 @ModelAttribute 注解进行参数绑定: ```java -package com.heibaiying.controller; - -import com.heibaiying.bean.Pet; -import org.springframework.validation.annotation.Validated; -import org.springframework.web.bind.annotation.*; - -/** - * @author : heibaiying - * @description : Restful 风格的请求 - */ - @RestController public class RestfulController { @@ -936,7 +727,13 @@ public class RestfulController { System.out.println("ownerId:" + pet.getOwnerId()); System.out.println("petId:" + pet.getPetId()); } - } - ``` + +在 RESTful 风格的请求中,资源通过 URL 进行标识和定位,而操作行为是通过 HTTP 方法进行定义,在进行不同行为时对应 HTTP 方法和 Spring 注解分别如下: + +- 创建资源时:POST(@PostMapping) +- 读取资源时:GET( @GetMapping) +- 更新资源时:PUT 或 PATCH(@PutMapping、@PatchMapping) +- 删除资源时:DELETE(@DeleteMapping) +