上一篇文章让我们初步的了解了Spring MVC与Servlet&JSP处理请求的不同,以及简单的学习了Spring MVC处理请求的方式。这一章我们来学习些它的常用的知识点。

常用知识点

定义Controller

@Controller
public class OwnerController {

    @GetMapping("/hello/{petId}")
    public String hello() {
        // ...
    }
}

处理请求

在Controller的方法上添加@RequestMapping注解

@RequestMapping("/hello")
    public String hello() {
        return "hello";
    }

获取请求参数

请求参数可以直接定义到方法参数里,并通过@RequestParam("key")注解修饰参数,这样SpringMVC会自动解析请求中的参数给你填充到方法参数中。

**注意:**此注解修饰的参数默认是必传的,如果请求中没有此参数会直接报错,可以通过设置此注解的required属性为false解决

@RequestMapping("/login")
    public String test(int age, String name, @RequestParam(value = "nickname", required = false) String nickName, Model model) {
        System.out.println("login, age: " + age+", name: " + name+", nickName: " + nickName);
        // 放到request作用域中,可以在前端获取
        model.addAttribute("msg", age);
        return "main";
    }

JDK1.8以后我们也可以通过添加编译参数-parameters参数告诉编译器在编译时保留方法参数名,这样如果请求中参数名与方法定义的参数名对应时,可以不用显示添加@RequestParam注解

获取请求头参数

如果想获取请求头中携带的参数,springmvc给我们提供了一个@RequestHeader注解,此注解和@RequestParam一样默认参数必传,同样可以选择使用required属性解决,使用方式如下:

@RequestMapping("/header")
public String getVarFromRequestHead(@RequestHeader(value = "sname", required = false) String sname, Model model) {
    model.addAttribute("sname", sname);
    System.out.println("sname: " + sname);
    return "hello";
}

一般请求头参数的用法测试需要用到专门的发送请求的工具,我使用的是PostMan这款软件,用法是新建请求后在Head里添加参数sname,访问地址是:http://localhost:8080/header

获取URI中的变量

这个作用通常用于restFul服务,可以通过@PathVariable注解获取到请求uri中的变量

@RequestMapping("/path_var/{sname}/{age}")
public String getVarFromUri(@PathVariable("sname") String sname, @PathVariable("age") Integer age, Model model) {
    model.addAttribute("sname", sname);
    model.addAttribute("age", age);
    System.out.println("sname: " + sname + "age: " + age);
    return "hello";
}

获取Cookie数据

Cookie中的数据同样可以获取到,只需要一个@CookieValue注解就能做到,示例如下:

@RequestMapping("/cookie/attr")
public String getAttrFromCookie(@CookieValue("sname") String sname, Model model) {
    model.addAttribute("sname", sname);
    System.out.println("sname: " + sname);
    return "hello";
}

测试我们还是选择使用刚才提到的postMan测试工具,新建请求后,添加一个Cookie后只需要修改cookie的key和value就行,访问路径:http://localhost:8080/cookie/attr

获取作用域数据

这里的作用域主要指request 作用域和session作用域,不包括application作用域

获取request作用域数据

可以使用@RequestAttribute注解获取作用域中的对应属性的值,示例:

@RequestMapping("/scope/request/attr")
public String getAttrFromScope(@RequestAttribute(value = "sname", required = false) String sname, Model model) {
    model.addAttribute("sname", sname);
    System.out.println("sname: " + sname);
    return "hello";
}

这个测试就需要在jsp页面添加一个属性到request作用域中

<% request.setAttribute("sname","张三"); %>

访问路径:http://localhost:8080/scope/request/attr

获取session作用域数据

可以使用@SessionAttribute注解获取作用域中的对应属性的值,示例:

@RequestMapping("/scope/session/attr/")
public String getAttrFromScope(@SessionAttribute(value = "sname", required = false) String sname, Model model) {
    model.addAttribute("sname", sname);
    System.out.println("sname: " + sname);
    return "hello";
}

这个测试就需要在jsp页面添加一个属性到request作用域中

<% session.setAttribute("sname","张三"); %>

访问路径:http://localhost:8080/scope/session/attr

使用提前初始化的数据

