SpringMVC学习笔记

依赖

SpringMVC属于Spring框架里的web模块,因此导包时,不仅要导入SpringWeb模块的包,还要导入核心包。

配置文件

web.xml

  1. 配置DispatcherServlet,给DispatcherServlet配置springmvc配置文件的位置。如果不指定的话,会从/WEB-INF目录下寻找springmvc配置文件的位置。

springmvc.xml

  1. 开启包扫描
  2. 配置视图解析器

开始

  1. 新建一个类,打上@Controller注解,告诉SpringMVC这是一个控制器。
  2. 新建一个方法,打上@RequestMapping注解,用来处理一个url的请求。
  3. 通过return一个字符串的方式,可以转发到一个新的页面。
  4. 通过配置视图解析器,可以实现SpringMVC自动将返回值和前后缀进行拼串。
controller:
@Controller
public class MyFirstController {
    @RequestMapping("/hello")
    public String helloWorld(){
        System.out.println("收到请求,正在处理");
        return "success";
    }
}
--------------------------------------------------------------------
springmvc.xml:
<context:component-scan base-package="ltc"></context:component-scan>
<bean class="org.springframework.web.servlet.
                view.InternalResourceViewResolver">
    <property name="suffix" value=".jsp"/>
</bean>

RequestMapping

可以打在类定义上,也可以打在方法上。当打在类定义上时,表示为类中所有映射路径添加了一个基准路径。例:

@Controller
@RequestMapping("/hello")
public class RequestMappingTestController {
    @RequestMapping("/world")
    public String helloWorld(){
        System.out.println("Hello World");
        return "helloworld";
    }
}

此时要访问helloWorld方法的路径是应该"/hello/world"
PS:

  1. 这里的RequestMapping里的路径即使不加上"/",访问路径也是"/hello/world",因为类定义上的RequestMapping添加的是基准路径,而不是字符串拼接。

RequestMapping的其他属性:

  1. method:规定访问的请求方式(GET、POST等)
  2. params:规定请求的参数
  • params="param"请求参数必须带有param
  • params="!param"请求参数不能带有param
  • params="param=xxx"请求参数必须带有param,且值必须为xxx
  • params="param!=xxx"请求参数不能为xxx,不带param参数,或者param!=xx都可以
  • params={"param1=xxx","param2"}多个规则
  1. headers:规定请求头
@RequestMapping(value = "/world02",headers = "User-Agent=Mozilla/5.0 (Windows NT 10.0; Win64; x64) " + 
"AppleWebKit/537.36 (KHTML, like Gecko) Chrome/75.0.3770.142 Safari/537.36")
public String testHeaders(){
    return "success";
}

这段代码规定了只接受来自谷歌浏览器的请求

ANT风格的URL

通过"?"、"*"、"**"来实现url的模糊匹配。
?:匹配任意一个字符
*:匹配0或多个字符,亦或是一层路径
**:匹配多层路径
PS:当多个url都匹配时,更加精确的优先
精确匹配 >> ?匹配 >> *匹配 >> **匹配

@PathVariable获取路径上的占位符

通过在路径上使用占位符{变量名},可以在方法的参数上打上@PathVariable来获取到占位符上的变量。

@RequestMapping("/{id}")
public String testPathVariable(@PathVariable("id")String id){
    System.out.println(id);
    return "success";
}

Rest风格的URL

以往风格的url:

  • /getBook?id=1
    查询书籍
  • /addBook
    添加书籍
  • /deleteBook?id=1
    删除书籍
  • /updateBook?id=1&info1=xxx&info2=xxx
    更新书籍

REST风格的url:

  • /book/1 get方式
    查询书籍
  • /book post方式
    添加书籍
  • /book/1 delete方式
    删除书籍
  • /book/1 put方式
    更新书籍

REST风格的特点:
以简洁的url来提交请求,以提交请求的方式来处理资源。

存在的问题:
页面只能发送get和post请求

解决方法:
在post表单中添加"_method"的参数,通过配置HiddenHttpMethodFilter过滤器,
来实现从post方式到指定方式的转化。

