0、目录

  • 1、视图解析器配置
  • 2、请求转发与重定向
  • 2.1 转发
  • 2.2 重定向
  • 3、ModelAndView详解
  • 3.1 添加模型数据
  • 3.2 设置视图
  • 3.3 添加模型数据同时设置视图
  • 4、Model,Map传值到页面
  • 4.1 隐含的模型
  • 4.2 对象使用
  • 4.3 JSP页面获取
  • 4.4 三者之间的关系
  • 5、SessionAttributes注解
  • 5.1 注解源码
  • 5.2 使用
  • 5.3 清除
  • 5.4 案例演示
  • 6、ModelAttribute
  • 6.1 修饰方法(无返回值)
  • 6.2 修饰参数
  • 6.3 修饰方法(有返回值)
  • 7、转发与重定向传参
  • 7.1 请求转发到方法的传值
  • 7.2 重定向倒方法的传值
  • 8、静态资源访问
  • 9、annotation-driven配置
  • 9.1 Default Servlet配置
  • 9.2 annotation-driven配置
  • 9.3 二者使用后的效果
  • 10、参考链接


1、视图解析器配置

简单配置

<!--配置视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <!--配置视图前缀-->
    <property name="prefix" value="/view/"/>
    <!--配置视图解析后缀,目前的视图采用JSP-->
    <property name="suffix" value=".jsp"/>
</bean>

2、请求转发与重定向

2.1 转发

一次请求(相同的request),地址栏的URL不会改变常规return默认为转发

1、转发至视图解析器中的JSP
该路径及后缀由解析器负责

/**
     * 1 转发测试:跳转到JSP界面(解析器自动解析)
     * @return
     */
    @RequestMapping("forwardToJsp")
    public String forwardToJsp(){
        System.out.println("转发至根路径JSP,无需带forward,无需写全路径,经由解析器解析");
        //默认return 在不加forward的情况下,就是跳转界面
        return "target";
    }

2、转发至应用根路径中的JSP
不会经由自己指定的解析器解析,只会由基础的解析器处理forward

/**
     * 2 转发测试:跳转到JSP界面(加forward的形式)
     * @return
     */
    @RequestMapping("forwardToFullJsp")
    public String forwardToFullJsp(){
        System.out.println("转发至跟路径JSP,需写全路径,不会经由自己指定的解析器解析");
        return "forward:/view/target.jsp";
    }

3、转发至当前类级别的URL

@RequestMapping("toMethod")
    public String toMethod(){
        System.out.println("toMethod —— forward测试");
        //系统默认就是转发,但是默认的操作只会转发到JSP界面,不会跳转到方法
        return "forward:targetMethod";
    }

    //待转发的目标方法
    @RequestMapping("targetMethod")
    public String targetMethod(){
        System.out.println("待转发的目标方法");
        //系统默认就是转发
        return "target";
    }

4、转发至项目根路径类的URL

/**
     * 从一个方法,转发到另外一个类中的方法中,那只需要写全路径即可
     * @return
     */
    @RequestMapping("forwardToClassMethod")
    public String forwardToClassMethod(){
        System.out.println("forwardToClassMethod —— forward另外一个类测试");
        return "forward:/target/targetMethod";
    }

    //跨类转发-目标类
    @Controller
    @RequestMapping("/target")
    public class TargetController {

        @RequestMapping("targetMethod")
        public String targetMethod(){
            System.out.println("TargetController 目标方法");
            return "target";
        }
    }

5、总结

转发到JSP界面一般不使用forward,也可以借助forward来完成,但需要写全路径,forward解析代码在UrlBasedViewResolver210行进行解析

若需要转发至服务端的其他方法需要借助forward来完成,本类中无需加全路径,直接指明方法映射即可

2.2 重定向

二次请求(不同的request),地址栏的URL会变成重定向之后的URL

1、重定向到项目根路径的jsp

@RequestMapping("toJsp")
    public String toJsp(){
        System.out.println("重定向到jsp");
        return "redirect:/view/target.jsp";
    }

2、重定向到当前类其他方法

@RequestMapping("toMethod")
    public String toMethod(){
        System.out.println("重定向到本类方法");
        return "redirect:toJsp";
    }

3、重定向到项目根路径的方法中(需要写全映射路径,不含工程名)

@RequestMapping("toClassMethod")
    public String toClassMethod(){
        System.out.println("重定向到其他类方法");
        return "redirect:/targetCon/classMethod";
    }

4、重定向到站外

@RequestMapping("toBaidu")
    public String toBaidu(){
        System.out.println("重定向到其他网站");
        return "redirect:http://www.baidu.com";
    }

3、ModelAndView详解

控制器处理方法的返回值如果为ModelAndView,则该返回值及包含模型数据也包含视图对象