初始化数据、参数,在方法上添加@ModelAttribute注解。

/**
 * 使用一个Model中提供的Map
 */
@ModelAttribute
public Model prepareData() {
    Model model = new ConcurrentModel();
    model.asMap().put("sname", "sss");
    return model;
}
/*
或者使用一个HashMap

@ModelAttribute
public Map<String, String> prepareData() {
    Map<String, String> data = new HashMap<>();
    data.put("sname", "张四丰");
    return data;
}*/

使用当前Controller中通过@ModelAttribute注解预初始化的数据

@RequestMapping("/model/attr")
public String testModelAttr(@ModelAttribute Model model, Model modelToView) {
    modelToView.addAttribute("sname", model.asMap().get("sname"));
    System.out.println("model key: sname->" + model.asMap().get("sname"));
    return "hello";
}
/*
直接使用HashMap的方式
 
@RequestMapping("/model_attr")
public String testModelAttr(@ModelAttribute Map<String, String> data, Model model) {
    System.out.println("model key: sname ->" + data.get("sname"));
    model.addAttribute("sname", data.get("sname"));
    return "hello";
}*/

**注:**如果使用Model提供的asMap方法,则测试时的第二个Model参数不能省略,因为使用@ModelAttribute注解后Model会变成一个只具有Map功能的Model,不再具有与视图解析器传递参数的功能,因此,此时如果想把数据放到作用域中传到前端,则只能再加一个Model参数。

测试的访问路径:http://localhost:8080/model/attr

请求参数自动绑定到对象中

如果RequestMapping方法的参数中是一个自定义的对象,Spring会调用DataBinder自动将请求中的参数注入到对象的同名属性中。
使用示例:

@RequestMapping("/test/bind_obj")
    public String testBindObject(Student student, Model model) {
        System.out.println("testBindObject, student: " + student);
        model.addAttribute("msg", student);
        return "main";
}

访问地址:

http://localhost:8080/test/bind_obj?sname=张三&age=34&id=2

通用无逻辑页面跳转

如果我们有些请求只是想跳转页面,不需要来后台处理什么逻辑,我们无法在Action中写一个空方法来跳转,直接在中配置一个如下的视图跳转控制器即可(不经过Action,直接跳转页面)

<mvc:view-controller path="/" view-name="home"/>

重定向

请求处理方法返回的视图名前添加redirect前缀

内部地址:

return "redirect:/myapp/some/resource"

外部地址:

return "redirect:https://myhost.com/some/arbitrary/path"

请求处理方法返回RedirectView对象

内部地址:

return new RedirectView("/myapp/some/resource");

外部地址:

return new RedirectView("https://myhost.com/some/arbitrary/path");

请求转发

请求处理方法返回的视图名前添加forward前缀

示例:

return "forward:/WEB-INF/pages/main";

请求处理方法返回InternalResourceView对象

直接返回一个页面的名称,默认会使用forward方式通过视图解析器转发到对应的页面,示例:

return "main";

放行静态资源

默认Tomcat是可以处理静态资源的(通过DefaultServlet处理,在%tomcat_home%/conf/web.xml中),但是我们配置了SpringMVC后, 往往会将DispatcherServlet的拦截路径设置成"/",会将Tomcat中默认的DefaultServlet覆盖掉,所以静态资源也会进入DispatcherServlet中处理,而默认DispatcherServlet中是没有处理静态资源相关的逻辑, 所以访问静态资源就会报404

第一种解决方式:

<mvc:default-servlet-handler />

将静态资源交还给容器来处理(比如说Tomcat就是让DefaultServlet来处理)

  • 优点:配置简单
  • 缺点:只能访问web根目录下公开的静态文件, WEB-INF、类路径下都无法访问

第二种解决方式:

<mvc:resources mapping="/static/views/**" location="WEB-INF/views/,classpath:/static/" />

放行静态资源, mapping匹配你请求的URI; location指定静态资源相对位置,会将mapping中通配部分的内容拼到location指定的相对位置后面

到此,我们已经基本上可以处理前后端交互时的常见请求了,那么Spring MVC就这点东西了吗?当然不可能,所以下一章就是一些进阶的知识了。