Controller配置:
-----------------------------------------------------------------------------
@RequestMapping(value = "/book/{id}",method = RequestMethod.GET)
public String getBook(@PathVariable("id")Integer id){
    System.out.println("查询了" + id + "号图书!");
    return "success";
}
@RequestMapping(value = "/book",method = RequestMethod.POST)
public String addBook(){
    System.out.println("添加了图书!");
    return "success";
}
@RequestMapping(value = "/book/{id}",method = RequestMethod.DELETE)
public String deleteBook(@PathVariable("id")Integer id){
    System.out.println("删除了" + id + "号图书!");
    return "success";
}
@RequestMapping(value = "/book/{id}",method = RequestMethod.PUT)
public String updateBook(@PathVariable("id")Integer id){
    System.out.println("更新了" + id + "号图书!");
    "success";
}
-----------------------------------------------------------------------------
jsp页面:
-----------------------------------------------------------------------------
<a href="/book/1">查询1号图书</a>
<form method="post" action="/book">
    <input type="submit" value="添加图书"/>
</form>
<form method="post" action="/book/1">
    <input name="_method" value="delete"/>
    <input type="submit" value="删除1号图书"/>
</form>
<form method="post" action="/book/1">
    <input name="_method" value="put"/>
    <input type="submit" value="更新1号图书"/>
</form>
-----------------------------------------------------------------------------
web.xml配置:
-----------------------------------------------------------------------------
<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>

PS:
高版本tomcat在将PUT或者DELETE请求转发给jsp页面时,会提示PUT和DELETE请求不支持,这时要在jsp声明添加属性isErrorPage="true"

<%@ page language="java" contentType="text/html; utf-8"
        pageEncoding="utf-8" isErrorPage="true" %>

Controller获取参数的方式

1. 直接在方法上定义名字和页面传来参数一样的入参。
public String testParam(String username){
    System.out.println("参数为:" + username);
    return "success";
}

如上的方法,能接受名为username的参数。

  • 当页面带来的参数有值时,username的值即为那个值。
  • 当页面传来的参数无值时(username=),username的值为空。
  • 当页面传来的参数不包含username时,username为null。
2. 通过@RequestParam来获取指定名字的参数。
@RequestMapping("/paramtest")
public String testParam(@RequestParam("user") String username){
    System.out.println("参数为:" + username);
    return "success";
}

如上的方法,能接受名为user的参数
@RequestParam有以下参数:

  • value:即接受的参数的名字
  • name:即value,value和name互为别名,且只能同时指定一个。
  • required:是否是必要的,默认为true,即不带指定参数时报错。
  • defaultValue:默认值,即不带user时,提供的默认值。

PS:@RequestParam和@PathVariable的区别:

  • @RequestParam:是从请求的参数里取的值
  • @PathVariable:是从请求地址的占位符里取得值
  • 两者都不能从对方的域中取值。
3. 通过@RequestHeader来获取请求头中的参数。
@RequestMapping("/headertest")
public String testHeader(@RequestHeader("User-Agent") String userAgent){
    System.out.println("使用的浏览器是" + userAgent);
    return "success";
}

如上方法可以获取请求头中User-Agent的信息。
@RequestHeader包含的参数和@RequestParam相同。

  • value:报文头中的参数
  • name:即value,value和name互为别名,且只能同时指定一个。
  • required:是否是必要的,默认为true,即不带指定参数时报错。
  • defaultValue:默认值,即不带指定参数时,提供的默认值。
4. 通过@CookieValue来获取请求头中的参数。
@RequestMapping("/cookietest")
public String testCookie(@CookieValue(value = "JSESSIONID", required = false, defaultValue = "123")String jid){
    System.out.println("Cookie中的jid为" + jid);
    return "success";
}

如上方法可以获取Cookie中JSESSIONID的值
@CookieValue参数和上面的相同。

5. 自动封装传入的参数为POJO

当设置请求的参数为POJO时,SpringMVC会尝试封装POJO的每一个属性,甚至是级联的属性,只要在表单设置好每个参数的名字,SpringMVC就能自动封装为POJO。当传入参数找不到POJO里的属性时,该属性就无法赋值。