需要注意的是,该对象返回的模型数据是存放在request的作用域范围中

3.1 添加模型数据

ModelAndView addObject(String attributeName, @Nullable Object attributeValue)
ModelAndView addAllObjects(@Nullable Map<String, ?> modelMap)

3.2 设置视图

void setView(@Nullable View view)
void setViewName(@Nullable String viewName)

3.3 添加模型数据同时设置视图

ModelAndView(String viewName, String modelName, Object modelObject)

4、Model,Map传值到页面

除了使用ModelAndView方式外。还可以使用Map、Model和ModelMap来向前台页面传值,这3种方式,都是在方法参数中,指定一个该类型的参数

4.1 隐含的模型

如果方法的入参为Map或Model及ModelMap类型,我们可以在方法的形参中加入Map或Model、ModelMap变量,SpringMVC会将隐含的模型对象引用传递给这些入参。

@RequestMapping("/test")
public String test(Map<String,Object> map, Model model, ModelMap modelMap){
    ......
    return "hello";
}

4.2 对象使用

在方法体内,开发者可以通过这个入参对象访问到模型中所有数据,也可以向模型中添加新的属性数据。

@RequestMapping("/test")
public String test(Map<String,Object> map, Model model, ModelMap modelMap){

    map.put("names", Arrays.asList("java","web","html"));
    model.addAttribute("time", new Date());
    modelMap.addAttribute("name", "hliedu");
    modelMap.put("gender", "male");
    return "hello";
}

4.3 JSP页面获取

<br/>1、time:${requestScope.time}
<br/>2、names:${requestScope.names }
<br/>3、city:${requestScope.city }
<br/>4、gender:${requestScope.gender }

4.4 三者之间的关系

  • 关系图
  • Model
    是一个接口,实现类为ExtendedModelMap,该实现类同时继承ModelMap
model.addAttribute("time", new Date());
  • ModelMap
    继承自LinkedHashMap
modelMap.addAttribute("name", "hliedu");
modelMap.put("gender", "male");
  • Map
    JDK集合类,采用put方法传值
  • 三者都为Spring框架自行创建并作为Controller入参,用户无需手动干预,可直接使用

与ModelAndView的区别
ModelAndView功能除了绑定数据外,还承担着解析视图的作用,其内部绑定数据也是依靠ModelMap来进行

5、SessionAttributes注解

如果希望在多个请求之间公用某个模型属性数据,则可以在控制器类上标注一个@SessionAttributes注解,SpringMVC将在模型中对应的属性暂存到HttpSession中。

@SessionAttributes除了可以通过属性名指定需要放到会话中的属性外,还可以通过模型属性的对象类型指定哪些模型需要放到会话中

5.1 注解源码

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface SessionAttributes {
    String[] value() default {};
    Class[] types() default {};
}

通过源码我们知道,该注解是用来修饰Class的

5.2 使用

  • 通过属性key完成session的共享数据设置
  • 通过模型数据的类型完成session的共享数据设置
//会将隐含模型中所有类型为 Emp类型的value添加到会话中
@SessionAttributes(types=Emp.class)

//会将隐含模型中所有类型为 String 类型的value添加到会话中
@SessionAttributes(types=String.class)

//会将隐含模型中key为 emp1和emp2的value添加到会话中
@SessionAttributes(value={"emp1","emp2"})

//会将隐含模型中所有类型为 Emp和Dept 的属性添加到会话中
@SessionAttributes(types={Emp.class,Dept.class})

//会将隐含模型中key为 emp1和emp2的value和Emp类型的value添加到会话中
@SessionAttributes(value={"emp1","emp2"},types=Emp.class)

5.3 清除

可以调用SessionStatus.setComplete来清除

5.4 案例演示

package com.hliedu.springmvc.controller.mvc;

import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.ModelAttribute;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.SessionAttributes;
import org.springframework.web.bind.support.SessionStatus;

@Controller
@RequestMapping("/book")
@SessionAttributes(value ={"book","description"},types={Double.class})
public class SessionAttributeCon {

    @RequestMapping("/index")
    public String index(Model model){
        model.addAttribute("book", "降龙十八掌");
        model.addAttribute("description","见龙在田很厉害");
        model.addAttribute("price", new Double("1000.00"));
        //跳转之前将数据保存到book、description和price中,因为注解@SessionAttribute中有这几个参数
        return "redirect:get";
    }

    @RequestMapping("/get")
    public String get(@ModelAttribute("book") String book, ModelMap model, SessionStatus sessionStatus){
        //可以获得book、description和price的参数
        System.out.println(model.get("book")+";"+model.get("description")+";"+model.get("price"));
        sessionStatus.setComplete();
        return "redirect:complete";
    }

