SpringMVC
SpringMVC是一种轻量级的、基于MVC的Web层应用框架。
通过一套 MVC 注解,让 POJO 成为处理请求的控制器,而无须实现任何接口。
采用了松散耦合可插拔组件结构,比其他 MVC 框架更具扩展性和灵活性。
优点:
1、天生与Spring框架集成,如:(IOC,AOP)
2、支持Restful风格
3、支持灵活的URL到页面控制器的映射
4、非常容易与其他视图技术集成,如:Velocity、FreeMarker等等
5、因为模型数据不存放在特定的API里,而是放在一个Model里(Map数据结构实现,因此很容易被其他框架使用)
6、非常灵活的数据验证、格式化和数据绑定机制、能使用任何对象进行数据绑定,
7、更加简单、强大的异常处理
8、对静态资源的支持
9、支持灵活的本地化、主题等解析
常用主要组件
① DispatcherServlet:前端控制器
② Controller:处理器/页面控制器,做的是MVC中的C的事情,但控制逻辑转移到前端控制器了,用于对请求进行处理
③ HandlerMapping:请求映射到处理器,找谁来处理,如果映射成功返回一个HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象、多个HandlerInterceptor拦截器对象)
④ View Resolver : 视图解析器,找谁来处理返回的页面。把逻辑视图解析为具体的View,进行这种策略模式,很容易更换其他视图技术;如InternalResourceViewResolver将逻辑视图名映射为JSP视图
⑤ LocalResolver:本地化、国际化
⑥ MultipartResolver:文件上传解析器
⑦ HandlerExceptionResolver:异常处理器
流程分析
基本步骤:
① 客户端请求提交到DispatcherServlet
② 由DispatcherServlet控制器查询一个或多个HandlerMapping,找到处理请求的Controller
③ DispatcherServlet将请求提交到Controller(也称为Handler)
④ Controller调用业务逻辑处理后,返回ModelAndView
⑤ DispatcherServlet查询一个或多个ViewResoler视图解析器,找到ModelAndView指定的视图
⑥ 视图负责将结果显示到客户端
标准的 HTTP 请求报头
@RequestMapping
1、使用@RequestMapping 注解来映射请求的 URL
@RequestMapping可以应用的地方
@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Mapping
public @interface RequestMapping {…}
请求的方式有
public enum RequestMethod { GET, HEAD, POST, PUT, PATCH, DELETE, OPTIONS, TRACE }
@RequestMapping可以为控制器指定可以处理哪些 URL 请求,将该注解中的 value 属性值映射成URL,客户端可以通过该URL请求到指定类中的方法。
1)在控制器的类定义或方法定义处都可标注 @RequestMapping
① 标记在类上:提供初步的请求映射信息。相对于 WEB 应用的根目录
② 标记在方法上:提供进一步的细分映射信息。相对于标记在类上的 URL。
2)若类上未标注 @RequestMapping,则方法处标记的 URL 相对于 WEB 应用的根目录
3)作用:DispatcherServlet 截获请求后,就通过控制器上 @RequestMapping 提供的映射信息确定请求所对应的处理方法。
@RequestMapping属性
value:指定URL路径
method:指定请求方式
params:指定请求参数
headers:指定请求头信息
映射请求参数、请求方式或请求头
1)@RequestMapping 除了可以使用请求 URL 映射请求外,还可以使用请求方法、请求参数及请求头来精确映射对应请求
2)@RequestMapping 的 value【重点】、method【重点】、params【了解】 及 heads【了解】 分别表示请求 URL、请求方式、请求参数及请求头的映射条件,他们之间是与的关系,联合使用多个条件可让请求映射更加精确化。即:需满足所有映射条件才可匹配到对应方法
3)params 和 headers支持简单的表达式:
param1: 表示请求必须包含名为 param1 的请求参数
!param1: 表示请求不能包含名为 param1 的请求参数
param1 != value1: 表示请求包含名为 param1 的请求参数,但其值不能为 value1
{"param1=value1", "param2"}: 请求必须包含名为 param1 和param2 的两个请求参数,且 param1 参数的值必须为 value1
Ant 路径风格
Ant 风格资源地址支持 3 种匹配符:【了解】
?:匹配文件名中的一个字符
*:匹配文件名中的任意字符
**:** 匹配多层路径
/user/*/**/createUser?? 匹配 /user/xxx/多层/createUserXX
REST
REST是什么?因为REST的内涵非常丰富,所以很难用一两句话解释清楚这个问题。首先,REST是Web自身的架构风格。
参考资料:理解本真的REST架构风格
REST:即 Representational State Transfer。(资源)表现层状态转化。是目前最流行的一种互联网软件架构。它结构清晰、符合标准、易于理解、扩展方便
资源(Resources):资源是一种看待服务器的方式。是网络上的一个实体,可以是一段文本、一张图片,可以用一个URI(统一资源定位符,独一无二的识别符)指向它,获取这个资源,访问它的URI就可以了
表现层:资源的表述(Representation)是一段对于资源在某个特定时刻的状态的描述,即把资源具体呈现出来的形式, 比如,文本可以用 txt 、JSON 格式表现,甚至可以采用二进制格式。
状态转化(State Transfer):状态转移说的是:在客户端和服务器端之间转移(transfer)代表资源状态的表述。通过转移和操作资源的表述,来间接实现操作资源的目的。如:每发出一个请求,就代表了客户端和服务器的一次交互过程。HTTP协议,是一个无状态协议,即所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生“状态转化”。而这种转化是建立在表现层之上的,所以就是 “表现层状态转化”。
统一接口(Uniform Interface)REST要求,必须通过统一的接口来对资源执行各种操作。对于每个资源只能执行一组有限的操作。例如:HTTP/1.1协议定义了一个操作资源的统一接口。REST还要求,对于资源执行的操作,其操作语义必须由HTTP消息体之前的部分完全表达,不能将操作语义封装在HTTP消息体内部。这样做是为了提高交互的可见性
超文本驱动(Hypertext Driven)将Web应用看作是一个由很多状态(应用状态)组成的有限状态机。资源之间通过超链接相互关联,超链接既代表资源之间的关系,也代表可执行的状态迁移。即:客户端应该依赖的是超媒体的状态迁移语义,而不应该对于是否存在某个URI或URI的某种特殊构造方式作出假设。一切都有可能变化,只有超媒体的状态迁移语义能够长期保持稳定。
具体对于HTTP来说,就是 HTTP 协议里面对应的四种常用基本操作:GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE 用来删除资源。应使用由客户端定义的请求方式指定对应的某种操作,而不应该通过某种特殊构造方式进行指定
HiddenHttpMethodFilter过滤器
浏览器 form 表单只支持 GET 与 POST 请求,HiddenHttpMethodFilter 可以将POST请求转换为标准的 http 方法以达到REST风格
使用步骤
1. 必须将form表单中的method设置为POST
2. 提交表单时,必须提交"_method"参数,一般使用隐藏域
原因:HiddenHttpMethodFilter过滤器将HttpServletRequest中的getMethod()方法,重写啦。
<!-- 配置处理请求方式--> <filter> <filter-name>hiddenHttpMethodFilter</filter-name> <filter-class>org.springframework.web.filter.HiddenHttpMethodFilter</filter-class> </filter> <filter-mapping> <filter-name>hiddenHttpMethodFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
处理请求数据
Spring MVC 框架会将 HTTP 请求的信息绑定到相应的方法入参中,并根据方法的返回值类型做出相应的后续处理。
可以对方法及方法入参标注相应的注解( @PathVariable 、@RequestParam、@RequestHeader 等)
@PathVariable请求占位符
是 Spring3.0 新增的功能,通过 @PathVariable 可以将 URL 中占位符参数绑定到控制器处理方法的入参中
//@PathVariable 注解可以将请求URL路径中的请求参数,传递到处理请求方法的入参中 // 浏览器的请求为: testPathVariable/1001 @RequestMapping(value="/testPathVariable/{id}",method=RequestMethod.DELET) public String testPathVariable(@PathVariable("id") Integer id) {}
@RequestParam请求参数
如果请求参数与形参不一致时,可以使用@RequestParam注解实现获取参数值
书写位置:标注在方法的参数中,springMVC默认会将请求参数注入(绑定)到方法形参中(两个参数名一致)
一旦使用该注解,必须为相应参数传参数。如果未传参,会报错:400,因为required默认为 true,
value:用于映射请求参数名称
required:是否必须。默认为 true, 表示请求参数中必须包含对应的参数,若不存在,将抛出异常
defaultValue: 默认值,当没有传递参数时使用该值作为默认值,不设默认为 null
@RequestMapping(value="/testRequestParam?username=guigu&age=10") public String testRequestParam( @RequestParam(value="username") String username, @RequestParam(value="age",required=false,defaultValue="0") int age){ return "success"; }
@RequestHeader 请求头
获取请求头信息,请求头包含了若干个属性,服务器可据此获知客户端的信息,通过 @RequestHeader 即可将请求头中的属性值绑定到处理方法的入参中
@CookieValue
获取指定的Cookie信息,可让处理方法入参绑定某个 Cookie 值
使用POJO作为参数
Spring MVC 会按请求参数名和 POJO 属性名进行自动匹配,自动为该对象填充属性值。支持级联属性。
@RequestMapping(value = "/emps",method = RequestMethod.PUT) public String updateEmp(Employee employee){ employeeDao.save(employee); return "redirect:/emps"; } //Spring MVC 会按请求参数名和 Employee 属性名进行自动匹配, 自动为该对象填充属性值。支持级联属性
配置字符编码过滤器
<!-- 处理POST请求和响应乱码--> <filter> <filter-name>characterEncodingFilter</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> <init-param> <param-name>forceEncoding</param-name> <param-value>true</param-value> </init-param> </filter> <filter-mapping> <filter-name>characterEncodingFilter</filter-name> <url-pattern>/*</url-pattern> </filter-mapping>
GET请求乱码
- GET请求参数是在地址后面的。我们需要修改tomcat的配置文件。需要在server.xml文件修改Connector标签,添加URIEncoding="utf-8"属性。
使用Servlet原生API
/** * 可以使用 Serlvet 原生的 API 作为目标方法的参数 具体支持以下类型 * HttpServletRequest * HttpServletResponse * HttpSession * java.security.Principal * Locale
* InputStream * OutputStream * Reader * Writer */ @RequestMapping("/testServletAPI") public void testServletAPI(HttpServletRequest request,HttpServletResponse response, Writer out) throws IOException { System.out.println("testServletAPI, " request ", " response); out.write("hello springmvc"); }
处理响应数据
2、返回值会通过视图解析器解析为实际的物理视图
输出模型数据类型
1) ModelAndView: 作为返回值类型,响应数据:处理方法返回值类型为 ModelAndView 时, 方法体即可通过该对象添加模型数据
2) String: 作为返回值类型,即为视图信息直接找字符串映射 URL 路径,转发或重定向
3) Map 或 Model: 作为参数,响应数据:入参为 Model、ModelMap 或 Map,处理方法返回时,Map 中的数据会自动添加到模型中。
ModelAndView
控制器处理方法的返回值如果为 ModelAndView, 则其既包含视图信息,也包含模型数据信息。
1) 两个重要的成员变量:
private Object view; 【视图信息】
private ModelMap model; 【模型数据】
2)添加模型数据:
MoelAndView addObject(String attributeName, Object attributeValue) 【设置模型数据】
ModelAndView addAllObject(Map<String, ?> modelMap)
4)设置视图:
void setView(View view) 【设置视图对象】
void setViewName(String viewName) 【设置视图名字】
5)获取模型数据
protected Map<String, Object> getModelInternal() 【获取模型数据】
public ModelMap getModelMap()
public Map<String, Object> getModel()
@RequestMapping("/testModelAndView") public ModelAndView testModelAndView(){ String viewName = "success"; ModelAndView mv = new ModelAndView(viewName); mv.addObject("time",new Date().toString()); //实质上存放到request域中 return mv; }
ModelAndView 底层工作原理,不论控制器返回一个String,ModelAndView,View都会转换为ModelAndView对象,将数据放到request域中,再通过转发实现页面跳转
Map Model
Spring MVC 在内部使用了一个 org.springframework.ui.Model 接口存储模型数据
Spring MVC 在调用方法前会创建一个隐含的模型对象作为模型数据的存储容器。
如果方法的入参为 Map 或 Model 类型,Spring MVC 会将隐含模型的引用传递给这些入参。
在方法体内,开发者可以通过这个入参对象访问到模型中的所有数据,也可以向模型中添加新的属性数据
//目标方法的返回类型也可以是一个Map类型参数(也可以是Model,或ModelMap类型) @RequestMapping("/testMap") public String testMap(Map<String, Object> map) { //【重点】 System.out.println(map.getClass().getName()); //org.springframework.validation.support.BindingAwareModelMap map.put("names", Arrays.asList("Tom", "Jerry", "Kite")); return "success"; }
注意问题:Map集合的泛型,key为String,Value为Object,而不是String
由源码可知:不论用那个类型作为数据模型,其内部都会转化为BindingAwareModelMap类型使其指向同一map对象
BindingAwareModelMap底层支持两种接口(Map&Model)推荐使用 Map 便于框架移植