springmvc是一个web层mvc框架, 何谓MVC?
model 模型
view 视图
controller 控制器
这是一种设计模式,将责任进行拆分,不同的组件负责不同的事情。
好处
结构清晰
更好维护(大量使用jsp的年代,<%%>,显示)
坏处
更加复杂了
目录(点击选项跳转)
springmvc
简介
入门体验
springmvc分析
/*不能写的原因
*.do的用法
/用法
关于前端控制器的解释
springmvc配置文件名字的问题
视图解析器
控制器的解释
注解开发模式
转发与重定向
关于springmvc访问web元素
注解详解
@RequestMapping
关于请求路径的问题
@GetMapping 、 @PostMaping.....
对于非get post请求的支持
@Responsebody
@ModelAttribute
@SessionAttribute
@SessionAttributes
@SessionAttribute
@RequestParam
@RequestBody
@InitBinder
@PathVariable
@RestController
关于静态资源访问的问题
关于post请求中文乱码问题解决
关于form表单提交数据的方式
方式一 通过属性名字绑定
方式二 利用@RequetParam
方式三 直接使用pojo形式传递
关于form表单提交日期格式数据问题的处理
json数据交互
额外依赖
JSON数据返回前台以及如何解析
JSON数据如何使用Ajax提交到后台,后台如何解析
关于Form提交数据与Ajax自定义JSON数据提交的区别
XML数据交互
创建web项目(运行环境 IDEA)
编写web.xml,在其中注册一个特殊的servlet,前端控制器
编写一个springmvc的配置文件
注册一个视图解析器
编写要给控制器
编写一个结果页面
创建maven web项目结构如下:
pom.xml
4.0.0
com.sz
springmvc01
1.0-SNAPSHOT
war
springmvc01 Maven Webapp
http://www.example.com
UTF-8
1.8
1.8
org.springframework
spring-webmvc
5.0.8.RELEASE
springmvc01
maven-clean-plugin
3.0.0
maven-resources-plugin
3.0.2
maven-compiler-plugin
3.7.0
maven-surefire-plugin
2.20.1
maven-war-plugin
3.2.0
maven-install-plugin
2.5.2
maven-deploy-plugin
2.8.2
web.xml
springmvc
org.springframework.web.servlet.DispatcherServlet
springmvc
/
springmvc-servlet.xml
控制器代码
package com.sz.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 {
@Override
public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
ModelAndView mav = new ModelAndView();
mav.addObject("girl","菲菲");
mav.setViewName("girl");
return mav;
}
}
视图代码
<%--
Created by IntelliJ IDEA.
User: Administrator
Date: 2018/8/27
Time: 9:09
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
我的脑海:${girl}
流程图
组件分析
web.xml
注册前端控制器,目的在于,我们希望让springmvc去处理所有的请求,
通过
springmvc
/
确实是处理所有的请求(不是真的所有)
urlPattern的写法问题
/
/* (永远都不要这么写)
*.do
请求 /helloController过去的时候,它的视图名称是girl,girl.jsp页面,它将其当作了一个叫girl.jsp的请求,尝试去匹配对应的controller,但是我的容器当中根本不存在这样的controller,所以无法匹配,导致404.
这种方式,是有的团队习惯将请求的行为加个小尾巴用来区分,do,行为 *.action
处理所有的请求,但是和/*不一样,它处理完之后要出去的时候不会再去将这个girl.jsp当作一个新的请求,将这个渲染的结构直接返回给浏览器
springmvc设计的理念是希望开发者尽量远离原生的servletAPI,API不是很好用,繁琐点,将操作进一步的简化,它将很多东西责任进行了拆分,不希望我们将一些技术点绑定死,可以做到随意的切换。本身还是基于Servlet设计的,分发的servlet.
默认情况下是用dispatcherServlet的名字当作命名空间
[servletName]-servlet.xml(WEB-INF)之下寻找。
[servletName]-servlet=namespace.
将配置文件移动位置之后,出现了下面的异常。
Could not open ServletContext resource [/WEB-INF/springmvc-servlet.xml]
如果非要重新使用另外一个名字。
默认的规则要求在web-inf下,但是maven项目的标准应该在resources下面,如何解决这个问题呢?
重新制定上下文的配置文件的位置即可。
contextConfigLocation
classpath:springmvc.xml
此时是在类路径下寻找springmvc.xml这个配置文件,我推荐使用这种。
springmvc支持多种视图技术
jsp
freemaker(模板技术)
内部的资源视图解析器
视图前缀
/jsp/ 它是我们的请求响应的资源的路径的配置, viewName : girl /jsp/girl
后缀
.jsp 此时我们的前缀+视图名称+后缀= /jsp/girl.jsp
request.getDS.forward(request,response);的作用是一样一样的。
物理视图由逻辑视图转换而来
物理视图是 webapp/jsp/girl.jsp
逻辑视图
prefix
logicViewName
suffix
p View = prefix + logicViewName + suffix;
是一种比较传统的实现一个接口的方式完成的,Controller。
如果要给接口只有一个方法,这种接口叫做函数式接口。
@Nullable
ModelAndView handleRequest(HttpServletRequest request, HttpServletResponse response) throws Exception;
他像谁?
servlet里面由doGet doPost 里面入参就是请求与响应。
void
设计为ModelAndView 。
在model当中填充数据,然后在具体的视图进行展示。
还需要在配置文件当中配置一下这个bean,要取个名字,就用来充当了这个请求URI。
它就处理一个请求,跟servlet差别不是很大。
基于实现接口的方式已经是过去式了,采用注解开发很简单
基本注解
@Controller
@RequestMapping
开发步骤
记得配置一下基础扫描的包,这样配置的注解才会生效
在指定的类上面添加@Controller注解
添加@RequestMapping 类似于前面的controller的那个名字(不同requesthandler处理的 HandlerMapping)
当我们写上Controller之后,就标记了它为spring的一个组件,并且是控制器的组件,此时我们的handlermapping会去扫描寻找这个controller是否与之匹配,如果发现匹配就把这里处理的工作交给它。
匹配的规则的又是什么呢?
具体的匹配就是通过请求的路径进行匹配的
@RequestMappint(URI)
此时就是通过这个URI进行匹配
@RequestMapping
可以写在方法上
类上(推荐使用二者结合的方式)
转发到页面 默认到选项
重定向到页面 redirect:path
转发到另外一个控制器 forward:path
request
session
application
可以通过模拟的对象完成操作,也可以使用原生的ServletAPI完成,直接在方法当中入参即可。
value 写的是路径,是一个数组的形式,可以匹配多个路径
path 是value的别名,所以二者任选其一,他们的作用是一样的
method 是可以指定可以访问的请求的类型,比如get post 它叶可以写成一个数组的形式。
params 可以去指定参数,你还可以去限定这个参数的特征,必须等于某个值,不等于某个值
headers 能够影响浏览器的行为
consumers 消费者,媒体类型,可以限定必须为application/json;charset=UTF-8
produces 产生的响应的类型
springmvc支持ant风格
? 任意的字符,斜杠例外
*
0到n,任意个字符都可以 不能含斜杠
** 支持任意层路径 /m3/** 这样才可以提现出来 /m3** 这样的效果等同于m3后面任意多个字符
getMapping 限定了只支持get请求
postMapping 限定了只支持post请求
对于非get post请求的支持,需要有额外的内容添加,要增加一个过滤器来额外处理
过滤器
返回的不再是页面而是数据
hiddenHttpMethodFilter
org.springframework.web.filter.HiddenHttpMethodFilter
hiddenHttpMethodFilter
/*
表单提交里面还要添加一个隐藏的参数
name="_method" value="DELETE"
返回数据的,一般情况下返回JSON格式,
使用方式一
// 就是在controller里面的任意一个处理具体的方法之前都会执行
@ModelAttribute
public User init(){
System.out.println("init.......");
User u = new User();
u.setName("王菲");
return u;
}
@RequestMapping("/login")
public String login(Model model){
System.out.println(model.containsAttribute("u"));
System.out.println(model.containsAttribute("user"));
System.out.println(model.containsAttribute("sdfdsfd"));
return "msg";
}
如果某些对象从头到尾每次请求当中都要存在,不消失,就适合这么用。
使用方式二
@ModelAttribute("user")
public void init(Model model){
System.out.println("init.......");
User user = new User();
user.setName("王菲");
model.addAttribute("user",user);
}
如果没有传递这个模型过来,那么方法上加了@modelattribute的为你提供,如果你传了就用你的
这个用在类上面,它会将模型自动填充到会话里面去。
要求当前这次访问当中的会话里面必须要有某个对象
json数据,不是通过form表单传递
ajax({
data:
})
数据转换,将日期转换处理
restful风格
详情参考并在2000年定义罗伊菲尔丁在他的博士论文。
Contrller + Response
由于我们的servlet设置了URL匹配方式为/ 所以,它将静态资源也当做一个后台的请求
比如http://localhost:8080/s/static/css/index.css
它尝试去匹配一个static/css/index.css的Controller里面的RequestMapping的组合,因为没有,所以404.
解决方式很多,最简单的,是让springmvc单独处理,将这些交给容器的默认的servlet处理,就不让DispatcherServlet来处理了。
解决方式1
MIME 类型
解决方式2
通过映射关系描述,一一对编写规则
解决方式3
自行在web.xml定义映射规则
我们添加一个过滤器即可,springmvc提供了非常好的字符编码过滤器,所以我们注册即可。
characterEncodingFilter
org.springframework.web.filter.CharacterEncodingFilter
encoding
UTF-8
forceRequestEncoding
true
characterEncodingFilter
/*
通过属性名称进行绑定,可以完成数据传递.
页面当中表单元素的name值要和后台的形参的名字保持一致。
如果有多个,多个形参按名字绑定即可,当传入的值较多的时候。
<%--
Created by IntelliJ IDEA.
User: Administrator
Date: 2018/8/31
Time: 8:31
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
Title
package com.sz.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseBody;
import java.util.HashMap;
import java.util.Map;
@Controller
@RequestMapping("/user")
public class UserController {
@PutMapping("/put")
// 需要额外的json包的支持
@ResponseBody
public String put(String name,String password){
System.out.println(name+password);
// Map map = new HashMap<>();
// map.put("msg","ok");
return "ok";
}
}
jsp页面不变
后台
@PutMapping("/put")
// 需要额外的json包的支持
@ResponseBody
public String put(@RequestParam("name") String name, @RequestParam("password") String password){
System.out.println(name+password);
// Map map = new HashMap<>();
// map.put("msg","ok");
return "ok";
}
jsp不变
后台
@PutMapping("/put")
// 需要额外的json包的支持
@ResponseBody
public String put(User user){
System.out.println(user.getName() + user.getPassword());
return "ok";
}
处理日期(没有时间)
package com.sz.controller;
import com.sz.pojo.User;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Controller
@RequestMapping("/user")
public class UserController {
//
@InitBinder("user")
public void init(WebDataBinder dataBinder){
// 这里指定什么格式,前台就只能传什么格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
sdf.setLenient(false);
dataBinder.registerCustomEditor(Date.class,new CustomDateEditor(sdf,false));
}
@PostMapping("/put")
// 需要额外的json包的支持
@ResponseBody
public String put(@ModelAttribute("user") User user){
System.out.println(user.getName() + user.getPassword());
System.out.println(user.getBirth());
return "ok";
}
}
// 通过initBinder指定了user名字和modelAttribute里面user绑定
不指定名字,根据数据类型一样可以分析解析转换成功
package com.sz.controller;
import com.sz.pojo.User;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Controller
@RequestMapping("/user")
public class UserController {
//
@InitBinder
public void init(WebDataBinder dataBinder){
// 这里指定什么格式,前台就只能传什么格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
sdf.setLenient(false);
dataBinder.registerCustomEditor(Date.class,new CustomDateEditor(sdf,false));
}
@PostMapping("/put")
// 需要额外的json包的支持
@ResponseBody
public String put(@ModelAttribute User user){
System.out.println(user.getName() + user.getPassword());
System.out.println(user.getBirth());
return "ok";
}
}
时间+日期
package com.sz.controller;
import com.sz.pojo.User;
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
@Controller
@RequestMapping("/user")
public class UserController {
//
@InitBinder
public void init(WebDataBinder dataBinder){
// 这里指定什么格式,前台就只能传什么格式
// SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
sdf.setLenient(false);
dataBinder.registerCustomEditor(Date.class,new CustomDateEditor(sdf,false));
}
@PostMapping("/put")
// 需要额外的json包的支持
@ResponseBody
public String put(@ModelAttribute User user){
System.out.println(user.getName() + user.getPassword());
System.out.println(user.getBirth());
return "ok";
}
}
在属性上面添加额外的注解
// @DateTimeFormat(pattern = "yyyy-MM-dd")
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date birth;
com.fasterxml.jackson.core
jackson-databind
2.9.3
com.fasterxml.jackson.core
jackson-core
2.9.3
com.fasterxml.jackson.core
jackson-annotations
2.9.3
net.sf.json-lib
json-lib
2.4
jdk15
org.codehaus.jackson
jackson-core-asl
1.9.2
org.codehaus.jackson
jackson-mapper-asl
1.9.2
另外记得添加
JSON后台返回
返回POJO
@RequestMapping("/m1")
@ResponseBody // 这个注解将知道现在返回的不是视图,它会将这个数据转换为JSON格式。
public User m1(){
User u = new User();
u.setPwd("321312");
u.setName("许晴");
return u;
}
返回Map
@RequestMapping("/m2")
@ResponseBody
public Map m2(){
Map map = new HashMap<>();
map.put("name","许晴请");
map.put("age",28);
return map;
}
返回数组
@RequestMapping("/m3")
// @ResponseBody
public User[] m3(){
User u = new User();
u.setName("开玩笑");
u.setPwd("123");
User u2 = new User();
u2.setName("不开玩笑");
u2.setPwd("321");
return new User[]{u,u2};
}
返回List
@RequestMapping("/m4")
public List m4(){
List l = new ArrayList<>();
User u = new User();
u.setName("开玩笑");
u.setPwd("123");
User u2 = new User();
u2.setName("不开玩笑");
u2.setPwd("321");
l.add(u);
l.add(u2);
return l;
}
JSON前台如何解析
解析返回的POJO
$('#b1').click(function () {
$.ajax({
url:'${ctx}/json/m1',
type:'post',
success:function (data) {
alert(data.name);
alert(data.pwd);
}
})
})
解析返回的Map
$('#b2').click(function () {
$.ajax({
url:'${ctx}/json/m2',
type:'post',
success:function (data) {
alert(data.name);
alert(data.age);
}
})
})
解析返回的数组
$('#b3').click(function () {
$.ajax({
url:'${ctx}/json/m3',
type:'post',
success:function (data) {
for(var i = 0 ; i < data.length; i ++){
alert(data[i].name);
alert(data[i].pwd);
}
}
})
})
解析返回的List
$('#b4').click(function () {
$.ajax({
url:'${ctx}/json/m4',
type:'post',
success:function (data) {
for(var i = 0 ; i < data.length; i ++){
alert(data[i].name);
alert(data[i].pwd);
}
}
})
})
返回List
$('#b5').click(function () {
$.ajax({
url:'${ctx}/json/m5',
type:'post',
success:function (data) {
for(var i = 0 ; i < data.length; i ++){
alert(data[i].u1.name);
alert(data[i].u1.pwd);
alert(data[i].u2.name);
alert(data[i].u2.pwd);
}
}
})
})
AJAX = 异步 JavaScript 和 XML。
AJAX 是一种用于创建快速动态网页的技术。
通过在后台与服务器进行少量数据交换,AJAX 可以使网页实现异步更新。这意味着可以在不重新加载整个网页的情况下,对网页的某部分进行更新。
传统的网页(不使用 AJAX)如果需要更新内容,必需重载整个网页面。
url 默认值: 当前页地址。发送请求的地址。
contentType 主要设置你发送给服务器的格式,dataType设置你收到服务器数据的格式。
success 请求成功后的回调函数。
type 默认值: "GET")。请求方式 ("POST" 或 "GET"), 默认为 "GET"。注意:其它 HTTP 请求方法,如 PUT 和 DELETE 也可以使用,但仅部分浏览器支持。
data 发送到服务器的数据。将自动转换为请求字符串格式。GET 请求中将附加在 URL 后。禁止此自动转换。必须为 Key/Value 格式。如果为数组,jQuery 将自动为不同值对应同一个名称。如 {foo:["bar1", "bar2"]} 转换为 '&foo=bar1&foo=bar2'。
ajax 的复杂JSON数据,用 JSON.stringify序列化后,然后发送,在服务器端接到然后用 JSON.parse 进行还原就行了,这样就能处理复杂的对象了。
contentType:"application/json;charset=utf-8"
1 前台写法
$('#b1').click(function () {
//
var obj = {
'name':'叶问',
'pwd':'怕老婆'
};
$.ajax({
url:'${ctx}/json2/add',
type:'post',
contentType:'application/json',
data:JSON.stringify(obj),
success:function (data) {
}
})
})
2 后台的写法
@RequestMapping("/add")
// User user入参只能处理表单提交的数据
public String add(@RequestBody User user){
System.out.println(user.getName() +user.getPwd());
return "msg";
}
一定要记得添加@requestBody否则无法解析。
form提交方式请求分析截图:
通过ajax组装的json数据发送分析
所以二者发送组装数据的区域存在不同,所以处理方式也不同。
对于form表单提交数据它的contentType是属于Content-Type: application/x-www-form-urlencoded
对于ajax发送JSON则是 application/json.
发送一个POJO
前台
$('#b1').click(function () {
//
var obj = {
'name':'叶问',
'pwd':'怕老婆'
};
$.ajax({
url:'${ctx}/json2/add',
type:'post',
contentType:'application/json',
data:JSON.stringify(obj),
success:function (data) {
}
})
})
后台
// 前台如何提交一个User对象过来
@RequestMapping("/add")
// User user入参只能处理表单提交的数据
@ResponseBody
public String add(@RequestBody User user ){
System.out.println(user.getName() +user.getPwd());
return "msg";
}
发送一组POJO到后台
前台
$('#b2').click(function () {
//
var obj = {
'name':'叶问',
'pwd':'怕老婆'
};
var obj2 ={
'name':'未知名的小帅哥',
'pwd':'123123'
};
var arr = new Array();
arr.push(obj);
arr.push(obj2);
$.ajax({
url:'${ctx}/json2/addList',
type:'post',
contentType:'application/json',
data:JSON.stringify(arr),
success:function (data) {
if(data.code == 2000){
alert("陈工啦");
}
}
})
})
后台
@RequestMapping("/addList")
// User user入参只能处理表单提交的数据
@ResponseBody
public Map addList(@RequestBody List list ){
// ma
Map map = new HashMap<>();
System.out.println(list);
map.put("code",2000);
return map;
}
对于很多第三方开发,还是由很多会采用XML作为数据交互,比如微信。
1 添加依赖
com.fasterxml.jackson.dataformat
jackson-dataformat-xml
2.9.3
2 方法返回数据类型定义
// 描述生产的类型,返回类型的描述,返回什么数据
@RequestMapping(value = "/m1",produces = {MediaType.APPLICATION_XML_VALUE} )
@ResponseBody
public User m1(){
// 将数据转换为XML形式user
User u = new User();
u.setName("张福新");
u.setPwd("好好学习,记得看书");
return u;
}
持续更新中。。