    @RequestMapping("/complete")
    public String complete(ModelMap modelMap){
        //已经被清除,无法获取book的值
        System.out.println(modelMap.get("book"));
        modelMap.addAttribute("book", "书不见了");
        return "satest";

    }
}

6、ModelAttribute

代码

@Target({ElementType.PARAMETER, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface ModelAttribute {
    @AliasFor("name")
    String value() default "";

    @AliasFor("value")
    String name() default "";

    boolean binding() default true;
}

被@ModelAttribute注释的方法会在此controller每个方法执行前被执行,因此对于一个controller映射多个URL的用法来说,要谨慎使用。
ModelAttribute有3种使用情况

6.1 修饰方法(无返回值)

  • 本类中使用
@Controller
@RequestMapping("/modelAttr")
public class ModelAttrController {

    @ModelAttribute 
    public void populateModel(Map<String,Object> map) {
       Object data = "前置对象";//该对象可以是从数据库查询出来的对象
       map.put("attrName", data);  
    }  

    //方法的入参:在进行数据绑定之前,首先会到请求范围当中查找指定的key(POJO类首字母小写对应的单词)对应的value对象
    @RequestMapping(value = "/helloWorld")  
    public String hello(String attrName) {
       System.out.println(attrName);
       return "helloWorld.jsp";  
    }

}

在这个代码中,访问控制器方法helloWorld时,会首先调用populateModel方法,该方法可以执行任意操作,比如从数据库获取数据。再放到map的attrName属性中,从而在hello方法中能够获取到该值。

并且该值也能够在转发的页面中获取到

<%@ page language="java" contentType="text/html; charset=utf-8"
    pageEncoding="utf-8"%>
<html>
<head>
</head>
<body>
    ModelAttribute:${attrName}
</body>
</html>

6.2 修饰参数

上述代码中,我们采用了方法入参与前置对象的key的约定,这种约定在代码复杂的情况下很难发现,所以我们可以采用参数的强制绑定,用法也非常简单,只需要采用ModelAttribute修饰方法即可

@RequestMapping(value = "/helloWorld")  
    public String hello(@ModelAttribute("attrName") String attrName) {
       .....
    }

6.3 修饰方法(有返回值)

@Controller
@RequestMapping("/modelAttr")
public class ModelAttrController {

    @ModelAttribute(value="v_data")
    public Object populateModel(Map<String,Object> map) {
       Object data = "前置对象";//该对象可以是从数据库查询出来的对象
       return data;  
    }  

    @RequestMapping(value = "/helloWorld")  
    public String hello(@ModelAttribute("v_data")Object data) {
       System.out.println(attrName);
       return "helloWorld.jsp";  
    }

}

7、转发与重定向传参

7.1 请求转发到方法的传值

  • 单值参数
@RequestMapping("toMethod")
    public String toMethod(){
        String param = "单值参数";
        return "forward:targetMethod?param="+param;
    }

    @RequestMapping("targetMethod")
    public String target(@RequestParam String param){
        System.out.println("获取到的参数" + param);
        return "target";
    }
  • 复杂对象参数
@RequestMapping("toMethod")
    public String toMethod(HttpServletRequest request){
       request.setAttribute("key" , new Object());
        return "forward:targetMethod";
    }
    @RequestMapping("targetMethod")
    public String target(HttpServletRequest request){
        Object key = request.getAttribute("key");
        return "target";
    }

7.2 重定向倒方法的传值

  • 路径后缀拼接(省略讲解)
  • RedirectAttributes

RedirectAttributes是SpringMVC3.1版本之后出来的一个功能,专门用于重定向之后还能带参数跳转的,使用该注解可以在不改变URL的情况下将参数传递给另外一个方法

其原理采用的是session数据共享技术,先将属性放置会话中,待下一个方法获取到之后就立即删除session中的该属性

@RequestMapping("toMethod")
    public String toMethod(RedirectAttributes attr){
        attr.addFlashAttribute("user1" , new User("张三丰",100));
        attr.addFlashAttribute("user2" , new User("张无忌",30));
        return "redirect:toJsp";
    }

    @RequestMapping("toJsp")
    public String toJsp(@ModelAttribute("user1") User user1,
                        @ModelAttribute("user2") User user2){
        System.out.println(user1);
        System.out.println(user2);
        return "redirect:/view/target.jsp";
    }

8、静态资源访问

<!--/images /**映射到 ResourceHttpRequestHandler 进行处理-->
<!--location指定静态资源的位置.可以是web application根目录下、jar包里面-->
<mvc:resources mapping="/static/**" location="/static/"/>
<mvc:annotation-driven />
<!doctype html>
<html lang="en">
<head>
    <title>jquery</title>
    <script type="text/javascript" src="http://localhost:8080/springmvc04/static/jquery-1.9.1.js"></script>
    <script>
        $().ready(function(){
            alert(123);
        });
    </script>
</head>
<body>
<h2>Forward Target JSP!</h2>
</body>
</html>

如果出现下面的错误,可能是没有配置 <mvc:annotation-driven /> 的原因。

//报错WARNING: No mapping found for HTTP request with URI [/springmvc04/770] in DispatcherServlet with name 'springMVC'

9、annotation-driven配置

9.1 Default Servlet配置

我们先来学习Default Servlet

1、官方介绍:

原话:Spring MVC allows for mapping the DispatcherServlet to / (thus overriding the mapping of the container’s default Servlet), while still allowing static resource requests to be handled by the container’s default Servlet. It configures aDefaultServletHttpRequestHandler with a URL mapping of /** and the lowest priority relative to other URL mappings.

This handler forwards all requests to the default Servlet. Therefore, it must remain last in the order of all other URL HandlerMappings. That is the case if you use mvc:annotation-driven. Alternatively, if you set up your own customized HandlerMapping instance, be sure to set its order property to a value lower than that of the DefaultServletHttpRequestHandler, which is Integer.MAX_VALUE.

理解:在我们配置核心控制器DispatcherServlet对应的映射路径为/后,SpringMVC 将捕获WEB 容器的所有请求, 包括静态资源的请求, SpringMVC 会将他们当成一个普通请求处理, 因找不到对应处理器将导致错误。所以我们可以通过配置mvc:default-servlet-handler/的方式解决静态资源的问题。

default-servlet-handler会在SpringMVC上下文中定义一个DefaultServletHttpRequestHandler类,该类会将所有进入核心控制器的请求进行筛选,如果发现是没有经过映射处理的请求,将该请求交由 WEB 应用服务器默认的Servlet处理。 如果不是静态资源的请求, 才由 DispatcherServlet 继续处理。
2、XML实现

<mvc:default-servlet-handler/>

3、代码实现

@Configuration
@EnableWebMvc
public class WebConfig implements WebMvcConfigurer {

    @Override
    public void configureDefaultServletHandling(DefaultServletHandlerConfigurer configurer) {
        configurer.enable();
    }
}

一般WEB应用的服务器默认的Servlet名称都是default,如果所使用的WEB服务器默认的Servlet名称不是default,则需要显示的在default-servlet-handler属性中指定,如下:

<mvc:default-servlet-handler default-servlet-name="myCustomDefaultServlet"/>

9.2 annotation-driven配置

官方文档:Enabling the MVC Java Config or the MVC XML Namespace annotation-driven

作用描述

1、会自动注册RequestMappingHandlerMapping、RequestMappingHandlerAdapter、ExceptionHandlerExceptionResolver三个bean支持使用了像@RquestMapping、ExceptionHandler等等的注解的controller 方法去处理请求。
2、支持使用了ConversionService的实例对表单参数进行类型转换。
3、支持使用@NumberFormat、@NumberFormat注解对数据类型进行格式化。
4、支持使用@Valid对javaBean进行JSR-303验证。
5、支持使用@RequestBody、@ResponseBody。

9.3 二者使用后的效果

通过上面的学习,我们知道,这俩东西无非是解决Servlet注解,映射,转换,校验等等问题的,但我们在之前的课程中都没有学过这俩标签,那MVC是如何工作的呢?

1、既没有配置mvc:default-servlet-handler也没有配置mvc:annotation-driven标签(静态资源无法加载)

SpringMVC 通过controller 跳转到html页面 springmvc实现页面跳转_重定向

  • HttpRequestHandlerAdapter:处理已实现HttpRequestHandler接口的handler,并且会调用handler中重写的
  • SimpleControllerHandlerAdapter:处理已实现Controller接口的handler,并且会调用handler中重写的handleRequest方法
  • RequestMappingHandlerAdapter:真正能够处理我们的handler方法的处理适配器

2、只配置了mvc:default-servlet-handler`(可以加载静态资源,但无法处理RequestMapping映射)

SpringMVC 通过controller 跳转到html页面 springmvc实现页面跳转_System_02


3、只配置了mvc:annotation-driven标签(需声明mvc:resources加载静态资源,也可以处理RequestMapping映射,最推荐的做法)

SpringMVC 通过controller 跳转到html页面 springmvc实现页面跳转_spring_03


4、俩标签都同时配置,与2者都不配置的效果一样(根路径请求经过两次,静态资源自动放行,无需手动配置mvc:resources)

SpringMVC 通过controller 跳转到html页面 springmvc实现页面跳转_Java_04


5、注意:老版本的AnnotationMethodHandlerAdapter已在4.X废弃,5.X彻底删除,功能由RequestMappingHandlerMapping代替

6、总结:我们应该采用mvc:annotation-driven和mvc:resources来配置SpringMVC框架

10、参考链接

【01】页面跳转