# SpringMVC **Repository Path**: xgdtianxian/SpringMVC ## Basic Information - **Project Name**: SpringMVC - **Description**: SpringMVC - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2023-11-08 - **Last Updated**: 2024-05-14 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README SpringMVC 一 .MVC MVC是软件设计的一种架构模式 M:model 模型 包含 数据和 行为两部分 功能:1.承载需要显示在页面里的数据(java实体 User,Book,Writer) 功能:2 执行具体的业务操作,数据库操作(Service对象和Dao的对象) V:view 视图:负责进行模型的显示,就是用户看到的界面.(jsp html) C:controller 控制器: 1.负责 接受请求中的 数据 2.委托给模型进行处理(控制器调用 service 再调用dao) 3.收到处理后的数据以后把数据转交给 视图,由视图来负责显示。 ps:框架之前 ——>Servlet SpringMVC -----> SpringMVC 的 Controller 二.SpringMVC 1.Spring框架的一部分 2.构建在Servlet之上的 3.轻量级的java的MVC框架 三.SpringMVC快速入门(略) 四.SpringMVC工作原理: SpringMVC的所有设计都是 围绕 DispatcherServlet 来进行设计。 1.客户端发起请求被DispatcherServlet 捕获 2.ds 调用HandlerMapping 根据注解配置,查找要被执行的handler 3 由hm 返回handler 给 ds 4 ds 调用 HandlerAdapter,给传递Handler 5.由HandlerAdapter 调用handler里的方法 6.Handler(我们自己写的Controller,后端处理器)执行方法,返回 ModelAndView对象(模型和视图) 7.HandlerAdapter 把ModelAndView 返回给Ds 8.Ds传递mv给ViewResolver(视图解析器),根据字符串获取相关的视图(jsp html) 9.视图解析器返回 View(视图对象)给Ds 10.ds 对 view进行渲染(得到纯静态的html、json) 11.ds把静态的html 返回给客户端。 五,请求参数处理: 1.请求中的中文乱码问题: a.对于POST请求而言,可以使用SpringMVC提供的字符编码过滤器 CharacterEncodingFilter characterEncodingFilter org.springframework.web.filter.CharacterEncodingFilter encoding UTF-8 characterEncodingFilter springMVC b.对于 GET请求而言,在Tomcat的安装目录下的 config /serve.xml中 2.请求参数为标量(字符串 数字 日期) a.请求的方法中,形参的名字和 参数的参数名(name属性)完全一致即可 ps:对于 日期类型(Date),默认要求 日期的格式为: yyyy/MM/dd hh:mm:ss b. 在形参前 加上 @RequestParam(value="请求参数的参数名",required="该参数是否必填",defaultValue="默认值") RequestParam 会把 指定参数名的 请求参数 赋值给 形参(不要求两个名字一致) 3.如果方法的参数 是一个 POJO对象 请求参数的名字 和 对象的属性名一样就可以了,和方法的形参名 无关(不需要写成 形参名.属性名的格式),就可以自动的吧请求的参数, 转换POJO对象的 属性的属性值。 如果POJO的某个属性 是另外一种对象b,请求参数的参数名:属性名.属性 4. 如果方法的参数 是一个 数组/集合 请求参数中,name属性和数组的名字一致即可 test05?hobbies=sing&hobbies=dance(不加索引) 集合,集合需要放在对象的属性出现,name属性为 集合名字[索引] 5. 如果对象的属性是一个 map集合 请求 参数中 name属性 应该是 对象属性名[key] 6. 获取 请求头中的数据 @RequestHeader("请求头的名字") String 形参 7. 获取 请求中 Cookie中的数据 @CookieValue("cookie的name") String 形参 8. 获取ServletAPI 简单来说就是获取 request response session application 这几个对象 a. 项目导入 servlet-api的jar包 b. 直接在 方法中,加入 相关的参数声明 public String getApi(HttpServletRequest request,HttpServletResponse response,HttpSession session){ ServletContext application = request.getServletContext(); } 六.请求映射处理 1.RequestMapping 可以放在Controller类上面,表示在访问 该Controller下的所有方法时,应该加上此时指定的路径 @RequestMapping("/user") public class UserController(){...} 2.RequestMapping 还可以放在Controller里的方法上(注意+/) ("/addUser") 完整的访问路径应该是 : 类上配置的路径+方法上的路径 ("项目名/user/addUser") ps: 方法中 返回页面时,+"/index.jsp" 表示 项目的根目录出发的 index.jsp 3.RequestMapping 可以通过属性配置请求方式等信息 @RequestMapping(value="/请求路径",method="{RequestMethod.POST,RequestMethod.GET}",params={"username","!password"},headers={"user-agent"}") method-> 设置请求方式 params-> 设置请求中参数的要求 headers-> 请求头中数据的要求 4. 可以使用 @PostMapping @GetMapping @DeleteMapping @PutMapping 去接受特定方式的请求。 5. 获取url目录级别的参数(即在url地址中,不通过 ?参数名=值 这种形式传参) https://www.acfun.cn/v/ac39779046(视频id) https://blog.csdn.net/weixin123/article/details/130389856 账号 文章id @RequestMapping("/user/{id}") public String getUser(@PathVariable("id") int userId){ // 可以把url地址中 {id}占位符的这部分,赋值给形参userId } @RequestMapping("/user/{username}/{password}") public String getUser2(User user){ // 占位符的名字要和对象的属性名一样 } 七.REST REST是一种架构模式, restful url指的是 rest这种风格的 URL链接 客户端到服务器资源 restful的url 通过 名词 + 方法的请求方式来确定 url地址的功能 localhost:8080/library/user/1 GET->查询 POST->添加 DELETE->删除 PUT->修改 单体应用中: HTML语言只支持 POST/GET请求 HTTP协议 支持 POST、GET、DELETE、PUT请求 ajax/axios 支持 DELETE 请求和 PUT请求 表单发起DELETE/PUT请求 1 请求方式 改为 POST 2 表单中加入 3 web.xml中 加入 HiddenHttpMethodFilter过滤器 4.方法中使用 @DeleteMapping @PutMapping 5.如果要在controller中做页面跳转 a.tomcat 7.0以前 b.jsp + isErrorPage = true c.跳转方式使用重定向 /项目名/资源。。 八.静态资源访问 DispatcherServlet(前端控制器)拦截了 除了 jsp之外的所有请求("/") ,导致再页面中 引入 css js html 图片这些静态资源都会被拦截, 而Controller里找不到与其对应的资源,会出404. 而这些资源并不需要经过Controller 处理方法1; 配置 默认TomcatServlet,当SpringMVC404时,请求交由Tomcat来处理。 方式2:配置静态资源直接访问 九.响应处理 9.1 视图解析器 controller中返回一个字符串,根据解析器找到对应的视图 9.2 视图控制器 对于部分jsp页面,不希望通过Controller进行访问,而又被放在了web-inf。 9.3 向request作用域存入数据 a. 直接在方法中加入 HttpServletRequest request. request.setAttribute(); b. 在方法中 加入 Model 或 ModelMap 或 Map,直接调用他们的方法存入数据.本质都是通过(BindingAwareModelMap 来存) c. 通过ModelAndView,即方法返回一个对象,在modelAndView里; mv.addObject(数据); mv.setViewName(视图名); d.特殊的,如果springMVC方法中进行了参数绑定,并且以转发到某一页面,可以在页面中 ${param.key}取出 9.4 向 session作用域 存取数据 a. 直接使用HttpSession 获取Session : a.1 直接在方法中 加入 HttpSession a.2 在controller 声明一个 HttpSession session 通过@Resource 进行自动注入。 @Resource private HttpSession session b. @SessionAttributes("msg"); b.1 放在Controller类上 b.2 当前类下的所有方法,只要向Model中 存入msg时,都会同时向session中存入一份 b.3 当前类的所有方法,在执行时,都会尝试从session中取出msg,并再model里存入一份 c.@SessionAttribute("msg"); c.1 放在方法形参前面,执行方法时,会从session中 取出一个msg,并赋值给形参 c.2 required = true 表示session必须有msg,没有会报错 c.3 required = false 如果session中没有,会赋值null 9.5 转发和重定向 Controller 里的方法 通过 return + "视图名" 默认是转发 转发: return "forward:/index.jsp" /表示 项目级别的根目录 (web目录下),此时必须写完整路径名(不会经过视图解析器) 重定向: return "redirect:/index.jsp" 同上。 9.6 @ModelAttribute 1. 放在 方法之上,不加任何参数,该Controller下的所有方法在执行之前,都会先执行 @ModelAttribute() 标记的方法 2. 放在方法上,标记的方法中,返回值类型为void,并且方法的参数中加入 Model model, 那么在model中加入的其他数据,可以在该Controller的所有方法里 都能取到 @ModelAttribute public void init(Model model){ model.addAttribute("msg","hello"); } 3.放在方法上,@ModelAttribute("名字A"),标记的方法的返回值,会被存入到Model,名字为"名字A" 相当于执行了 model.addAttribute("A",方法返回值); @ModelAttribute("msg") public String init(){ return "hello"; } 4.放在Controller的方法的 形参前 public String update(@ModelAttribute("u") User user){ // 从model中取出变量 u,和前端传过来user 对象 进行合并 // 在u的基础上,前端传过来的user 有的属性就覆盖,没有则维持不变。 } 9.7 session 获取的线程安全问题 a.SpringMVC controller是单例的 b.直接在方法中加入 参:HttpSession session, 线程安全的,方法的运行在栈内存,每次这个方法被调用都是独立的栈帧。不同的请求就是不同的会话。 c.使用Spring 在Controller里 声明一个 private HttpSession session属性,并通过@Autowired进行自动注入(按理来说是不安全的,因为单例的Controller 只有一个 session属性,只有一个空间来存储) 但是!它是线程安全的,Spring底层实际是把session绑定到线程。 d. 通过ModelAttribute()来获取Session,再传入controller里提前声明的session中,不安全。因为session是声明再类级别里的,共享变量,所以线程不安全 结论,再Controller的内部,由于是单例模式,所以不要声明 类级别的变量(属性,或者 静态属性),会存在线程安全问题 十:类型转换器 功能:按照自定义的规则,把前端传递过来的参数进行类型转换. 流程: 1.创建转换器类,实现Converter<源类型,目标类型>接口 2.重写 convert方法,制定转换规则 3.配置 conversionService bean 类型: ConversionServiceFactoryBean 并在其中注入 converters属性 注入我们自己的转换器 4.配置 ps:bean的id 必须叫conversionService 十一:数据格式化 SpringMVC 运行 我们按照指定的格式进行传参,或者是在页面显示某一个属性时间,按照指定的格式进行显示。 1.直接在 实体类的属性上,添加相关的注解 2.配置类型转换服务 为 FormattingConversionServiceFactoryBean 这个服务bean 同时支持 类型转换器 和 数据格式化。 相关注解: 1.@DateTimeFormat 可以用于 java中的 Date类型的属性上 @DateTimeFormat(pattern ="日期的格式",fallbackPattern={"备用格式"}) 2.@NumberFormat 用于格式化数字类型 @NumberFormat(style="NumberFormat.Style.CURRENCY",pattern="¥#,###.##") style 包含 NUMBER CURRENCY 货币组织 PERCENT 百分比 pattern 中 #代表一位数字 ps:如果希望在jsp页面中 按照格式化的注解来输出对象的属性 导入 spring标签库 <%@ taglib prefix="spring" uri="http://www.springframework.org/tags" %> 十二:数据校验 指的是简化后端的非业务型的输入校验 a.导入jar包 hibernate-validator, 实现了 JSR303的校验标准 b.在需要进行验证的属性上加上相关的注解: @Null 必须为空 @NotNull 必须不为空 @AssortTure 必须为true @AssortFalse 必须为false @Min(value) 允许的最小值(>=) @Max(value) 允许的最大值(<=) @DecimalMin(value) 最小值 @DecimalMax(value) 最大值 @Past 必须为过去的时间 @Future 必须为将来的时间 @Pattern("regx") 必须满足指定的正则表达式 @Email 必须为邮箱格式 @Length (字符串的长度) @NotEmpty (字符串必须非空) @Range (被注解的元素必须在合适的范围内) c.Controller的方法里,在需要进行校验的参数前 @Valid d.方法中加入 BindingResult 来获取所有的错误信息 BindingResult的方法 hasErrors() 是否有错误信息 getFieldErrors() 获取所有的 属性错误信息 List FieldError的方法: getFiled() 获取没有通过校验的属性的属性名 getDefaultMessage() 获取错误的提示信息 ps:数据校验可以配合Spring 表单标签 进行简化 a.导入 spring form标签库 b 来表示一个表单 跳转到 这个页面的时候 request里 必须要有一个 名字叫 user的对象 来表示一个一般的输入框 path后跟对象的属性 来表示 username的 错误信息提示 c. 后端controller的方法中,直接使用 @Valid BindingResult,方法中不再需要自己来解析错误信息 十三: SpringMVC json处理 前置: json对象,指的是 javascript格式描述的一个对象 {"userId":1,"username":"zhangsan"} json字符串: 本质上是一个字符串符合json格式 '{"userId":1,"username":"zhangsan"}' 前端: JSON.parse(字符串) -> 把字符串转换成对象 JSON.stringify(对象) -> 对象转换成 json字符串 1.返回JSON(字符串) a。导入json转换相关的jar包,负责把java对象转换成json字符串,把json字符串转换成java对象 (常用的 jackson 2.12.4 fastJson) b. 在Controller的方法上 加上注解 @ResponseBody ,这时这个方法不在进行该页面跳转,只负责返回数据 。这种接口 叫做 服务型API ps: 如果一个Controller的方法都是服务型API,那么可以直接在类上 使用 @RestController,不需要额外@ResponseBody c. 方法的返回值直接返回需要的数据类型(Object),直接在方法的末尾return ps:静态资源配置不可少 注解: @JsonIgnore 放在 某个属性上,当这个对象转换成json字符串时,会忽略这个属性 @JsonFormat(pattern="yyyy-MM-dd") private Date birthday 默认的 java date类型再转换成json字符串时,转换成时间戳,通过该注解,可以设置转换的日期格式 2.接受Json a. 前端 需要 向 后端发起请求 a.原始的js b.jquery的ajax c.vue的 axios b. 使用 ajax $.ajax({ url:"请求地址", type:“请求方式”, data:"给后端发送的数据", contentType:"设置前端给后端发送的数据格式(默认是表单,application/json)", dataType:前端接收后端的数据的格式(预估) success:function(data){ 请求成功后响应的数据 } }) c. 1如果前端发送的是 json字符串(json格式) a.ajax 里 必须设置 contentType:"application/json", b.后端使用 @RequestBody 把字符串 转换成java对象 c.前端不能使用 get请求,使用POST 2 如果 前端发送的是 一个对象,等效于用表单发起请求 a.contentType "application/x-www-form-unlencoded",可以不写 b. 后端不能用 @RequestBody c. data里的数据实际被转换成了 key=value&key=value 这种格式 d. 可以使用 post get请求 如果要给后端发送集合 使用方式1 十四。上传和下载 下载的方式: 1.直接在a标签的地址中写要下载的文件路径 a.需要配置静态资源访问 b.不能进行权限控制 c.对于某些格式的文件会直接打开,而不是下载。 2.基于ServletAPI ,通过response对象获取到输出流 直接向客户端写回要下载的文件 @RequestMapping("/download01") public void download01(HttpServletRequest request,HttpServletResponse response) throws IOException { // 获取要下载的文件的真实路径(从盘符出发的路径) String realPath = request.getServletContext().getRealPath("/file/简易.txt"); //创建一个 File表示 要被下载的文件路径 File file = new File(realPath); //创建输入流来读取文件 FileInputStream ins = new FileInputStream(file); //设定 响应的格式,告知输出流的文件以附件的形式打开,URLEncoder 确保下载的文件名中文正常显示。 response.setHeader("content-disposition","attachment;fileName="+ URLEncoder.encode(file.getName(),"UTF-8")); //获取 用于向客户端 返回数据的 输出流 OutputStream oos = response.getOutputStream(); //创建一个 缓冲区 用于装每次从输入流中读出来的信息,缓冲区可以减少 磁盘io的次数. byte[] buffer = new byte[1024]; //文件复制 int len = 0; while((len = ins.read(buffer))!= -1){ oos.write(buffer,0,len); } ins.close(); oos.close(); } 3.基于SpringMVC提供的 ResponseEntity 响应实体进行下载 ,响应实体的构造方法中需要传入 要下载文件的完整字节流, HttpHeaders 和 响应状态码 /* * 基于 Spring提供的 ResponseEntity 进行下载, * ResponseEntity用于表示 返回的实体。 * 潜在风险:一次性获取 完整的输入流中的数据,有可能造成内存溢出。 * */ @RequestMapping("/download02") public ResponseEntity download02(HttpServletRequest request) throws IOException { // 设置 要下载文件的真实路径 String realPath = request.getServletContext().getRealPath("/file/简易.txt"); //创建一个 file对象 File file = new File(realPath); // 创建 一个 Http头文件 HttpHeaders headers = new HttpHeaders(); // 头文件中 设置客户端浏览器打开的方式 headers.set("content-disposition", "attachment;fileName=" + URLEncoder.encode(file.getName(), "UTF-8")); // 创建对 下载文件的 输入流 InputStream ins = new FileInputStream(file); // 创建一个 响应实体,并返回,需要传入 完整的输入流中的数据,响应头,和响应码 byte[] bytes = new byte[ins.available()]; ins.read(bytes); return new ResponseEntity(bytes, headers, HttpStatus.OK); } 上传: 1。导入相关的jar commons-fileupload 2. 在Spring容器中 加入 multipartResolver //可以注入 最大文件大小 编码 每个文件大小等属性 3.表单中
....
4.Controller 使用 MultipartFile mf来接受用户上传的文件 有一组get方法获取文件的信息 mf.transferTo(File file/String path) 把文件复制到指定路径。 十五.SpringMVC拦截器 1.概念: Interceptor拦截器 和过滤器类似,采用面向切面的思想,在Controller里的方法执行之前或者之后执行一些和主业务没有关系的公共功能 (权限/登录验证 日志处理 异常记录 性能分析) 2.步骤: a.自定义 一个拦截器类 实现HandlerInterceptor接口 b.重写 接口的方法 preHandle 预处理方法,再Controller的方法执行之前执行(登录/权限 验证),返回true放行.false中断,不会再执行controller里的方法。 自行使用 request或者response 进行页面跳转。 postHandle 后处理方法,在controller的方法执行完毕之后,视图渲染之前(还没跳页面),可以对ModelAndView进行处理。 ps:如果controller出错,这个方法不会执行,所以不能做异常日志. afterCompletion:请求处理完毕之后(试图已经渲染好),用于记录请求耗时,释放资源,异常记录,统一异常处理(转发) c. 在容器中 配置拦截器 //会拦截所有的请求 // 配置具体的某一个拦截器 /login(具体某一个路径) /*(所有路径,但不包含二级路径) /**(所有包含二级) /user/* user下的所有请求 同上,注意添加放行静态资源 SpringMVC的拦截器 和 Servlet的过滤器 区别: 1.过滤器是基于函数回调,拦截器是基于java的反射机制 2.过滤器依赖于Tomcat,拦截器依赖SpringMVC 3.过滤器可以拦截所有的请求,而拦截器只作用于SpringMVC的controller 4.拦截器可以访问 Handler的上下文,而过滤器不行。 十六:SpringMVC 国际化: 给不同国家的用户,以不同样的语言文本呈现网站内容就叫做国际化,简称 i18n (Internationalization) l10n (Localization) 本地化 a.通过修改浏览器语言设置国际化 1. 在resource下添加 国际化资源文件 i18n/login.properties 或 视图名_语言_地区.properties 资源文件中 key=value 多个语言的key保持一致 2. 配置使用属性资源文件 i18n/login --i18n/视图名 3.确保 jsp页面 是通过 Controller 进行访问,并且使用视图解析器 4.在jsp中 使用 输出对应的文字 5.修改所在区域 b.通过超链接来切换国际化 a. 发起任何请求时,在请求中携带 locale 地区参数 login?locale=zh_CN login?locale=en_US b.配置地区变更拦截器,会保存地区变更的信息 c. 修改 默认的 地区解析器 地区解析器: AcceptHeaderLocaleResolver (默认) 根据浏览器的语言信息(请求头中的accept-language) 解析地区信息 SessionLocaleResolver 从session中获取 locale信息 CookieLocaleResolver 从Cookie中获取 locale信息 d.当任何一处改变了地区信息,那么接下来全部的操作都会使用相同的语言。 3.在Controller中的方法中如何获取 国际化资源文件中的文本 a.在Controller中 注入一个 MessageSource(容器中得有) @Resource private MessageSource messageSource b.在Controller中的方法中 传入Locale locale参数 c.messageSource.getMessage("key",null,locale) 获取对应语言的对应key的文本. 4.数据校验国际化实现: a.校验部分使用规则不变 b.在国际化资源文件中 以 校验规则(注解名).对象名.属性="错误消息" 格式编写资源文件 Length.user.username=用户名长度。。。 c.如果不使用Spring的表单标签,那么自行在controller的方法中,根据出错的表单项目,取对应的消息 如果使用 Spring表单标签,则不需要做任何处理 直接使用 取对应的错误消息 十七:SpringMVC 统一异常处理 方式一:针对某一个Controller下的所有方法,在执行时发生的异常进行处理 a.自定义一个 异常处理方法,返回值类型必须为ModelAndView, b.使用 @ExceptionHandler(异常的类型) 来捕获具体的异常 c.在方法中设置 返回的异常视图即可,选择性加上异常信息。(或者日志记录 输出 异常信息) 4.当有多个异常处理方法时,会使用范围更精准的方法。 方式二:全局异常捕获 a.创建一个全局的异常处理类,使用 @ControllerAdvise,会处理所有Controller中的异常. b。@ExceptionHandler + 异常处理方法(同方式一) c.当同时存在全局异常处理和单个Controller里的异常处理,会优先使用单个Controller中的异常处理。 ps: request.getHeaders("X-Requested-With") 不为空时,说明是异步请求 ModelAndView mv = new ModelAndView(new MappingJacksonToJsonView()); 通过此方式初始化的 mv,不再做页面跳转,直接返回json试图(会把mv里的数据,直接转换陈json字符串返回给前端,用于异步请求) 方式三: 只用于设置 Controller方法 执行过程中产生的异常,并进行页面跳转 //默认 错误视图 //配置异常类型 和对应 视图名 404处理: 在web.xml中 404 /404.jsp Swagger2 应用 1.swagger2 可以用于自动生成 接口的文档,可以用于接口的测试 流程: 1. 导入相关的jar包 2. 编写swagger的配置类 3. 在容器中添加 swagger的配置类和swagger需要的静态资源访问 4. 在controller类 方法 参数上使用一些注解 5. 通过 项目名/swagger-ui.html 访问页面 常用注解: @Api(value="名字",tags="标签名(会打组,会覆盖value)") 用在Controller的 类上面。 @ApiOperation(value="名字",notes="提示信息",httpMethod="设置请求") 用在Controller的方法之上 @ApiModel(value="名字",description="介绍文字") 放在bean entity的实体类上 @ApiModelProperty(name="名字",value="名字",notes="介绍") 用在实体类的属性上 @ApiParam(name="page",value="提示",required="是否必填") 用在 controller方法里的,参数前。