Controller:
---------------------------------------------------------------
@RequestMapping(value = "/book",method = RequestMethod.POST)
public String addBook(Book book){
    System.out.println(book);
    return "success";
}
---------------------------------------------------------------
Book类:
---------------------------------------------------------------
public class Book {
    private String bookName;
    private String author;
    private Double price;
    private Address address;
}
---------------------------------------------------------------
Address类:
---------------------------------------------------------------
public class Address {
    private String province;
    private String city;
}
---------------------------------------------------------------
页面表单:
---------------------------------------------------------------
<form method="post" action="/book">
    书名:<input name="bookName" /><br/>
    作者:<input name="author"/><br/>
    价格:<input name="price"/><br/>
    <hr/>
    省份:<input name="address.province"/><br/>
    城市:<input name="address.city"/><br/>
    <input type="submit"/>
</form>
5. servlet原生API的使用

只要在Controller方法的入参声明了,就可以在方法内直接使用。

@RequestMapping("/originalapi")
public String usingOriginalApi(HttpServletRequest request,
                               HttpServletResponse response, HttpSession session){
    request.setAttribute("message", "this is request");
    session.setAttribute("message", "this is session");
    return "show";
}

如上,在入参声明了HttpServletRequest、HttpServletResponse、HttpSession就可以直接在方法内使用了。

6. 解决中文乱码问题
  1. get请求乱码
    通过在tomcat的server.xml配置文件中,配置URIEncoding="UTF-8"来解决get请求的乱码。
<Connector port="8080" protocol="HTTP/1.1"
            connectionTimeout="20000"
            redirectPort="8443"
            URIEncoding="UTF-8"/>
  1. post请求乱码
    其他请求因为也是从post请求包装来的,因此处理方法与post相同。解决方法如下:
    通过配置SpringMVC自带的CharacterEncodingFilter过滤器来解决乱码问题。
<filter>
    <filter-name>CharacterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <!--这里的encoding指定的是要转换成的编码格式-->
    <init-param>
        <param-name>encoding</param-name>
        <param-value>UTF-8</param-value>
    </init-param>
    <!--这里的forceEncoding决定的事是否转换respone的编码格式-->
    <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>

源码中对这段转换的操作是这样的

protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException {
    if (this.encoding != null && (this.forceEncoding || 
                        request.getCharacterEncoding() == null)) {
        request.setCharacterEncoding(this.encoding);
        if (this.forceEncoding) {
            response.setCharacterEncoding(this.encoding);
        }
    }
    filterChain.doFilter(request, response);
}

PS:
    这里要注意的是,如果同时也配置了HiddenHttpMethodFilter,一定要将CharacterEncodingFilter配置在HiddenHttpMethodFilter前面(先对请求进行编码转换),自己配置的时候就是因为这个问题导致乱码的问题无法解决。
    我个人的理解是,在经过HiddenHttpMethodFilter包装后,被转发的不在是原本的request了,而此时对原本的request再进行编码转换是没有用的。

百度到的解释

这是因为(至少在tomcat里)

  • request对象的parameter并不是一开始就解析的,它是等你第一次调用“getParameter*等凡是 一切和获得请求参数有关的方法的时候才解析的
  • paramter一旦被解析过一次,那就不会再次被解析
  • 所以如果在CharacterEncodingFilter之前有另外一个filter,而这个filter调用了getParameter()方法,那么 就有可能使用错误的encoding来解析,从而曹成乱码问题。
    http://ju.outofmemory.cn/entry/133399
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,099评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,828评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,540评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,848评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,971评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,132评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,193评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,934评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,376评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,687评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,846评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,537评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,175评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,887评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,134评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,674评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,741评论 2 351

推荐阅读更多精彩内容

  • 一、SpringMVC简介1.什么是MVCModel:数据模型View:视图Controller:控制器 MVC是...
    青年心路阅读 2,516评论 0 1
  • springmvc学习笔记 学习springmvc框架原理 包括哪些组件: 前端控制器(中心) 处理器映射器(三大...
    罗志贇阅读 1,171评论 0 51
  • 第二部分 注解 一、分层标记 @RestController 标记控制层,在 Spring4 以后引入的复合注解,...
    页川叶川阅读 408评论 0 1
  • 强烈推荐使用Spring Boot,搭建Spring Web项目,进入网址,把WEB依赖打钩,点击生成项目,会下载...
    tenlee阅读 1,586评论 3 44
  • 1.SpringMVC中常用的几个注解 1)@Controller 表明当前类为Controller2)@Requ...
    _chubby阅读 1,794评论 0 29