回顾 MVC
什么是 MVC
MVC是模型(Model)、视图(View)、控制器(Controller)的简写,是一种软件设计规范
是将业务逻辑、数据、显示分离的方法来组织代码
MVC主要作用是降低了视图与业务逻辑间的双向偶合
MVC不是一种设计模式,MVC是一种架构模式
Model(模型) :数据模型,提供要展示的数据,因此包含数据和行为,可以认为是领域模型或JavaBean组件(包含数据和行为),不过现在一般都分离开来:Value Object(数据Dao) 和 服务层(行为Service)。也就是模型提供了模型数据查询和模型数据的状态更新等功能,包括数据和业务。
View(视图) :负责进行模型的展示,一般就是我们见到的用户界面,客户想看到的东西。
Controller(控制器) :接收用户请求,委托给模型进行处理(状态改变),处理完毕后把返回的模型数据返回给视图,由视图负责展示。 也就是说控制器做了个调度员的工作。
最典型的MVC就是JSP + servlet + javabean的模式。
MVC框架要做哪些事
将url映射到java类或java类的方法
封装用户提交的数据
处理请求—调用相关的业务处理—封装响应数据
将响应的数据进行渲染 .jsp / html 等表示层数据
说明:
常见的服务器端MVC框架有:Struts、Spring MVC、ASP.NET MVC、Zend Framework、JSF;常见前端MVC框架:vue、angularjs、react、backbone;由MVC演化出了另外一些模式如:MVP、MVVM 等等….
Spring MVC 简介
概述
SpringMVC是Spring Framework的一部分,是基于Java实现MVC的轻量级Web框架。
查看官方文档:https://docs.spring.io/spring/docs/5.2.0.RELEASE/spring-framework-reference/web.html#spring-web
SpringMVC的特点:
轻量级,简单易学
高效 , 基于请求响应的MVC框架
与Spring兼容性好,无缝结合
约定优于配置
功能强大:RESTful、数据验证、格式化、本地化、主题等
简洁灵活
中心控制器
Spring的web框架围绕DispatcherServlet [ 调度Servlet ] 设计。 DispatcherServlet的作用是将请求分发到不同的处理器。从Spring 2.5开始,使用Java 5或者以上版本的用户可以采用基于注解的Controller声明方式。
Spring MVC框架像许多其他MVC框架一样, 以请求为驱动 , 围绕一个中心Servlet分派请求及提供其他功能 ,DispatcherServlet是一个实际的Servlet (它继承自HttpServlet 基类) 。
当发起请求时被前置的控制器拦截到请求,根据请求参数生成代理请求,找到请求对应的实际控制器,控制器处理请求,创建数据模型,访问数据库,将模型响应给中心控制器,控制器使用模型与视图渲染视图结果,将结果返回给中心控制器,再将结果返回给请求者。
Spring MVC 执行原理
图为SpringMVC的一个较完整的流程图,实线表示SpringMVC框架提供的技术,不需要开发者实现,虚线表示需要开发者实现。
简要分析执行流程
DispatcherServlet表示前置控制器,是整个SpringMVC的控制中心。用户发出请求,DispatcherServlet接收请求并拦截请求。
假设请求的url为 : http://localhost:8080/SpringMVC/hello 该url可拆分成三部分:
通过分析,如上url表示为:请求位于服务器localhost:8080上的SpringMVC站点的hello控制器。
HandlerMapping为处理器映射。DispatcherServlet调用HandlerMapping,HandlerMapping根据请求url查找Handler。
HandlerExecution表示具体的Handler,其主要作用是根据url查找控制器,如上url被查找控制器为:hello。
HandlerExecution将解析后的信息传递给DispatcherServlet,如解析控制器映射等。
HandlerAdapter表示处理器适配器,其按照特定的规则去执行Handler。
Handler让具体的Controller执行。
Controller将具体的执行信息返回给HandlerAdapter,如ModelAndView。
HandlerAdapter将视图逻辑名或模型传递给DispatcherServlet。
DispatcherServlet调用视图解析器(ViewResolver)来解析HandlerAdapter传递的逻辑视图名。
视图解析器将解析的逻辑视图名传给DispatcherServlet。
DispatcherServlet根据视图解析器解析的视图结果,调用具体的视图。
最终视图呈现给用户。
Hello Spring MVC
配置web.xml
注册DispatcherServlet
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns ="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version ="4.0" > <servlet > <servlet-name > springmvc</servlet-name > <servlet-class > org.springframework.web.servlet.DispatcherServlet</servlet-class > <init-param > <param-name > contextConfigLocation</param-name > <param-value > classpath:springmvc-servlet.xml</param-value > </init-param > <load-on-startup > 1</load-on-startup > </servlet > <servlet-mapping > <servlet-name > springmvc</servlet-name > <url-pattern > /</url-pattern > </servlet-mapping > <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 > forceRequestEncoding</param-name > <param-value > true</param-value > </init-param > </filter > <filter-mapping > <filter-name > CharacterEncodingFilter</filter-name > <url-pattern > /*</url-pattern > </filter-mapping > </web-app >
注意点 :
/
不会匹配 .jsp
/*
匹配所有的请求,包括 .jsp
*.jsp
由Tomcat负责处理,不需要SpringMVC拦截处理。因此常用 /
原因分析
所有JavaWeb项目里的web.xml都继承自Tomcat的父web.xml,其内配置了一个默认的DefaultServlet :
1 2 3 4 5 6 7 8 9 10 11 12 <servlet > <servlet-name > defaultServlet</servlet-name > <servlet-class > org.apache.catalina.servlets.DefaultServlet</servlet-class > <init-param > <param-name > readonly</param-name > <param-value > false</param-value > </init-param > </servlet > <servlet-mapping > <servlet-name > defaultServlet</servlet-name > <url-pattern > /</url-pattern > </servlet-mapping >
DefaultServlet 是Tomcat用于处理静态资源(除了jsp和servlet之外都是静态资源)的处理器,当DefaultServlet 判断得知url中访问的是静态资源文件时,就会直接去服务器目录下找该资源是否存在。其配置了url-pattern:/
而SpringMVC中我们同样配置了url-pattern:/
,因此会覆盖Tomcat中的DefaultServlet,使得静态资源不能被Tomcat里的DefaultServlet所处理,只能被我们配置的DispatcherServlet拦截处理。静态资源被DispatcherServlet拦截时会判断哪个方法的@RequestMapping
是这个静态资源,显然并不能找到,因此无法正常显示。
*.jsp
处理问题:Tomcat里的web.xml中配置了对jsp文件的处理,该处理器将处理jsp文件:
1 2 3 4 5 <servlet-mapping > <servlet-name > jsp</servlet-name > <url-pattern > *.jsp</url-pattern > <url-pattern > *.jspx</url-pattern > </servlet-mapping >
若我们在SpringMVC配置中只添加url-pattern:/
而没有添加url-pattern:*.jsp
,则将只覆盖父web.xml里的url-pattern:/
(处理静态资源),并没有覆盖url-pattern:*.jsp
。因此这种情况下,遇到jsp文件,则由Tomcat里的默认处理器处理;遇到普通请求,由DispatcherServlet处理;遇到静态资源,因覆盖了Tomcat,则无法处理。
若配置url-pattern:/*
,则所有请求资源都将被拦截处理。
因此,若想在使用Spring MVC的DispatcherServlet 的同时仍能处理静态资源,则需要添加:
1 <mvc:default-servlet-handler />
其能将Spring MVC无法处理的请求交给Tomcat默认的Servlet处理,让Spring MVC不处理静态资源。
添加 Spring MVC 配置文件
让IoC的注解生效
静态资源过滤 :HTML,JS ,CSS ,图片,视频 ……
MVC的注解驱动
配置视图解析器
在resource目录下添加SpringMVC的配置文件,名称:springmvc-servlet.xml : [servletname]-servlet.xml。配置的形式与Spring容器配置基本类似,为了支持基于注解的IoC,设置了自动扫描包的功能,具体配置信息如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:context ="http://www.springframework.org/schema/context" xmlns:mvc ="http://www.springframework.org/schema/mvc" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd" > <context:component-scan base-package ="com.zhao.controller" /> <mvc:default-servlet-handler /> <mvc:annotation-driven /> <bean class ="org.springframework.web.servlet.view.InternalResourceViewResolver" id ="internalResourceViewResolver" > <property name ="prefix" value ="/WEB-INF/jsp/" /> <property name ="suffix" value =".jsp" /> </bean > </beans >
在视图解析器中我们把所有的视图都存放在/WEB-INF/目录下,这样可以保证视图安全,因为这个目录下的文件,客户端不能直接访问。
<mvc:default-servlet-handler />
将在SpringMVC上下文中定义一个DefaultServletHttpRequestHandle
:
它会对进入DispatcherSevlet的请求进行筛查,如果发现是没有经过映射的请求,就将该请求交由WEB应用服务器默认的Servlet处理(将SpringMVC无法处理的请求交给Tomcat默认的Servlet处理)
会过滤静态资源文件 :如果不是静态资源的请求,才由DispatcherServlet继续处理。
创建 Controller
编写一个Java控制类: com.zhao.controller.HelloController
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 package com.zhao.controller;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.web.bind.annotation.RequestMapping;@Controller @RequestMapping("/HelloController") public class HelloController { @RequestMapping("/hello") public String sayHello (Model model) { model.addAttribute("msg" ,"hello,SpringMVC" ); return "hello" ; } }
@Controller
是为了让Spring IoC容器初始化时自动扫描到;
@RequestMapping
是为了映射请求路径,这里因为类与方法上都有映射所以访问时应该是/HelloController/hello
;
方法中声明Model
类型的参数是为了把Action中的数据带到请求域中;
方法返回的结果是视图的名称hello
,加上配置文件中的前后缀变成WEB-INF/jsp/hello.jsp
创建视图层
在WEB-INF/ jsp目录中创建hello.jsp,视图可以直接取出并展示从Controller带回的信息;可以通过EL表示取出Model中存放的值,或者对象;
1 2 3 4 5 6 7 8 9 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html> <head> <title>SpringMVC</title> </head> <body> ${msg} </body> </html>
小结
实现步骤:
新建一个web项目
导入相关jar包
编写web.xml , 注册DispatcherServlet
编写springmvc配置文件
创建对应的控制类Controller
最后完善前端视图和Controller之间的对应
测试运行调试.
使用SpringMVC必须配置的三大件:
处理器映射器、处理器适配器、视图解析器
通常,我们只需要手动配置视图解析器 ,而处理器映射器 和处理器适配器 只需要开启注解驱动 即可,而省去了大段的xml配置
常用注解
汇总
1 2 3 4 5 6 7 8 9 @RequestMapping(value = "/test", method = {RequestMethod.POST}, params = {"username"}) public String test ( @RequestParam(value = "username", required = false, defaultValue = "zhangsan") String username, @RequestHeader(value = "User-Agent", required = false, defaultValue = " ") String userAgent, @CookieValue("JSESSIONID", required = false, defaultValue = " ") String jid) { return "success" ; }
@RequestMapping
@RequestMapping
用于请求映射,执行匹配的方法
method 参数:限定请求方式
1 2 3 4 5 6 @RequestMapping(value = "/test", method = {RequestMethod.POST}) public String test (Model model) { model.addAttribute("msg" , "hello!" ); return "hello" ; }
params 参数:解析发送请求里的参数
1 2 3 4 5 @RequestMapping(value = "/test", params = {"username"}) public String test () { return "hello" ; }
consumes参数 : 指定处理请求的提交内容类型(Content-Type),例如 application/json, text/html
produces参数 :指定返回的内容类型,仅当 request 请求头中的(Accept)类型中包含该指定类型才返回
1 2 3 4 5 6 7 @Controller @RequestMapping(value = "/users", method = RequestMethod.POST, consumes="application/json", produces="application/json") @ResponseBody public List<User> addUser (@RequestBody User userl) { return List<User> users; }
Ant风格路径匹配
模糊和精确多个匹配情况下,精确匹配优先
?:匹配一个字符,0个和多个都不行
*:匹配任意多个字符,优先级低于? 也可以匹配一层路径。
**:匹配任意层路径,优先级低于*
1 2 3 4 5 @RequestMapping("/test/antTest0?") @RequestMapping("/test/antTest0*") @RequestMapping("/test/a*/antTest0*") @RequestMapping("/test/a/*antTest0*") @RequestMapping("/test/**/antTest0")
@RequestParam
@RequestParam 用于获取表单数据(在 URL中:http://xxx?a=1&b=2&c=3)
@RequestParam
用于获取请求参数。@RequestParam("user") String username
效果等价于 username = request.getParameter("user")
该注解可设置的参数:
1 2 3 4 5 @RequestMapping("/test") public String test (@RequestParam("username", required=false, defaultValue="zhangsan") String name) { System.out.println(name); return "hello" ; }
@RequestHeader
用于获取请求头中的值。@RequestHeader("User-Agent") String userAgent
效果等价于 userAgent = request.getHeader("User-Agent")
。
1 2 3 4 5 @RequestMapping("/test") public String test (@RequestParam("username") String name, @RequestHeader(value = "User-Agent", required = false, defaultValue = " ") String userAgent) { System.out.println(userAgent); return "hello" ; }
@CookieValue
@CookieValue
可用于获取Cookie值
1 2 3 4 5 @RequestMapping(value = "/test") public String test (@CookieValue("JSESSIONID", required = false, defaultValue = " ") String jid) { return "success" ; }
@PathVariable
@PathVariable
用于解析RESTful风格中的参数:
1 2 3 4 5 6 7 8 @RequestMapping("/commit/{p1}/{p2}") public String index (@PathVariable("p1") int p1, @PathVariable("p2") int p2, Model model) { int result = p1+p2; model.addAttribute("msg" , "结果:" +result); return "hello" ; }
RESTful
概念
REST:Representational State Transfer,直译过来表现层状态转换 (同一个 URL 可以根据需求被被转换成不同的状态,例如 GET/PUT/POST/DELETE),是目前最流行的一种互联网软件架构。它主要用于客户端和服务器交互类的软件。基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
资源 (Resources):网络上的一个实体,或者说是网络上的一个具体信息。它可以是一段文本、一张图片、一首歌曲、一种服务,总之就是一个具体的存在。可以用一个URI(统一资源定位符)指向它,每种资源对应一个特定的 URI 。要获取这个资源,访问它的URI就可以,因此 URI 即为每一个资源的独一无二的识别符。
表现层 (Representation):把资源具体呈现出来的形式,叫做它的表现层(Representation)。比如,文本可以用 txt 格式表现,也可以用 HTML 格式、XML 格式、JSON 格式表现,甚至可以采用二进制格式。
状态转化 (State Transfer):每发出一个请求,就代表了客户端和服务器的一次交互过程。HTTP协议,是一个无状态协议,即所有的状态都保存在服务器端。因此,如果客户端想要操作服务器,必须通过某种手段,让服务器端发生“状态转化”(State Transfer)。而这种转化是建立在表现层之上的,所以就是 “表现层状态转化”。具体说,就是 HTTP 协议里面,四个表示操作方式的动词:GET
、POST
、PUT
、DELETE
。它们分别对应四种基本操作:
GET
用来获取资源
POST
用来新建资源
PUT
用来更新资源
DELETE
用来删除资源
满足REST设计风格的程序或接口我们称之为RESTful(从单词字面来看就是一个形容词)。所以RESTful API 就是满足REST架构风格的接口。它不是标准也不是协议,只是一种风格,基于这个风格设计的软件可以更简洁,更有层次,更易于实现缓存等机制。
功能
传统方式操作资源 :通过不同的参数来实现不同的效果!方法单一,post 和 get
使用RESTful操作资源 : 可以通过不同的请求方式来实现不同的效果!如下:请求地址一样,但是功能可以不同!
使用
在SpringMVC中可以使用 @PathVariable
注解,让方法参数的值对应绑定到一个URI模板变量上。
1 2 3 4 5 6 7 8 9 10 11 12 @Controller public class RestFulController { @RequestMapping("/commit/{p1}/{p2}") public String index (@PathVariable("p1") int p1, @PathVariable("p2") int p2, Model model) { int result = p1+p2; model.addAttribute("msg" , "结果:" +result); return "test" ; } }
使用method属性指定请求类型
@RequestMapping
注解能够处理 HTTP 请求的方法,约束请求的类型,可以收窄请求范围。指定请求谓词的类型如GET
, POST
, HEAD
, OPTIONS
, PUT
, PATCH
, DELETE
, TRACE
等
1 2 3 4 5 6 @RequestMapping(value = "/hello", method = {RequestMethod.POST}) public String index2 (Model model) { model.addAttribute("msg" , "hello!" ); return "test" ; }
所有的地址栏请求默认都会是 HTTP GET 类型的。
方法级别的注解变体有如下几个: 组合注解
1 2 3 4 5 @GetMapping @PostMapping @PutMapping @DeleteMapping @PatchMapping
@GetMapping
是一个组合注解,它所扮演的是 @RequestMapping(method = RequestMethod.GET)
的一个快捷方式。
HiddenHttpMethodFilter :浏览器 form 表单只支持 GET
与 POST
请求,而DELETE
、PUT
等 method 并不支持,Spring3.0 添加了一个过滤器,可以将这些请求转换为标准的 http 方法,使得支持 GET
、POST
、PUT
与DELETE
请求。使用时需要在web.xml中添加HiddenHttpMethodFilter
:
1 2 3 4 5 6 7 8 9 <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 >
添加HiddenHttpMethodFilter 后,若想发送DELETE
或PUT
请求,则需要创建一个表单,在表单项中携带一个_method
参数,这个参数的值可以设置为DELETE
或PUT
。
1 2 3 4 <form action ="commit/1" method ="post" > <input name ="_method" value ="DELETE" /> <input type ="submit" value ="删除1号" /> </form >
注意 :在Tomcat 8.0以上版本,使用REST风格转发到jsp页面时,因为默认的jsp文件不支持DELETE
和PUT
这种请求,因此有异常,无法正常显示。此时需要在jsp文件头添加:isErrorPage="true"
1 2 <%@ page language="java" contentType="text/html; charset=UTF-8" pageEncoding="UTF-8" isErrorPage="true" %>
HiddenHttpMethodFilter源码分析
HiddenHttpMethodFilter
类里的拦截方法具体如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 protected void doFilterInternal (HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { String paramValue = request.getParameter(this .methodParam); if ("POST" .equals(request.getMethod()) && StringUtils.hasLength(paramValue)) { String method = paramValue.toUpperCase(Locale.ENGLISH); HttpServletRequest wrapper = new HiddenHttpMethodFilter.HttpMethodRequestWrapper(request, method); filterChain.doFilter(wrapper, response); } else { filterChain.doFilter(request, response); } }
wrapper对象被重写的getMethod()
方法将直接返回_method
里的值DELETE
。并且包装后的wrapper对象被传递到了过滤器链中,从而后续的过滤器在调用此wrapper对象的getMethod()
时将获取到DELETE
。
GET/POST 请求处理总结
GET 请求
GET 请求:常用于检索 && 获取 ,是幂等性 的。一般不会携带请求体数据,传递的参数会拼接在 URL 上(URL 长度有限制,视浏览器而定,例如 Chrome 的 URL 长度限制为 2Mb,2048 个字符)。Content-Type
通常为 application/x-www-form-urlencoded
。
在 Spring MVC 中:
使用 @PathVariable("xxx")
注解解析 Restful 请求 URL 中的路径参数
使用 @RequestParam("xxx")
注解解析 GET 请求 URL 中的 "xxx"
数据(如果方法中的参数名和 URL 中传来的参数名一致,可以省略该注解)。如果前端传来的的表单域参数名和 VO 类的所有属性名都一致,可以直接省略该注解,使用 VO
也可以直接使用 @RequestParam Map<String, Object> params
将 URL 中所有数据都存储到一个 map 中
若想返回 JSON 数据,则只需要在方法上标注 @ResponseBody
或直接在类上标注 @RestController
若不想返回 JSON 数据,而想进行页面跳转,则不需要标注 @ResponseBody
注解,直接返回视图名即可(不适合前后端分离项目)
案例:前端发送 GET 请求 /product/attr/base/list/{catelogId}
,在路径中指定属性类型 base
以及商品分类 id catelogId
,并在 URL 上携带需要分页查询的参数。要求后端返回 JSON 数据(存储在响应体里)
Controller 层代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 @ResponseBody @GetMapping("/{attrType}/list/{catelogId}") public R baseAttrList (@RequestParam Map<String, Object> params, @PathVariable("attrType") String type, @PathVariable("catelogId") Long catelogId) { PageUtils page = attrService.queryBaseAttrPage(params, type, catelogId); return R.ok().put("page" , page); }
POST 请求
POST 请求:常用来创建 || 更新 。数据内容不会显示在 URL 中,而是显示在请求体 中。相对安全。请求对资源有副作用,不是幂等性的 ,多次 POST 请求会创建重复的数据内容。前端以 JSON 形式发送 POST 请求时,Content-Type
为 application/json; charset=utf-8
。
HTTP POST 请求的内容是在请求体内的,但也不是绝对安全的。他人截获该请求后仍可以得到请求体内容。若想保证安全性,还需要使用 HTTPS 的加密方法对请求体内容进行加密。
在 Spring MVC 中,使用 @RequestBody
注解修饰的参数将接受 POST 请求体中的 JSON 数据,按照属性名映射。
案例:
Controller 层代码:
1 2 3 4 5 6 @ResponseBody @PostMapping("/merge", consumes="application/json", produces="application/json") public R merge (@RequestBody MergeVo mergeVo) { purchaseService.mergePurchase(mergeVo); return R.ok(); }
其中,方法仅处理 Content-Type
为 "application/json"
类型的请求;并且返回的类型为 "application/json"
。
若想实现:实体类中某些字段不为空时才添加到 JSON 中返回给前端,如果为空不添加到 JSON 中。则可以给字段添加 JsonInclude()
注解(com.fasterxml.jackson
包):
1 2 3 4 5 6 7 8 @JsonInclude(JsonInclude.Include.NON_EMPTY) @TableField(exist = false) private List<CategoryEntity> children;
结果跳转方式
ModelAndView
设置ModelAndView对象,根据view的名称和视图解析器跳到指定的页面。
页面 : {视图解析器前缀} + viewName +{视图解析器后缀}
1 2 3 4 5 6 7 8 <bean class ="org.springframework.web.servlet.view.InternalResourceViewResolver" id ="internalResourceViewResolver" > <property name ="prefix" value ="/WEB-INF/jsp/" /> <property name ="suffix" value =".jsp" /> </bean >
对应的Controller类
1 2 3 4 5 6 7 8 9 public class ControllerTest1 implements Controller { public ModelAndView handleRequest (HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { ModelAndView mv = new ModelAndView(); mv.addObject("msg" ,"ControllerTest1" ); mv.setViewName("test" ); return mv; } }
ServletAPI
通过设置ServletAPI,不需要视图解析器。
通过HttpServletResponse进行输出
通过HttpServletResponse实现重定向
通过HttpServletResponse实现转发
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @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); } }
SpringMVC
通过SpringMVC来实现转发和重定向 - 无需视图解析器;
测试前,需要将视图解析器注释掉
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 @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" ; } }
通过SpringMVC来实现转发和重定向 - 有视图解析器;
重定向不需要视图解析器,本质就是重新请求到一个新地方,所以注意路径问题.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Controller public class ResultSpringMVC2 { @RequestMapping("/rsm2/t1") public String test1 () { return "test" ; } @RequestMapping("/rsm2/t2") public String test2 () { return "redirect:/index.jsp" ; } }
RedirectAttributes
当重定向时,请求域 Request 中的数据(例如 Model 数据中的数据)将无法获取到。此时可以使用 RedirectAttributes
将想要转发的数据存进去:
1 2 3 4 5 6 7 8 9 10 11 @PostMapping("/register") public String register (UserRegisterVo registerVo, RedirectAttributes attributes) { Map<String, String> errors = new HashMap<>(); attributes.addFlashAttribute("errors" , errors); return "redirect:/reg.html" ; }
addFlashAttribute()
方法本质上是将数据存放到session中,重定向后再从session中获取数据,并且一旦取出来后就把该数据从session中删掉,也就是只能取一次。对比:
addFlashAttribute()
:将数据放在 Session 中,但是只能取一次,使用 session.xxx
获取值
addAttribute()
:将数据拼接在 URL 后面,使用 @RequestParam("skuId")
获取值
数据处理
处理提交数据
1、提交的域名称和处理方法的参数名一致
提交数据 : http://localhost:8080/hello?name=zhangsan
1 2 3 4 5 @RequestMapping("/hello") public String hello (String name) { System.out.println(name); return "hello" ; }
2、提交的域名称和处理方法的参数名不一致
提交数据 : http://localhost:8080/hello?username=zhangsan
使用@RequestParam("username")
1 2 3 4 5 6 @RequestMapping("/hello") public String hello (@RequestParam("username") String name) { System.out.println(name); return "hello" ; }
3、提交的是一个对象
要求提交的表单域和对象的属性名一致,参数使用对象即可(SpringMVC自动封装)
实体类
1 2 3 4 5 6 7 8 9 public class User { private int id; private String name; private int age; }
提交数据 : http://localhost:8080/mvc04/user?name=zhangsan&id=1&age=15
处理方法 :
1 2 3 4 5 @RequestMapping("/user") public String user (User user) { System.out.println(user); return "hello" ; }
后台输出 : User { id=1, name=’zhangsan’, age=15 }
说明:如果使用对象的话,前端传递的参数名和对象名必须一致,否则就是null。
数据显示到前端
第一种 : 通过ModelAndView
1 2 3 4 5 6 7 8 9 public class ControllerTest1 implements Controller { public ModelAndView handleRequest (HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception { ModelAndView mv = new ModelAndView(); mv.addObject("msg" ,"ControllerTest1" ); mv.setViewName("test" ); return mv; } }
第二种 : 通过ModelMap
1 2 3 4 5 6 7 8 @RequestMapping("/hello") public String hello (@RequestParam("username") String name, ModelMap model) { model.addAttribute("name" ,name); System.out.println(name); return "hello" ; }
第三种 : 通过Model
1 2 3 4 5 6 7 8 @RequestMapping("/ct2/hello") public String hello (@RequestParam("username") String name, Model model) { model.addAttribute("msg" ,name); System.out.println(name); return "test" ; }
对比
Model
是一个接口,只有寥寥几个方法只适合用于储存数据,简化了新手对于Model
对象的操作和理解;
ModelMap
继承了 LinkedMap
,除了实现了自身的一些方法,同样的继承 LinkedMap
的方法和特性;
ModelAndView
可以在储存数据的同时,可以进行设置返回的逻辑视图,进行控制展示层的跳转。
可以在方法处传入Map
、Model
或ModelMap
,在这些参数中保存的数据都会放到请求域(requestScope)中。使用Map
,Model
和ModelMap
本质上是使用了Spring的 BindingAwareModelMap 在工作,相当于在BindingAwareModelMap
中保存的数据都会放到请求域中。SpringMVC在运行时拥有唯一的一个BindingAwareModelMap
对象,各个方法中获取到的Map/ModelMap
都会被转换成同一个该对象,从而可以做到多个方法中的数据共享。
@ModelAttribute
被@ModelAttribute
注解修饰的方法会在所有请求执行前执行,在该方法内可以从数据库获取到pojo对象,如下图中book对象,并将其添加到map中,这样其他请求方法在执行时就能从中获取到该对象。该注解在整合MyBatis后较少使用。
https://www.bilibili.com/video/BV1d4411g7tv?t=591&p=155
乱码问题
SpringMVC给我们提供了一个过滤器,可以在web.xml中配置。
1 2 3 4 5 6 7 8 9 10 11 12 <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 >
注意:里需要写上/*而非/,否则.jsp文件无法经过该过滤器,因此无法解决.jsp文件的乱码问题。
Controller 返回 JSON 数据
JSON解析工具:
首先使用jackson,导入jar包:
1 2 3 4 5 6 <dependency > <groupId > com.fasterxml.jackson.core</groupId > <artifactId > jackson-databind</artifactId > <version > 2.9.8</version > </dependency >
配置web.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 <?xml version="1.0" encoding="UTF-8"?> <web-app xmlns ="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation ="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd" version ="4.0" > <servlet > <servlet-name > SpringMVC</servlet-name > <servlet-class > org.springframework.web.servlet.DispatcherServlet</servlet-class > <init-param > <param-name > contextConfigLocation</param-name > <param-value > classpath:springmvc-servlet.xml</param-value > </init-param > <load-on-startup > 1</load-on-startup > </servlet > <servlet-mapping > <servlet-name > SpringMVC</servlet-name > <url-pattern > /</url-pattern > </servlet-mapping > <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 > </web-app >
配置springmvc-servlet.xml
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:context ="http://www.springframework.org/schema/context" xmlns:mvc ="http://www.springframework.org/schema/mvc" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd" > <context:component-scan base-package ="com.zhao.controller" /> <bean class ="org.springframework.web.servlet.view.InternalResourceViewResolver" id ="internalResourceViewResolver" > <property name ="prefix" value ="/WEB-INF/jsp/" /> <property name ="suffix" value =".jsp" /> </bean > </beans >
假设已经存在实体类User,编写一个Controller:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @Controller public class UserController { @RequestMapping(value = "/json1", produces = "application/json;charset=utf-8") @ResponseBody public String json1 () throws JsonProcessingException { ObjectMapper mapper = new ObjectMapper(); User user = new User("zhangsan" , 3 , "男" ); String str = mapper.writeValueAsString(user); return str; } }
这里使用到注解@ResponseBody
,其会将return 返回的字符串转为JSON格式
注意:使用JSON时记得处理可能出现的乱码问题,解决方案:在@RequestMapping
中设置utf-8
1 2 @RequestMapping(value = "/json1",produces = "application/json;charset=utf-8")
乱码统一解决
上一种方法比较麻烦,如果项目中有许多请求则每一个都要添加。可以通过Spring配置统一指定,这样就不用每次都去处理乱码问题。
我们可以在springmvc的配置文件上添加一段消息StringHttpMessageConverter转换配置
1 2 3 4 5 6 7 8 9 10 11 12 13 14 <mvc:annotation-driven > <mvc:message-converters register-defaults ="true" > <bean class ="org.springframework.http.converter.StringHttpMessageConverter" > <constructor-arg value ="UTF-8" /> </bean > <bean class ="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter" > <property name ="objectMapper" > <bean class ="org.springframework.http.converter.json.Jackson2ObjectMapperFactoryBean" > <property name ="failOnEmptyBeans" value ="false" /> </bean > </property > </bean > </mvc:message-converters > </mvc:annotation-driven >
返回json字符串统一解决
在类上直接使用 @RestController ,这样子,里面所有的方法都只会返回 JSON字符串了,不用再每一个方法都添加@ResponseBody 。在前后端分离开发中,一般都使用 @RestController ,十分便捷。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @RestController public class UserController { @RequestMapping(value = "/json1") public String json1 () throws JsonProcessingException { ObjectMapper mapper = new ObjectMapper(); User user = new User("zhangsan" , 3 , "男" ); String str = mapper.writeValueAsString(user); return str; } }
fastJson
fastjson.jar是阿里开发的一款专门用于Java开发的包,可以方便的实现JSON对象与JavaBean对象的转换,实现JavaBean对象与JSON字符串的转换,实现JSON对象与JSON字符串的转换。实现JSON的转换方法很多,最后的实现结果都是一样的。
fastjson 的 pom 依赖:
1 2 3 4 5 <dependency > <groupId > com.alibaba</groupId > <artifactId > fastjson</artifactId > <version > 1.2.60</version > </dependency >
fastjson 三个主要的类:
代码测试,新建一个FastJsonDemo 类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 package com.zhao.controller;import com.alibaba.fastjson.JSON;import com.alibaba.fastjson.JSONObject;import com.zhao.pojo.User;import java.util.ArrayList;import java.util.List;public class FastJsonDemo { public static void main (String[] args) { User user1 = new User("zhangsan1号" , 3 , "男" ); User user2 = new User("zhangsan2号" , 3 , "男" ); User user3 = new User("zhangsan3号" , 3 , "男" ); User user4 = new User("zhangsan4号" , 3 , "男" ); List<User> list = new ArrayList<User>(); list.add(user1); list.add(user2); list.add(user3); list.add(user4); System.out.println("*******Java对象 转 JSON字符串*******" ); String str1 = JSON.toJSONString(list); System.out.println("JSON.toJSONString(list)==>" +str1); String str2 = JSON.toJSONString(user1); System.out.println("JSON.toJSONString(user1)==>" +str2); System.out.println("\n****** JSON字符串 转 Java对象*******" ); User jp_user1=JSON.parseObject(str2,User.class); System.out.println("JSON.parseObject(str2,User.class)==>" +jp_user1); System.out.println("\n****** Java对象 转 JSON对象 ******" ); JSONObject jsonObject1 = (JSONObject) JSON.toJSON(user2); System.out.println("(JSONObject) JSON.toJSON(user2)==>" +jsonObject1.getString("name" )); System.out.println("\n****** JSON对象 转 Java对象 ******" ); User to_java_user = JSON.toJavaObject(jsonObject1, User.class); System.out.println("JSON.toJavaObject(jsonObject1, User.class)==>" +to_java_user); } }
AJAX
简介
AJAX = Asynchronous JavaScript and XML(异步的 JavaScript 和 XML)。
AJAX 是一种在无需重新加载整个网页的情况下,能够更新部分网页的技术。AJAX 不是一种新的编程语言,而是一种用于创建更好更快以及交互性更强的Web应用程序的技术。
在 2005 年,Google 通过其 Google Suggest 使 AJAX 变得流行起来。Google Suggest能够自动帮你完成搜索单词。Google Suggest 使用 AJAX 创造出动态性极强的 web 界面:当您在谷歌的搜索框输入关键字时,JavaScript 会把这些字符发送到服务器,然后服务器会返回一个搜索建议的列表。
传统的网页(即不用AJAX技术的网页),想要更新内容或者提交一个表单,都需要重新加载整个网页。而使用AJAX技术的网页,通过在后台服务器进行少量的数据交换,就可以实现异步局部更新。使用AJAX,用户可以创建接近本地桌面应用的直接、高可用、更丰富、更动态的Web用户界面。
jQuery.ajax
AJAX的核心是XMLHttpRequest对象(XHR)。XHR为向服务器发送请求和解析服务器响应提供了接口。能够以异步方式从服务器获取新数据。
jQuery 提供多个与 AJAX 有关的方法。
通过 jQuery AJAX 方法,您能够使用 HTTP Get 和 HTTP Post 从远程服务器上请求文本、HTML、XML 或 JSON – 同时您能够把这些外部数据直接载入网页的被选元素中。
jQuery AJAX本质就是 XMLHttpRequest,对他进行了封装,方便调用!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 jQuery.ajax(...) 部分参数: url:请求地址 type:请求方式,GET、POST(1.9 .0 之后用method) headers:请求头 data:要发送的数据 contentType:即将发送信息至服务器的内容编码类型(默认: "application/x-www-form-urlencoded; charset=UTF-8" ) async :是否异步 timeout:设置请求超时时间(毫秒) beforeSend:发送请求前执行的函数(全局) complete:完成之后执行的回调函数(全局) success:成功之后执行的回调函数(全局) error:失败之后执行的回调函数(全局) accepts:通过请求头发送给服务器,告诉服务器当前客户端课接受的数据类型 dataType:将服务器端返回的数据转换成指定类型 "xml" : 将服务器端返回的内容转换成xml格式 "text" : 将服务器端返回的内容转换成普通文本格式 "html" : 将服务器端返回的内容转换成普通文本格式,在插入DOM中时,如果包含JavaScript标签,则会尝试去执行。 "script" : 尝试将返回值当作JavaScript去执行,然后再将服务器端返回的内容转换成普通文本格式 "json" : 将服务器端返回的内容转换成相应的JavaScript对象 "jsonp" : JSONP 格式使用 JSONP 形式调用函数时,如 "myurl?callback=?" jQuery 将自动替换 ? 为正确的函数名,以执行回调函数
ajax常用参数:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 $.ajax({ url: "http://www.hzhuti.com" , dataType: "json" , async : true , data: { "id" : "value" }, type: "GET" , beforeSend: function ( ) { }, success: function (result ) { }, complete: function ( ) { }, error: function ( ) { } });
技巧:data
属性中若想添加某个表单里的数据时,一个一个获取表单属性值较为繁琐,可以使用jQuery提供的.serialize()
方法获取完整的表单数据,例如:data: ${#form}.serialize()
使用案例
配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 <?xml version="1.0" encoding="UTF-8"?> <beans xmlns ="http://www.springframework.org/schema/beans" xmlns:xsi ="http://www.w3.org/2001/XMLSchema-instance" xmlns:context ="http://www.springframework.org/schema/context" xmlns:mvc ="http://www.springframework.org/schema/mvc" xsi:schemaLocation ="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd" > <context:component-scan base-package ="com.zhao.controller" /> <mvc:default-servlet-handler /> <mvc:annotation-driven /> <bean class ="org.springframework.web.servlet.view.InternalResourceViewResolver" id ="internalResourceViewResolver" > <property name ="prefix" value ="/WEB-INF/jsp/" /> <property name ="suffix" value =".jsp" /> </bean > </beans >
编写一个AjaxController
1 2 3 4 5 6 7 8 9 10 11 12 @RestController public class AjaxController { @RequestMapping("/a1") public List<User> ajax1 () { List<User> list = new ArrayList<User>(); list.add(new User("zhangsan1" ,3 ,"男" )); list.add(new User("zhangsan2" ,3 ,"男" )); list.add(new User("zhangsan3" ,3 ,"男" )); return list; } }
前端页面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 <%@ page contentType="text/html;charset=UTF-8" language="java" %> <html > <head > <title > Title</title > </head > <body > <input type ="button" id ="btn" value ="获取数据" /> <table width ="80%" align ="center" > <tr > <td > 姓名</td > <td > 年龄</td > <td > 性别</td > </tr > <tbody id ="content" > </tbody > </table > <script src ="${pageContext.request.contextPath}/statics/js/jquery-3.1.1.min.js" > </script > <script > $(function ( ) { $("#btn" ).click(function ( ) { $.post("${pageContext.request.contextPath}/a2" ,function (data ) { console .log(data) var html="" ; for (var i = 0 ; i <data.length ; i++) { html+= "<tr>" + "<td>" + data[i].name + "</td>" + "<td>" + data[i].age + "</td>" + "<td>" + data[i].sex + "</td>" + "</tr>" } $("#content" ).html(html); }); }) }) </script > </body > </html >