键盘敲烂,月薪过万作业不做,等于没学
当前系列: 从JSP到Spring 修改讲义

Dispatcher

复习:

  • Web-INF文件夹下web.xml中配置的DispatcherServlet
  • springmvc-servlet.xml中配置bean的作用

理解:

  • 在SpringMVC中,所有请求由tomcat交DispatcherServlet处理
  • DispatcherServlet读取springmvc-servlet.xml中的配置,再将/login请求交LoginController中唯一方法handleRequest()处理

resource

对.html、.css、.js、图片文件等静态资源,还是按路径直接获取,不需要dispatch。可以:

  • 保留DefaultServlet,指定以上静态资源仍然使用DefaultServlet:
        <servlet-mapping>
            <servlet-name>default</servlet-name>
            <url-pattern>*.html</url-pattern>
            <url-pattern>*.js</url-pattern>
            <url-pattern>/image/**</url-pattern>
        </servlet-mapping>
    注意一定要放置在DispatcherServlet之前
  • 在DispatcherServlet中添加mvc:resources节点(推荐):
    <mvc:resources mapping="*.html" location="/" />
    <mvc:resources mapping="*.js" location="/" />
    <mvc:resources mapping="/images/**" location="/" />


annotation代替xml

使用XML进行配置dispatcher总是不直观的……能不能(和Hibernate一样)用annotation呢?当然可以(而且强烈推荐)。

@Controller和@RequestMapping

可以使用:

  • @Controller标记某个类为Controller(不用任何继承)
  • @RequestMapping("/Login")标记该Controller处理何种URL
@Controller
@RequestMapping("/log")
public class LogController {

如上代码,就向SpringMVC表明:/log的请求,由我LoginController来处理。

但是,由Controller中哪一个方法来处理呢?标记@RequestMapping()的那一个

@RequestMapping()
public ModelAndView handleRequest(HttpServletRequest arg0,

但是,这还不够,还需要

配置springmvc-servlet.xml

无fuck可说,这就是Java框架……

需要我们额外引入

xmlns:context="http://www.springframework.org/schema/context"
并在xsi:schemaLocation中添加:
http://www.springframework.org/schema/context 
http://www.springframework.org/schema/context/spring-context.xsd
最后,加入节点:
<context:component-scan base-package="controllers" />

这样SpringMVC就会去自动扫描base-package下所有被@Controller等注解的Java类,并纳入管理。

断点演示:/login 请求能够到达LoginController。


@RequestMapping

组合

比如我们的url可以有/log/on(登录),也可以有/log/off(退出登录),一个Controller一个method是不是有点浪费?能不能把他们放在同一个Controller中?

这是完全可以的(一样强烈推荐):

@RequestMapping("/log")
public class LogController {

	@RequestMapping("in")
	public ModelAndView In(
	
	@RequestMapping("/off")    //斜杠可有可无
	public ModelAndView Off(
  1. Controller上的@RequestMapping 表明在该控制器中处理的所有方法都是相对于 /log 路径
  2. method上的@RequestMapping 对应着 /log路径之后的“子”路径

method

很多时候同一个url,GET和POST请求需要分别处理,能不能写成两个方法?OK的:
@RequestMapping(value="/in", method = RequestMethod.GET)
public ModelAndView In(HttpServletRequest arg0, HttpServletResponse arg1)  {
	
@RequestMapping(path="/in", method = RequestMethod.POST)
public ModelAndView In(HttpServletRequest arg0) {

注意:

  • 多个注解属性,value(path的别名)不能省略
  • 两个同名方法,还是要用方法签名的不同构成重载
  • 除了GET和POST,还有其他RequestMethod枚举(演示),我们在SpringBoot中详述

这种用法如此常见,以至于后面的SpringMVC直接引入了其简写方式:

@PostMapping("/in")
@GetMapping("/in")

params

params:为带有不同的参数的url指定不同的方法。假如Controller中只有这两个方法:

@GetMapping(path="in", params = {"id"})	
public ModelAndView In(

@GetMapping(path="in", params = {"id", "prepage"})	
public ModelAndView In(
如上所示代码,当url为:
  • /log/in?id=3时,访问第一个方法
  • /log/in?id=3&prepage=/article时,访问第二个方法
  • /log/in?prepage=/article时,报404错误,因为没有匹配单个prepage的方法
  • /log/in时,报404错误,因为没有匹配没有param的方法
即params使用的是“严格”或者“且”的模式,如果声明了id和prepage,那就一定要url中(至少)有id和prepage才能匹配得上。

PS:其他属性还有consumes()、produces()和headers()见SpringBoot。


动态URI

观察当前页面:

你觉得706和825是什么?有什么作用?

他们实际上类似于:

  • /Code/Single.jsp?id=706
  • /Code/Single.jsp?id=825

用户访问的是同一个页面地址,不同的是URL参数;后台根据url参数id的值从数据库获取该id对应的文章……这种url在早期影响SEO,但现在唯一的问题就是看起来不舒服,尤其是url参数不止一个的时候。

所以,SpringMVC允许我们映射这种含有可变内容(path parameter)的请求路径:

  • path parameter用花括号括起来{},既可以括住两个斜杠间的一整段,也可以括住一部分
  • 花括号中由开发人员自定义一个名称,比如{id},以后可以根据该名称获取其值。
  • 可以使用正则表达式指定path parameter的格式,比如:
    • {id:[a-Z]+}:只能是英文字母
    • {id:[1-9]\d*}:正整数

演示:

  • /article/page-{pageIndex:\d*}
  • /article/{id:[1-9]\d*}
  • /article/{category:[a-z]+}

PS:在MVC的Dispatcher机制出现之前,普遍利用tomcat的filter进行映射,这种技术又被称之为urlrewrite


@RequestParam和@PathVariable

如果请求参数和处理方法参数的名称一样的话,@RequestParam 注解的 value 这个参数就可省掉了(强烈推荐,没道理不这样做)

@PathVariable 同 @RequestParam的运行方式不同。你使用 @PathVariable 是为了从 URI 里取到查询参数值。换言之,你使用 @RequestParam 是为了从 URI 模板中获取参数值。


使用技巧

避免重叠

比如以下三种动态URI匹配模板:

  • /article/page-{pageIndex:\d*}
  • /article/{id}
  • /article/{category:[a-z]+}
演示:当请求地址是
  1. /article/csharp时,报ambiguous错误
  2. /article/page-3时,直接进列表分页页面
@想一想@:为什么?
  1. /article/{id}和/article/{category:[a-z]+}两者一样,所以无法决策,只能报错
  2. page-{pageIndex:\d*}的优先级最高,所以可以直接dispatch到分页列表的handler方法

其他

  • 尽量保存Controller的类名、方法名和url路径一致,这样便于代码的维护(方便查找)。PS:借鉴ASP.NET MVC
  • 推荐所有的URL使用常量按集中统一放置在一个类中,避免冲突
  • 如果要考虑SEO的话,不要将多个URL映射到同一个handler(使用redirect)


作业

使用@Controller和@RequestMapping完成以下URL映射:
  1. 文章单页:/Article/Single?id=1 => /Article/1
  2. 文章修改:/Article/Edit?id=7 => /Article/Edit/7
  3. 所有文章列表:/Article/Index?pageIndex=3 => /Article/Page-3
  4. 文章分类列表:/Article/IndexOfCategory?pageIndex=3&category=1 => /Article/Category-1/Page-3
  5. 博主文章列表:/Article/IndexOfAuthor?author=5&pageIndex=2 => /Article/User-5/Page-2
  6. 任务历史月记录:/Task/HistoryOfMonth?year=2019&month=8&id=5 => /Task/History/5/2019/8

根据你的选择,可以用Suggest/Problem代替Article。

学习笔记
源栈学历
大多数人,都低估了编程学习的难度,而高估了自己的学习能力和毅力。

作业

觉得很 ,不要忘记分享哟!

任何问题,都可以直接加 QQ群:273534701

在当前系列 从JSP到Spring 中继续学习:

多快好省!前端后端,线上线下,名师精讲

  • 先学习,后付费;
  • 不满意,不要钱。
  • 编程培训班,我就选源栈

更多了解 加:

QQ群:273534701

答疑解惑,远程debug……

B站 源栈-小九 的直播间

写代码要保持微笑 (๑•̀ㅂ•́)و✧

公众号:源栈一起帮

二维码