SpringMVC学习
SpringFramework历史版本zip下载
https://repo.spring.io/ui/native/release/org/springframework/spring
1. 什么是SpringMVC
1.1 概述
Spring MVC是Spring Framework的一部分,是基于Java实现MVC的轻量级Web框架。
Spring MVC的特点:
- 轻量级,简单易学
- 高效 , 基于请求响应的MVC框架
- 与Spring兼容性好,无缝结合
- 约定优于配置
- 功能强大:RESTful、数据验证、格式化、本地化、主题等
- 简洁灵活
Spring的web框架围绕DispatcherServlet [ 调度Servlet ] 设计。
DispatcherServlet的作用是将请求分发到不同的处理器。从Spring 2.5开始,使用Java 5或者以上版本的用户可以采用基于注解形式进行开发,十分简洁;
正因为SpringMVC好 , 简单 , 便捷 , 易学 , 天生和Spring无缝集成(使用SpringIoC和Aop) , 使用约定优于配置 . 能够进行简单的junit测试 . 支持Restful风格 .异常处理 , 本地化 , 国际化 , 数据验证 , 类型转换 , 拦截器 等等…所以我们要学习 .
最重要的一点还是用的人多 , 使用的公司多 .
1.2 中心控制器
Spring的web框架围绕DispatcherServlet设计。DispatcherServlet的作用是将请求分发到不同的处理器。从Spring 2.5开始,使用Java 5或者以上版本的用户可以采用基于注解的controller声明方式。
Spring MVC框架像许多其他MVC框架一样, 以请求为驱动 , 围绕一个中心Servlet分派请求及提供其他功能,DispatcherServlet是一个实际的Servlet (它继承自HttpServlet 基类)。
Spring Web MVC中的请求处理工作流程:
当发起请求时被前置的控制器拦截到请求,根据请求参数生成代理请求,找到请求对应的实际控制器,控制器处理请求,创建数据模型,访问数据库,将模型响应给中心控制器,控制器使用模型与视图渲染视图结果,将结果返回给中心控制器,再将结果返回给请求者。
1.3 SpringMVC执行原理
1. 用户发送请求至前端控制器DispatcherServlet
2. DispatcherServlet收到请求调用处理器映射器HandlerMapping。
3. 处理器映射器根据请求url找到具体的处理器,生成处理器执行链HandlerExecutionChain(包括处理器对象和处理器拦截器)一并返回给DispatcherServlet。
4. DispatcherServlet根据处理器Handler获取处理器适配器HandlerAdapter执行HandlerAdapter处理一系列的操作,如:参数封装,数据格式转换,数据验证等操作
5. 执行处理器Handler(Controller,也叫页面控制器)。
6. Handler执行完成返回ModelAndView
7. HandlerAdapter将Handler执行结果ModelAndView返回到DispatcherServlet
8. DispatcherServlet将ModelAndView传给ViewReslover视图解析器
9. ViewReslover解析后返回具体View
10. DispatcherServlet对View进行渲染视图(即将模型数据model填充至视图中)。
11. DispatcherServlet响应用户。
组件说明:
1.DispatcherServlet:前端控制器。用户请求到达前端控制器,它就相当于mvc模式中的c,dispatcherServlet是整个流程控制的中心,由它调用其它组件处理用户的请求,dispatcherServlet的存在降低了组件之间的耦合性,系统扩展性提高。由框架实现2.HandlerMapping:处理器映射器。HandlerMapping负责根据用户请求的url找到Handler即处理器,springmvc提供了不同的映射器实现不同的映射方式,根据一定的规则去查找,例如:xml配置方式,实现接口方式,注解方式等。由框架实现
3.Handler:处理器。Handler 是继DispatcherServlet前端控制器的后端控制器,在DispatcherServlet的控制下Handler对具体的用户请求进行处理。由于Handler涉及到具体的用户业务请求,所以一般情况需要程序员根据业务需求开发Handler。
4.HandlAdapter:处理器适配器。通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。由框架实现。
5.ModelAndView是springmvc的封装对象,将model和view封装在一起。
6.ViewResolver:视图解析器。ViewResolver负责将处理结果生成View视图,ViewResolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。
7View:是springmvc的封装对象,是一个接口, springmvc框架提供了很多的View视图类型,包括:jspview,pdfview,jstlView、freemarkerView、pdfView等。一般情况下需要通过页面标签或页面模版技术将模型数据通过页面展示给用户,需要由程序员根据业务需求开发具体的页面。
2. 第一个MVC程序
2.1 配置版
servlet-mapping 中 / 和 /* 的区别
- < url-pattern > / </ url-pattern > 不会匹配到.jsp, 只针对我们编写的请求;即:.jsp 不会进入spring的 DispatcherServlet类 。
- < url-pattern > /* </ url-pattern > 会匹配 *.jsp,会出现返回 jsp视图 时再次进入spring的DispatcherServlet 类,导致找不到对应的controller所以报404错。
2.1.1 新建一个Moudle , springmvc-02-hello , 添加web的支持!
2.1.2 确定导入了SpringMVC 的依赖!
2.1.3 配置web.xml , 注册DispatcherServlet
1 <?xml version="1.0" encoding="UTF-8"?> 2 <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" 5 version="4.0"> 6 7 <!--1.注册DispatcherServlet--> 8 <servlet> 9 <servlet-name>springmvc</servlet-name> 10 <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class> 11 <!--关联一个springmvc的配置文件:【servlet-name】-servlet.xml--> 12 <init-param> 13 <param-name>contextConfigLocation</param-name> 14 <param-value>classpath:springmvc-servlet.xml</param-value> 15 </init-param> 16 <!--启动级别-1--> 17 <load-on-startup>1</load-on-startup> 18 </servlet> 19 20 <!--/ 匹配所有的请求;(不包括.jsp)--> 21 <!--/* 匹配所有的请求;(包括.jsp)--> 22 <servlet-mapping> 23 <servlet-name>springmvc</servlet-name> 24 <url-pattern>/</url-pattern> 25 </servlet-mapping> 26 27 </web-app>
2.1.4 编写SpringMVC 的 配置文件!
名称:springmvc-servlet.xml : [servletname]-servlet.xml
说明,这里的名称要求是按照官方来的
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xsi:schemaLocation="http://www.springframework.org/schema/beans 5 http://www.springframework.org/schema/beans/spring-beans.xsd"> 6 7 </beans>
2.1.5 添加处理映射器
1 <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"/>
2.1.6 添加处理适配器
<bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"/>
2.1.7 添加视图解析器
<!--视图解析器:DispatcherServlet给他的ModelAndView--> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="InternalResourceViewResolver"> <!--前缀--> <property name="prefix" value="/WEB-INF/jsp/"/> <!--后缀--> <property name="suffix" value=".jsp"/> </bean>
2.1.8 编写我们要操作业务Controller ,要么实现Controller接口,要么增加注解;需要返回一个ModelAndView,装数据,封视图;
package nuc.ss.controller; import org.springframework.web.servlet.ModelAndView; import org.springframework.web.servlet.mvc.Controller; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; //注意:这里我们先导入Controller接口 public class HelloController implements Controller { public ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception { //ModelAndView 模型和视图 ModelAndView mv = new ModelAndView(); //封装对象,放在ModelAndView中。Model mv.addObject("msg","HelloSpringMVC!"); //封装要跳转的视图,放在ModelAndView中 mv.setViewName("hello"); //: /WEB-INF/jsp/hello.jsp return mv; } }
2.1.9 将自己的类交给SpringIOC容器,注册bean
<!--Handler--> <bean id="/hello" class="nuc.ss.controller.HelloController"/>
2.1.10 写要跳转的页面
<%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>Kuangshen</title> </head> <body> ${msg} </body> </html>
可能遇到的问题
- 查看控制台输出,看一下是不是缺少了什么jar包。
- 如果jar包存在,显示无法输出,就在IDEA的项目发布中,添加lib依赖!
- 重启Tomcat 即可解决
2.2 注解版
和配置版有一些区别:
2.2.1 添加SpringMVC配置文件
在resource目录下添加springmvc-servlet.xml配置文件,配置的形式与Spring容器配置基本类似,为了支持基于注解的IOC,设置了自动扫描包的功能,具体配置信息如下:
1 <?xml version="1.0" encoding="UTF-8"?> 2 <beans xmlns="http://www.springframework.org/schema/beans" 3 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" 4 xmlns:context="http://www.springframework.org/schema/context" 5 xmlns:mvc="http://www.springframework.org/schema/mvc" 6 xsi:schemaLocation="http://www.springframework.org/schema/beans 7 http://www.springframework.org/schema/beans/spring-beans.xsd 8 http://www.springframework.org/schema/context 9 https://www.springframework.org/schema/context/spring-context.xsd 10 http://www.springframework.org/schema/mvc 11 https://www.springframework.org/schema/mvc/spring-mvc.xsd"> 12 13 <!-- 自动扫描包,让指定包下的注解生效,由IOC容器统一管理 --> 14 <context:component-scan base-package="nuc.ss.controller"/> 15 <!-- 让Spring MVC不处理静态资源 --> 16 <mvc:default-servlet-handler /> 17 <!-- 18 支持mvc注解驱动 19 在spring中一般采用@RequestMapping注解来完成映射关系 20 要想使@RequestMapping注解生效 21 必须向上下文中注册DefaultAnnotationHandlerMapping 22 和一个AnnotationMethodHandlerAdapter实例 23 这两个实例分别在类级别和方法级别处理。 24 而annotation-driven配置帮助我们自动完成上述两个实例的注入。 25 --> 26 <mvc:annotation-driven /> 27 28 <!-- 视图解析器 --> 29 <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" 30 id="internalResourceViewResolver"> 31 <!-- 前缀 --> 32 <property name="prefix" value="/WEB-INF/jsp/" /> 33 <!-- 后缀 --> 34 <property name="suffix" value=".jsp" /> 35 </bean> 36 37 </beans>
2.2.2 我们的controller类不再需要实现Controller接口
package nuc.ss.controller; import org.springframework.stereotype.Controller; import org.springframework.ui.Model; import org.springframework.web.bind.annotation.RequestMapping; @Controller @RequestMapping("/HelloController") public class HelloController { //真实访问地址 : 项目名/HelloController/hello @RequestMapping("/hello") public String sayHello(Model model){ //向模型中添加属性msg与值,可以在JSP页面中取出并渲染 model.addAttribute("msg","hello,SpringMVC"); //web-inf/jsp/hello.jsp return "hello"; } }
2.2.3 使用springMVC必须配置的三大件
处理器映射器、处理器适配器、视图解析器
通常,我们只需要手动配置视图解析器,而处理器映射器和处理器适配器只需要开启注解驱动即可,而省去了大段的xml配置
3. Annotated Controllers
3.1 declaration
3.1.1 controller简介
-
控制器复杂提供访问应用程序的行为,通常通过接口定义或注解定义两种方法实现。
-
控制器负责解析用户的请求并将其转换为一个模型。
-
在Spring MVC中一个控制器类可以包含多个方法
-
在Spring MVC中,对于Controller的配置方式有很多种
3.1.2 方式一 (实现Controller接口)
//实现该接口的类获得控制器功能 public interface Controller { //处理请求且返回一个模型与视图对象 ModelAndView handleRequest(HttpServletRequest var1, HttpServletResponse var2) throws Exception; }
测试:
-
新建一个Moudle,springmvc-04-controller!
- mvc的配置文件只留下 视图解析器!(不需要视图映射器和视图适配器,需要将实现Controller接口的类注册到springmvc-servlet中)
-
编写一个Controller类,ControllerTest1
//定义控制器 //注意点:不要导错包,实现Controller接口,重写方法; public class ControllerTest1 implements Controller { public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { //返回一个模型视图对象 ModelAndView mv = new ModelAndView(); mv.addObject("msg","Test1Controller");
// 要跳转到的视图 mv.setViewName("test"); return mv; } }
4. 编写完毕后,去Spring配置文件中注册请求的bean;name对应请求路径,class对应处理请求的类
<bean name="/t1" class="nuc.ss.controller.ControllerTest1"/>
5. 编写前端的test.jsp文件
说明:
-
实现接口Controller定义控制器是较老的办法
-
缺点是:一个控制器中只有一个方法,如果要多个方法则需要定义多个Controller;定义的方式比较麻烦
3.1.3 使用注解@Controller
<!-- 自动扫描指定的包,下面所有注解类交给IOC容器管理 --> <context:component-scan base-package="nuc.ss.controller"/>
//@Controller注解的类会自动添加到Spring上下文中 @Controller public class ControllerTest2{ //映射访问路径 @RequestMapping("/t2") public String index(Model model){ //Spring MVC会自动实例化一个Model对象用于向视图中传值 model.addAttribute("msg", "ControllerTest2"); //返回视图位置 return "test"; } }
可以发现,我们的两个请求都可以指向一个视图(test),但是页面结果的结果是不一样的,从这里可以看出视图是被复用的,而控制器与视图之间是弱偶合关系。
3.1.4 AOP Proxies
3.2 RequestMapping
@RequestMapping注解用于映射url到控制器类或一个特定的处理程序方法。可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
还有一些变体,如下: 以下变体只能用在方法上。
-
@GetMapping
-
@PostMapping
-
@PutMapping
-
@DeleteMapping
-
@PatchMapping
RequestMapping中可以添加的参数:
1. consumes 限制content-Type
@PostMapping(path = "/pets", consumes = "application/json") public void addPet(@RequestBody Pet pet) { // ... }
2. produces 限制返回数据的类型
@GetMapping(path = "/pets/{petId}", produces = "application/json") @ResponseBody public Pet getPet(@PathVariable String petId) { // ... }
3. Parameters and Headers (不是很懂)
@GetMapping(path = "/pets/{petId}", params = "myParam=myValue") public void findPet(@PathVariable String petId) { // ... }
或者
@GetMapping(path = "/pets", headers = "myHeader=myValue") public void findPet(@PathVariable String petId) { // ... }
3.3 RestFul风格
概念
Restful就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
测试:
1. 在Spring MVC中可以使用 @PathVariable 注解,让方法参数的值对应绑定到一个URI模板变量上
@Controller
public class RestFulController {
@RequestMapping("/add/{a}/{b}")
//@RequestMapping("/add")
//原来的: http://localhost:8080/add?a=1&b=2
// @RequestMapping("/add/{a}/{b}")
//RestFul http://localhost:8080/add/a/b
public String test1(@PathVariable int a,@PathVariable int b, Model model){
int res = a+b;
model.addAttribute("msg","结果为:"+res);
return "test";
}
}
思考:使用路径变量的好处
-
使路径变得更加简洁;
-
获得参数更加方便,框架会自动进行类型转换。
-
通过路径变量的类型可以约束访问参数,如果类型不一样,则访问不到对应的请求方法,如这里访问是的路径是/add/1/a,则路径与方法不匹配,而不会是参数转换失败。
使用method属性指定请求类型:
用于约束请求的类型,可以收窄请求范围。指定请求谓词的类型如GET, POST, HEAD, OPTIONS, PUT, PATCH, DELETE, TRACE等
增加一个方法;
//映射访问路径,必须是POST请求 @RequestMapping(value = "/hello",method = {RequestMethod.POST}) public String index2(Model model){ model.addAttribute("msg", "hello!"); return "test"; }
我们使用浏览器地址栏进行访问默认是Get请求,会报错405:
如果将POST修改为GET则正常了
小结:
Spring MVC 的 @RequestMapping 注解能够处理 HTTP 请求的方法, 比如 GET, PUT, POST, DELETE 以及 PATCH。
所有的地址栏请求默认都会是 HTTP GET 类型的。
4. SpringMVC:结果跳转方式
4.1 ModelAndView
设置ModelAndView对象 , 根据view的名称 , 和视图解析器跳到指定的页面 .
页面 : {视图解析器前缀} + viewName +{视图解析器后缀}
1 <!-- 视图解析器 --> 2 <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" 3 id="internalResourceViewResolver"> 4 <!-- 前缀 --> 5 <property name="prefix" value="/WEB-INF/jsp/" /> 6 <!-- 后缀 --> 7 <property name="suffix" value=".jsp" /> 8 </bean>
public ModelAndView handleRequest(){ //返回一个模型视图对象 ModelAndView mv = new ModelAndView(); mv.addObject("msg","ControllerTest1"); mv.setViewName("test"); //mv.setViewName("forward:/test"); 或者 //mv.setViewName("redirect:/test"); 或者 return mv; }
4.2 Servle API
通过设置ServletAPI , 不需要视图解析器 .
- 通过HttpServletResponse进行输出
- 通过HttpServletResponse实现重定向
- 通过HttpServletResponse实现转发
@Controller public class ResultGo { @RequestMapping("/result/t1") public void test1(HttpServletRequest req, HttpServletResponse rsp) throws IOException { rsp.getWriter().println("Hello,Spring BY servlet API"); } @RequestMapping("/result/t2") public void test2(HttpServletRequest req, HttpServletResponse rsp) throws IOException { rsp.sendRedirect("/index.jsp"); } @RequestMapping("/result/t3") public void test3(HttpServletRequest req, HttpServletResponse rsp) throws Exception { //转发 req.setAttribute("msg","/result/t3"); req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req,rsp); } }
4.3 SpringMVC
通过SpringMVC来实现转发和重定向 - 无需视图解析器;
测试前,需要将视图解析器注释掉
@Controller public class ResultSpringMVC { @RequestMapping("/rsm/t1") public String test1(){ //转发 return "/index.jsp"; } @RequestMapping("/rsm/t2") public String test2(){ //转发二 return "forward:/index.jsp"; } @RequestMapping("/rsm/t3") public String test3(){ //重定向 return "redirect:/index.jsp"; } }
5. 前后端数据交互
5.1 前端 -> 后端
在Spring MVC中支持多种方式将数据从前端传送到后端
5.1.1 查询参数
本质上是通过HTTP发起的一个带有参数的RPC请求,请求的形式为"/aa?name=deyken"
,后端处理形式为:
@RequestMapping(value = "/aa", method = RequestMethod.POST) public String func(Model model, @RequestParam("name") String name) { ... }
注意:如果请求中的参数名和方法中的参数名一致,可以不需要RequestParam()
5.1.2 路径变量(RestFul风格)
前端请求的时候可以不用暴露变量名:
直接请求资源,请求的形式为"/aa/deyken"
,后端处理的形式为:
@RequestMapping(value = "/aa/{name}", method = RequestMethod.POST) public String func(Model model, @PathVariable("name") String name) { ... }
5.1.3 表单提交数据
在Spring MVC中同样支持表单数据的前端到后台传输。以用户登录为例,表单形式为:
<form action="/login" method="post"> name:<input type="text" name="name"/> password:<input type="text" name="password"/> <input type="submit"/> </form>
后端只需在方法参数列表里接收传送过来的变量,而无需再指定查询参数或路径变量:
@RequestMapping(value = "/login", method = RequestMethod.POST) public String add(Model model, String name, String password){ ... }
如果已经定义过用户User类:
public User { String name; String password; ...Setter() ...Getter() }
通过表单提交时 属性名和参数名表要一致
那么Spring MVC会将表单传送过来的数据自动封装为一个User对象,此时后端方法可以这么写:
@RequestMapping(value = "/login", method = RequestMethod.POST) public String add(Model model, User user){ ... }
5.2 后端到前端
5.2.1 第一种 : 通过ModelAndView
public class ControllerTest1 implements Controller { public ModelAndView handleRequest() throws Exception { //返回一个模型视图对象 ModelAndView mv = new ModelAndView(); mv.addObject("msg","ControllerTest1"); mv.setViewName("test"); //设置视图名字 return mv; } }
5.2.2 第二种 : 通过ModelMap
ModelMap
@RequestMapping("/hello") public String hello(@RequestParam("username") String name, ModelMap model){ //封装要显示到视图中的数据 //相当于req.setAttribute("name",name); model.addAttribute("name",name); return "hello"; }
5.2.3 第三种 : 通过Model
Model
@RequestMapping("/ct2/hello") public String hello(@RequestParam("username") String name, Model model){ //封装要显示到视图中的数据 //相当于req.setAttribute("name",name); model.addAttribute("msg",name); System.out.println(name); return "test"; }
5.2.4 对比
就对于新手而言简单来说使用区别就是:
Model 只有寥寥几个方法只适合用于储存数据,简化了新手对于Model对象的操作和理解;
ModelMap 继承了 LinkedMap ,除了实现了自身的一些方法,同样的继承 LinkedMap 的方法和特性;
ModelAndView 可以在储存数据的同时,可以进行设置返回的逻辑视图,进行控制展示层的跳转。
当然更多的以后开发考虑的更多的是性能和优化,就不能单单仅限于此的了解。
5.3 前端页面取数据
如果是数据被放在model,modelAndView,modelMap中,并且页面是jsp页面,可以通过el表达式来获取放在其中的数据 ${}
6. 乱码处理
6.1 使用springMVC自带的过滤器
<!--2.配置SpringMVC的乱码过滤--> <filter> <filter-name>encoding</filter-name> <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class> <init-param> <param-name>encoding</param-name> <param-value>utf-8</param-value> </init-param> </filter> <filter-mapping> <filter-name>encoding</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
注意:filter配置的过滤器,确保了从前端到后端不为乱码。。。
有些极端情况下.这个过滤器对get的支持不好 .
处理方法 :
6.2 自定义过滤器
1. 修改tomcat配置文件 : 设置编码!
<Connector URIEncoding="utf-8" port="8080" protocol="HTTP/1.1" connectionTimeout="20000" redirectPort="8443" />
package com.kuang.filter; import javax.servlet.*; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletRequestWrapper; import javax.servlet.http.HttpServletResponse; import java.io.IOException; import java.io.UnsupportedEncodingException; import java.util.Map; /** * 解决get和post请求 全部乱码的过滤器 */ public class GenericEncodingFilter implements Filter { @Override public void destroy() { } @Override public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException { //处理response的字符编码 HttpServletResponse myResponse=(HttpServletResponse) response; myResponse.setContentType("text/html;charset=UTF-8"); // 转型为与协议相关对象 HttpServletRequest httpServletRequest = (HttpServletRequest) request; // 对request包装增强 HttpServletRequest myrequest = new MyRequest(httpServletRequest); chain.doFilter(myrequest, response); } @Override public void init(FilterConfig filterConfig) throws ServletException { } } //自定义request对象,HttpServletRequest的包装类 class MyRequest extends HttpServletRequestWrapper { private HttpServletRequest request; //是否编码的标记 private boolean hasEncode; //定义一个可以传入HttpServletRequest对象的构造函数,以便对其进行装饰 public MyRequest(HttpServletRequest request) { super(request);// super必须写 this.request = request; } // 对需要增强方法 进行覆盖 @Override public Map getParameterMap() { // 先获得请求方式 String method = request.getMethod(); if (method.equalsIgnoreCase("post")) { // post请求 try { // 处理post乱码 request.setCharacterEncoding("utf-8"); return request.getParameterMap(); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } else if (method.equalsIgnoreCase("get")) { // get请求 Map<String, String[]> parameterMap = request.getParameterMap(); if (!hasEncode) { // 确保get手动编码逻辑只运行一次 for (String parameterName : parameterMap.keySet()) { String[] values = parameterMap.get(parameterName); if (values != null) { for (int i = 0; i < values.length; i++) { try { // 处理get乱码 values[i] = new String(values[i] .getBytes("ISO-8859-1"), "utf-8"); } catch (UnsupportedEncodingException e) { e.printStackTrace(); } } } } hasEncode = true; } return parameterMap; } return super.getParameterMap(); } //取一个值 @Override public String getParameter(String name) { Map<String, String[]> parameterMap = getParameterMap(); String[] values = parameterMap.get(name); if (values == null) { return null; } return values[0]; // 取回参数的第一个值 } //取所有值 @Override public String[] getParameterValues(String name) { Map<String, String[]> parameterMap = getParameterMap(); String[] values = parameterMap.get(name); return values; } }
7. 常用注解
1、@Controller 和@Service 和@Repository
@Controller 用于标记在一个类上,使用它标记的类就是一个SpringMVC Controller 对象。分发处理器将会扫描使用了该注解的类的方法,并检测该方法是否使用了@RequestMapping 注解。@Controller 只是定义了一个控制器类,而使用@RequestMapping 注解的方法才是真正处理请求的处理器。
@Controller 标记在一个类上还不能真正意义上的说它就是SpringMVC 的一个控制器类,因为这个时候Spring 还不认识它。这个时候就需要我们把这个控制器类交给Spring 来管理。有两种方式可以管理:
<!--方式一--> <bean class="com.cqvie.handler.HelloWorld"/> <!--方式二--> < context:component-scan base-package = "com.cqvie" /> <!-- 路径写到controller的上一层 -->
注意:@Service 和@Repository 和@Controller功能类似,@Service注解用于Service层,@Repository注解用于dao层
2、@RequestMapping
1. RequestMapping是一个用来处理请求地址映射的注解,可用于类或方法上。用于类上,表示类中的所有响应请求的方法都是以该地址作为父路径。
2. 返回值会通过视图解析器解析为实际的物理视图,对于 InternalResourceViewResolver 视图解析器,会做如下的解析:
RequestMapping注解有六个属性:
1、 value
value:指定请求的实际地址;
2、method;
method: 指定请求的method类型, GET、POST、PUT、DELETE等,下面例子的@PathVariable后面讲解:
@RequestMapping(value = "/testRestPut/{id}", method = RequestMethod.PUT) public String testRestPut(@PathVariable int id) { System.out.println("testRestPut:" + id); return SUCCESS; }
3、consumes
consumes: 指定处理请求的提交内容类型(Content-Type),例如application/json, text/html;
4、produces
produces: 指定返回的内容类型,仅当request请求头中的(Accept)类型中包含该指定类型才返回;
5、params
params: 指定request中必须包含某些参数值是,才让该方法处理。
6、headers
headers: 指定request中必须包含某些指定的header值,才能让该方法处理请求。
3、@Resource和@Autowired
@Resource和@Autowired都是做bean的注入时使用,其实@Resource并不是Spring的注解,它的包是javax.annotation.Resource,需要导入,但是Spring支持该注解的注入。
1、共同点
两者都可以写在字段和setter方法上。两者如果都写在字段上,那么就不需要再写setter方法。
2、不同点
(1)@Autowired
@Autowired为Spring提供的注解,需要导入包org.springframework.beans.factory.annotation.Autowired;只按照byType注入。
public class HelloWorld{ // 下面两种@Autowired只要使用一种即可 @Autowired private UserDao userDao; // 用于字段上 @Autowired public void setUserDao(UserDao userDao) { // 用于属性的方法上 this.userDao = userDao; } }
@Autowired注解是按照类型(byType)装配依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。如果我们想使用按照名称(byName)来装配,可以结合@Qualifier注解一起使用。如下:
public class HelloWorld{ @Autowired @Qualifier("userDao") private UserDao userDao; }
(2)@Resource
@Resource默认按照ByName自动注入,由J2EE提供,需要导入包javax.annotation.Resource。@Resource有两个重要的属性:name和type,而Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以,如果使用name属性,则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不制定name也不制定type属性,这时将通过反射机制使用byName自动注入策略。
public class HelloWorld{ // 下面两种@Resource只要使用一种即可 @Resource(name="userDao") private UserDao userDao; // 用于字段上 @Resource(name="userDao") public void setUserDao(UserDao userDao) { // 用于属性的setter方法上 this.userDao = userDao; } }
注:最好是将@Resource放在setter方法上,因为这样更符合面向对象的思想,通过set、get去操作属性,而不是直接去操作属性。
4、@PathVariable
用于将请求URL中的模板变量映射到功能处理方法的参数上,即取出uri模板中的变量作为参数。如:
@Controller public class TestController { @RequestMapping(value="/user/{userId}/roles/{roleId}",method = RequestMethod.GET) public String getLogin(@PathVariable("userId") String userId, @PathVariable("roleId") String roleId){ System.out.println("User Id : " + userId); System.out.println("Role Id : " + roleId); return "hello"; } @RequestMapping(value="/product/{productId}",method = RequestMethod.GET) public String getProduct(@PathVariable("productId") String productId){ System.out.println("Product Id : " + productId); return "hello"; } @RequestMapping(value="/javabeat/{regexp1:[a-z-]+}", method = RequestMethod.GET) public String getRegExp(@PathVariable("regexp1") String regexp1){ System.out.println("URI Part 1 : " + regexp1); return "hello"; } }
5、@CookieValue
作用:用来获取Cookie中的值;
参数: value:参数名称 required:是否必须 defaultValue:默认值
使用案例:
/** * 获取 Session * JSESSIONID=411A032E02A2594698F6E3F4458B9CE4 */ @RequestMapping("/testCookieValue") public String testCookieValue(@CookieValue("JSESSIONID") String sessionId) { System.out.println("JSESSIONID = " + sessionId); return "success"; }
6、@RequestParam
@RequestParam用于将请求参数区数据映射到功能处理方法的参数上,用例:
/** * @RequestParam("id") 带参映射 * @param id * @return */ @RequestMapping("/testRequestParam") public String testRequestParam(@RequestParam("id") int id) { System.out.println("testRequestParam " + id); return "success"; }
7、@SessionAttributes
@SessionAttributes即将值放到session作用域中,写在class上面。
@SessionAttributes 除了可以通过属性名指定需要放到会话中的属性外(value 属性值),
还可以通过模型属性的对象类型指定哪些模型属性需要放到会话中(types 属性值),用例:
package com.cqvie.yjq; import java.util.Map; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.SessionAttributes; import com.cqvie.model.User; @SessionAttributes(value = {"user"}, types = {String.class}) @RequestMapping("/springmvc") @Controller public class SessionAttributesTest { /** * @SessionAttributes * 除了可以通过属性名指定需要放到会话中的属性外(value 属性值), * 还可以通过模型属性的对象类型指定哪些模型属性需要放到会话中(types 属性值)。 * 注意: 该注解只能放在类的上面,不能放在方法上面 * * @return */ @RequestMapping("/testSessionAttributes") public String testSessionAttributes(Map<String, Object> map) { User user = new User(1, "刘邦", "qwe", "123", "辽宁"); map.put("user", user); map.put("school", "重庆"); return "success"; } }
8、@ModelAttribute
代表的是:该Controller的所有方法在调用前,先执行此@ModelAttribute方法,可用于注解和方法参数中,可以把这个@ModelAttribute特性,应用在BaseController当中,所有的Controller继承BaseController,即可实现在调用Controller时,先执行@ModelAttribute方法。
package com.cqvie.yjq; import java.util.Map; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.ModelAttribute; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import com.cqvie.model.User; @Controller @RequestMapping("/springmvc") public class ModelAttributeTest { private static final String SUCCESS = "success"; /** * 1.有 @ModelAttribute 标记的方法,会在每个目标方法执行之前被 SpringMVC 调用 * 2.@ModelAttribute注解也可以修饰目标方法POJO类形的入参,其value的属性值有如下作用: * 1)SpringMVC会使用value属性值在implicitModel中查找对应的对象,若存在则直接传入到目标方法的入参中 * 2)SpringMVC会以value为key,POJO类型的对象为value,存入的request中 * * @param id * @param map */ @ModelAttribute public void getUser(@RequestParam(value = "id", required = false) int id, Map<String, Object> map) { //模拟数据库中获取对象 User user = new User(1, "刘邦", "123", "023", "重庆"); System.out.println("从数据库中获取一个对象:" + user); map.put("abc", user); } /** * 运行流程: * 1.执行@ModelAttribute注解修饰的方法,从数据库中取出对象,把对象放入Map中,键为:user; * 2.SpringMVC从Map中取出User对象,并把表单的请求参数赋值给该User对象的对应属性; * 3.SpringMVC把上述对象传入目标方法的参数。 * * 注意:在@ModelAttribute修饰的方法中,放入到Map时的键需要和目标方法入参类型的第一个字母小写的字符串一致 * * @param user * @return */ @RequestMapping("/testModelAttribute") public String testModelAttribute(@ModelAttribute("abc") User user) { System.out.println("修改:" + user); return SUCCESS; } }
9、@ResponseBody
作用: 该注解用于将Controller的方法返回的对象,通过适当的HttpMessageConverter转换为指定格式后,写入到Response对象的body数据区。
使用时机:返回的数据不是html标签的页面,而是其他某种格式的数据时(如json、xml等)使用;
8. json
主要有两个工具
jackson
fastjson
9. Ajax
10. springMVC拦截器(interceptor)
10.1 概述
SpringMVC的处理器拦截器类似于Servlet开发中的过滤器Filter,用于对处理器进行预处理和后处理。开发者可以自己定义一些拦截器来实现特定的功能。
过滤器与拦截器的区别:拦截器是AOP思想的具体应用。
过滤器
servlet规范中的一部分,任何java web工程都可以使用
在url-pattern中配置了/*之后,可以对所有要访问的资源进行拦截
拦截器:
1. 拦截器是SpringMVC框架自己的,只有使用了SpringMVC框架的工程才能使用
2. 拦截器只会拦截访问的控制器方法, 如果访问的是jsp/html/css/image/js是不会进行拦截的
10.2 自定义拦截器
那如何实现拦截器呢?
想要自定义拦截器,必须实现 HandlerInterceptor 接口。
1.编写web.xml文件和spring-servlet.xml文件
2.编写一个拦截器
public class MyInterceptor implements HandlerInterceptor { //return true 执行下一个拦截器,放行 @Override public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { System.out.println("=============处理前============"); return true; } //下面这两个方法主要写拦截日志 @Override public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception { System.out.println("=============处理后============"); } @Override public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception { System.out.println("=============清理============"); } }
3. 在springvc-serlet.xml文件中配置拦截器
<mvc:interceptors>
<mvc:interceptor>
<mvc:mapping path="/**"/>
<bean class="com.zhx.config.MyInterceptor"/>
</mvc:interceptor>
</mvc:interceptors>
4. 编写controller接收请求
package com.kuang.controller; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.ResponseBody; //测试拦截器的控制器 @Controller public class InterceptorController { @RequestMapping("/interceptor") @ResponseBody public String testFunction() { System.out.println("控制器中的方法执行了"); return "hello"; } }
5. 前端
<a href="${pageContext.request.contextPath}/interceptor">拦截器测试</a>
6. 测试
10.3 拦截器用处
利用拦截器可以对用户发送的请求进行验证,是否满足我们预先设置好的逻辑。。。例如可以用与是否登录的验证,因为有很多操作,要求用户进行登录,如果用户没有登录,进行相关请求,一定是失败的。。。
10.4 验证用户是否登录
拦截器示例,springmvc-07
public class LoginInterceptor implements HandlerInterceptor { public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception { HttpSession session = request.getSession(); //放行:判断什么情况下登录 //登录页面也会放行 if (request.getRequestURI().contains("goLogin")) { return true; }
//登录的时候,什么信息都没有,但是需要放行 if (request.getRequestURI().contains("login")) { return true; } //已经登录 if (session.getAttribute("userLoginInfo") != null) { return true; } //判断什么情况下没有登录 request.getRequestDispatcher("/WEB-INF/jsp/login.jsp").forward(request,response); return false; } }
1、有一个登陆页面,需要写一个controller访问页面。
2、登陆页面有一提交表单的动作。需要在controller中处理。判断用户名密码是否正确。如果正确,向session中写入用户信息。返回登陆成功。
3、拦截用户请求,判断用户是否登陆。如果用户已经登陆。放行, 如果用户未登陆,跳转到登陆页面
11. 文件上传和下载
11.1 概述
文件上传是项目开发中最常见的功能之一,springMVC可以很好的支持文件上传,但是
SpringMVC上下文中默认没有装配MultipartResolver,因此默认情况下其不能处理文件上传工作。如果想使用Spring的文件上传功能,则需要在上下文中配置MultipartResolver。
前端表单要求:为了能上传文件,必须将表单的method设置为POST,并将enctype设置为multipart/form-data。只有在这样的情况下,浏览器才会把用户选择的文件以二进制数据发送给服务器;
11.2 对表单中的enctype属性做个详细的说明:
1. application/x-www=form-urlencoded:默认方式,只处理表单域中的value 属性值,采用这种编码方式的表单会将表单域中的值处理成URL编码方式。
2. multipart/form-data:这种编码方式会以二进制流的方式来处理表单数据,这种编码方式会把文件域指定文件的内容也封装到请求参数中,不会对字符编码。
3. text/plain:除了把空格转换为“+”号外,其他字符都不做编码处理,这种方式适用直接通过表单发送邮件。
<form action="${pageContext.request.contextPath}/upload" enctype="multipart/form-data" method="post"> <input type="file" name="file"/> <input type="submit" value="upload"> </form>
一旦设置了enctype为multipart/form-data,浏览器即会采用二进制流的方式来处理表单数据,而对于文件上传的处理则涉及在服务器端解析原始的HTTP响应。在2003年,Apache Software Foundation发布了开源的Commons FileUpload组件,其很快成为Servlet/JSP程序员上传文件的最佳选择。
- Servlet3.0规范已经提供方法来处理文件上传,但这种上传需要在Servlet中完成。
- 而Spring MVC则提供了更简单的封装。
- Spring MVC为文件上传提供了直接的支持,这种支持是用即插即用的MultipartResolver实现的。
- Spring MVC使用Apache Commons FileUpload技术实现了一个MultipartResolver实现类:
- CommonsMultipartResolver。因此,SpringMVC的文件上传还需要依赖Apache Commons FileUpload的组件。
11.3 文件上传
11.3.1 导入依赖
导入文件上传的jar包,commons-fileupload , Maven会自动帮我们导入他的依赖包 commons-io包;
<dependency> <groupId>commons-fileupload</groupId> <artifactId>commons-fileupload</artifactId> <version>1.3.3</version> </dependency>
<dependency> <groupId>javax.servlet</groupId> <artifactId>javax.servlet-api</artifactId> <version>4.0.1</version> </dependency>
11.3.2 配置bean:multipartResolver
<!--文件上传配置--> <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"> <!-- 请求的编码格式,必须和jSP的pageEncoding属性一致,以便正确读取表单的内容,默认为ISO-8859-1 --> <property name="defaultEncoding" value="utf-8"/> <!-- 上传文件大小上限,单位为字节(10485760=10M) --> <property name="maxUploadSize" value="10485760"/> <property name="maxInMemorySize" value="40960"/> </bean>
11.3.3 前端页面
<form action="/upload" enctype="multipart/form-data" method="post"> <input type="file" name="file"/> <input type="submit" value="upload"> </form>
11.3.4 controller
12. CORS 跨域问题解决
12.1 @CrossOrigin
注意: 该注解可以在方法和类上使用,除此之外,还有许多其他的参数。。
The following example specifies a certain domain and sets maxAge
to an hour:
@CrossOrigin(origins = "https://domain2.com", maxAge = 3600) @RestController @RequestMapping("/account") public class AccountController { @GetMapping("/{id}") public Mono<Account> retrieve(@PathVariable Long id) { // ... } @DeleteMapping("/{id}") public Mono<Void> remove(@PathVariable Long id) { // ... } }
You can use @CrossOrigin
at both the class and the method level, as the following example shows:
@CrossOrigin(maxAge = 3600) @RestController @RequestMapping("/account") public class AccountController { @CrossOrigin("https://domain2.com") @GetMapping("/{id}") public Mono<Account> retrieve(@PathVariable Long id) { // ... } @DeleteMapping("/{id}") public Mono<Void> remove(@PathVariable Long id) { // ... } }
12.2 global configuration
12.2.1 使用xml方式实现
<!-- 允许跨域请求--> <mvc:cors> <mvc:mapping path="/**" allowed-origins="*" allowed-methods="POST,GET,OPTIONS,DELETE,PUT,PATCH" allowed-headers="Content-Type,Access-Control-Allow-Headers,Authorization,X-Requested-With" exposed-headers="Content-Type,Access-Control-Allow-Headers,Authorization,X-Requested-With" allow-credentials="true" max-age="30" /> </mvc:cors>
12.2.2 使用java加注解实现
@Configuration @EnableWebFlux public class WebConfig implements WebFluxConfigurer { @Override public void addCorsMappings(CorsRegistry registry) { registry.addMapping("/api/**") .allowedOrigins("https://domain2.com") .allowedMethods("PUT", "DELETE") .allowedHeaders("header1", "header2", "header3") .exposedHeaders("header1", "header2") .allowCredentials(true).maxAge(3600); // Add more mappings... } }
12.2.3 CrosFilter
13. springmvc.xml 配置文件解析
(1). 扫描包下被注解的类
<context:component-scan base-package="com.zhx.controller" />
(2) <mvc:annotation-driven /> 标签
1.这个配置会自动注册了一个 RequestMappingHandlerMapping、一个RequestMappingHandlerAdapter、以及一个ExceptionHandlerExceptionResolver 以支持使用注解。
2. 并提供一系列的功能: 数据绑定,数字和日期format等
(3) <mvc:default-servlet-handler /> 标签
处理静态资源
(3) 静态资源映射 (前后端不分离使用)
<mvc:resources mapping="/img/**" location="/statics/img/"/>
<mvc:resources mapping="/css/**" location="/statics/css/"/>
<mvc:resources mapping="/js/**" location="/statics/js/"/>
<mvc:resources mapping="/layui/**" location="/statics/layui/"/>
(4) 跨域的处理
<!-- 允许跨域请求--> <mvc:cors> <mvc:mapping path="/**" allowed-origins="*" allowed-methods="POST,GET,OPTIONS,DELETE,PUT,PATCH" allowed-headers="Content-Type,Access-Control-Allow-Headers,Authorization,X-Requested-With" exposed-headers="Content-Type,Access-Control-Allow-Headers,Authorization,X-Requested-With" allow-credentials="true" max-age="30" /> </mvc:cors>
(5) 视图解析器 (前后端不分离的时候使用)
<!-- 3.配置jsp 显示ViewResolver视图解析器 --> <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"> <property name="viewClass" value="org.springframework.web.servlet.view.JstlView" /> <!-- --> <property name="prefix" value="/WEB-INF/html/" /> <property name="suffix" value=".html" /> </bean>
14. View Technologies (前后端不分离)
要么是使用jsp,要么使用 Thymeleaf , 两者都可以说是一种模板引擎。。。
其他的模板引擎还有
FreeMarker,