Back

SpringMVC(一)

SpringMVC的介绍与基本使用

Spring MVC(一)

Spring MVC概括

  1. 什么是MVC

    • MVC 是模型(Model)、视图(View)、控制器(Controller)的简写,其核心思想是通过将业务逻辑、数据、显示分离来组织代码。

      image01

    • MVC主要作用是降低了视图与业务逻辑间的双向偶合

    • MVC不是一种设计模式,MVC是一种架构模式。当然不同的MVC存在差异

    Model(模型):数据模型,提供要展示的数据,因此包含数据和行为,可以认为是领域模型或者JavaBean组件(包括数据和行为),不过现在一般分离开来:Value Object(数据Dao) 和服务层(行为Service)。也就是模型提供了模型数据查询和模型数据的状态更新等功能,包括数据和业务。

    View(视图):负责进行模型的展示,一般就是我们见到的用户界面,用户想看到的东西。

    Conttroller(控制器):接收用户请求,委托给模型进行处理(状态改变),处理完毕后把返回的模型数据返回给视图,由视图负责 展示、也就是说控制器做了个调度员的工作。

    最典型的MVC就是JSP+Servlet+JavaBean

    image02

  2. Model1

    在Web开发早期通常采用Model1模式进行开发。

    在Model1模式下,整个Web应用几乎全部用JSP页面组成,只用少量的JavaBean来处理数据库连接、访问等操作

    这个模式下JSP即使控制层(Controller)又是视图层(View)。这明显不符合Java的单一职责原则。比如控制逻辑和表现逻辑混杂在一起,导致代码重用率极低;再比如前端和后端相互依赖,难以进行测试维护并且开发效率极低。

    mvc-model1

​ Model1优点:架构简单,适合小型项目开发;

​ Model1缺点:JSP职责不单一,,职责过重,不便于维护

  1. Model2

    Model2就是上面所述的Java Bean+JSP+Servlet这种开发模式,这就是早期的JavaWeb MVC开发模式。

    • Model:系统涉及的数据,也就是dao与bean
    • View:展示模型中的数据,只是用来展示
    • controller:处理用户请求发送给Model,然后Model返回数据的处理结果给JSP并展示给用户

    mvc-model2

    职责分析:

    Controller:控制器

    1. 取得表单数据
    2. 调用业务逻辑
    3. 转向指定的页面

    Model:模型

    1. 业务逻辑
    2. 保存数据的状态

    View:视图

    1. 显示页面

    Model2模式下还存在着很多问题,Model2的抽象和封装程度还远远不够,使用Model2进行开发时不可避免地重复造轮子,这就大大降低了程序的可维护性和复用性。

    于是,很多JavaWeb开发相关的MVC框架应运而生,比如Stuts2,但是Struts2比较笨重。

  2. Spring MVC时代

    随着Spring轻量级开发框架1的流行,Spring生态圈出现了Spring MVC框架,Spring MVC框架是当前最优秀的MVC框架。相比于Struts2,Spring MVC使用更加啊简单和方便,开发效率更高,并且Spring MVC运行速度更快。

    MVC是一种设计模式,Spring MVC是一款很优秀的MVC框架。Spring MVC可以帮我们更简洁Web层的开发,并且它天生与Spring框架集成。Spring MVC下我们一般把后端项目分为Service层(处理业务)、dao层(数据库操作)、Entity层(实体类)、Controller层(控制层)

Spring MVC工作原理

Spring MVC的原理如下图所示:

springmvc-yuanli

  • 流程说明:
    1. 客户端(浏览器)发送请求,直接请求到DispatcherServlet
    2. DispatcherServlet根据请求信息调用HandlerMapper,解析请求对应的Handler
    3. 解析到对应的Handler(也就是Controller控制器)后,开始由HandlerAdapter适配器处理。
    4. HandlerAdapter会根据Handler来调用真正的处理器来处理请求,并处理相应的业务逻辑。
    5. 处理器处理完业务后,会返回一个ModelAndView对象,Model是返回的数据对象,View是逻辑上的View。
    6. ViewResolver会根据逻辑View查找实际的View
    7. DispaterServlet把返回的Model传给View(视图渲染)。
    8. View返回给请求者(浏览器)。

第一个Spring MVC程序

使用配置实现

  1. 创建并部署项目springmvc-02-hello;导入依赖

  2. 配置web.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
        <!--注册dispatchServlet-->
        <servlet>
            <servlet-name>springmvc</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <!--关联一个1spring的配置文件-->
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:springmvc-servlet.xml</param-value>
            </init-param>
            <!--启动级别 1-->
            <load-on-startup>1</load-on-startup>
        </servlet>
        <!--/-匹配所有请求:不包括.jsp-->
        <!--/*-匹配所有请求:包括.jsp-->
        <servlet-mapping>
            <servlet-name>springmvc</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    </web-app>
    
  3. 编写SpringMVC的配置文件:springmvc-servlet.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
        <!--添加处理映射器-->
        <bean class="org.springframework.web.servlet.handler.BeanNameUrlHandlerMapping"></bean>
        <!--添加处理适配器-->
        <bean class="org.springframework.web.servlet.mvc.SimpleControllerHandlerAdapter"></bean>
    
        <!--添加视图解析器-->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
            <!--前缀-->
            <property name="prefix" value="/WEB-INF/jsp/"></property>
            <!--后缀-->
            <property name="suffix" value=".jsp"></property>
        </bean>
        <!--Handler-->
        <bean id="/hello" class="com.heng.controller.HelloController"></bean>
    </beans>
    
  4. 编写操作业务的Controller控制器,要么实现Controller接口,要么增加注解;需要返回一个ModelAndView,装数据,封视图。

    package com.heng.controller;
    
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.mvc.Controller;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    /**
     * @Author: minster
     * @Date: 2021/11/10 9:38
     */
    public class HelloController implements Controller {
    
        @Override
        public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
            //ModelAndView模型和视图
            ModelAndView mv = new ModelAndView();
            //封装对象,放在ModelAndView中。Model
            mv.addObject("msg","HelloSpringMVC!");
            //封装要跳转的视图,放在ModelAndView中
            mv.setViewName("hello");
            return mv;
        }
    }
    
  5. 在spring中注册上面的类

    </bean>
    <!--Handler-->
    <bean id="/hello" class="com.heng.controller.HelloController"></bean>
    
  6. 编写jsp文件

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>HelloSpringMVC</title>
    </head>
    <body>
    ${msg}
    </body>
    </html>
    
  7. 测试

    hellospringmvc

使用注解实现

  1. 新建一个Moudle,springmvc-03-annotation。添加web支持,导入依赖

  2. 由于Maven可能存在资源过滤问题,我们将其配置完善

    
    <build>
       <resources>
           <resource>
               <directory>src/main/java</directory>
               <includes>
                   <include>**/*.properties</include>
                   <include>**/*.xml</include>
               </includes>
               <filtering>false</filtering>
           </resource>
           <resource>
               <directory>src/main/resources</directory>
               <includes>
                   <include>**/*.properties</include>
                   <include>**/*.xml</include>
               </includes>
               <filtering>false</filtering>
           </resource>
       </resources>
    </build>
    
  3. 配置web.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
             xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
             version="4.0">
        <!--注册servlet-->
        <servlet>
            <servlet-name>springmvc</servlet-name>
            <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
            <!--关联springmvc的配置文件-->
            <init-param>
                <param-name>contextConfigLocation</param-name>
                <param-value>classpath:springmvc-servlet.xml</param-value>
            </init-param>
            <!--设置启动顺序-->
            <load-on-startup>1</load-on-startup>
        </servlet>
        <!--所有请求都会被springmvc拦截-->
        <servlet-mapping>
            <servlet-name>springmvc</servlet-name>
            <url-pattern>/</url-pattern>
        </servlet-mapping>
    </web-app>
    

    / 和 /* 的区别:

    < url-pattern > / </ url-pattern > 不会匹配到.jsp, 只针对我们编写的请求;即:.jsp 不会进入spring的 DispatcherServlet类 。

    < url-pattern > /* </ url-pattern > 会匹配 *.jsp,会出现返回 jsp视图 时再次进入spring的DispatcherServlet 类,导致找不到对应的controller所以报404错。

  4. 添加Spring MVC的配置文件

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
           xmlns:mvc="http://www.springframework.org/schema/mvc"
           xmlns:context="http://www.springframework.org/schema/context"
           xsi:schemaLocation="http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans.xsd
           http://www.springframework.org/schema/mvc
           http://www.springframework.org/schema/mvc/spring-mvc.xsd
           http://www.springframework.org/schema/context
           http://www.springframework.org/schema/context/spring-context.xsd">
        <!--自动扫描包,让指定包下的注解生效,由IOC统一管理-->
        <context:component-scan base-package="com.heng.controller"></context:component-scan>
        <!--让SpringMVC不处理静态资源-->
        <mvc:default-servlet-handler></mvc:default-servlet-handler>
        <!--支持mvc注解驱动-->
        <mvc:annotation-driven></mvc:annotation-driven>
        <!--视图解析器-->
        <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
            <!--前缀-->
            <property name="prefix" value="/WEB-INF/jsp/"></property>
            <!--后缀-->
            <property name="suffix" value=".jsp"></property>
        </bean>
    </beans>
    

    **<mvc:annotation-driven></mvc:annotation-driven>**作用:

    在spring中一般采用@RequestMapping注解来完成映射关系要想使@RequestMapping注解生效必须向上下文中注册DefaultAnnotationHandlerMapping和一个AnnotationMethodHandlerAdapter实例

    这两个实例分别在类级别和方法级别处理。而annotation-driven配置帮助我们自动完成上述两个实例的注入。

  5. 创建Controller

    package com.heng.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    /**
     * @Author: minster
     * @Date: 2021/11/10 15:16
     */
    @Controller
    @RequestMapping("/HelloController")
    public class HelloController {
        //真实访问地址 : 项目名/HelloController/hello
        @RequestMapping("/hello")
        public String hello(Model model){
            model.addAttribute("msg","helloSpringMVCAnnotation");
            return "hello";
        }
    }
    
    • @Controller是为了让Spring IOC容器初始化时自动扫描到;
    • @RequestMapping是为了映射请求路径,这里因为类与方法上都有映射所以访问时应该是/HelloController/hello;
    • 方法中声明Model类型的参数是为了把Action中的数据带到视图中;
    • 方法返回的结果是视图的名称hello,加上配置文件中的前后缀变成WEB-INF/jsp/hello.jsp。
  6. 创建视图层

    在WEB-INF/ jsp目录中创建hello.jsp , 视图可以直接取出并展示从Controller带回的信息;

    可以通过EL表示取出Model中存放的值,或者对象;

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
        <title>Hello</title>
    </head>
    <body>
    ${msg}
    </body>
    </html>
    
  7. 测试运行

    image03

小结

使用springMVC必须配置的三大件:

处理器映射器、处理器适配器、视图解析器

通常,我们只需要手动配置视图解析器,而处理器映射器处理器适配器只需要开启注解驱动即可,而省去了大段的xml配置

image04

@Controller与@RequestMappping

  • 控制器Controller

    1. 控制器复杂提供访问应用程序的行为1,通常通过接口定义或注解定义两种方式实现。
    2. 控制器负责解析用户的请求并将其转换为一个模型。
    3. 在Spring MVC中一个控制器类可以包含多个方法
    4. 在Spring MVC中,对于Controller的配置方式有很多种

    实现Controller接口来配置Controller

    Controller接口

    package org.springframework.web.servlet.mvc;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    import org.springframework.lang.Nullable;
    import org.springframework.web.servlet.ModelAndView;
    
    @FunctionalInterface
    public interface Controller {
        @Nullable
        ModelAndView handleRequest(HttpServletRequest var1, HttpServletResponse var2) throws Exception;
    }
    

    观察上面代码我们可以知道,当我们使用Controller接口来配置Controller时,我们只需要实现一个返回ModelAndView的方法即可。

    package com.heng.controller;
    
    import org.springframework.web.servlet.ModelAndView;
    import org.springframework.web.servlet.mvc.Controller;
    
    import javax.servlet.http.HttpServletRequest;
    import javax.servlet.http.HttpServletResponse;
    
    /**
     * @Author: minster
     * @Date: 2021/11/10 9:38
     */
    public class HelloController implements Controller {
    
        @Override
        public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
            //ModelAndView模型和视图
            ModelAndView mv = new ModelAndView();
            //封装对象,放在ModelAndView中。Model
            mv.addObject("msg","HelloSpringMVC!");
            //封装要跳转的视图,放在ModelAndView中
            mv.setViewName("hello");
            return mv;
        }
    }
    

    使用该方法还需要去Spring MVC的配置文件springmvc-servlet.xml中注册一个Bean

    <!--Handler-->
    <bean id="/hello" class="com.heng.controller.HelloController"></bean>
    

    使用@Controller注解来配置Controller

    @Controller注解类型用于声明Spring类的实例是一个控制器(在讲IOC时还提到了另外3个注解);

    Spring可以使用扫描机制来找到应用程序中所有基于注解的控制器类,为了保证Spring能找到你的控制器,需要在配置文件中声明组件扫描

    <!-- 自动扫描指定的包,下面所有注解类交给IOC容器管理 -->
    <context:component-scan base-package="com.kuang.controller"/>
    

    使用@Controller注解

    package com.heng.controller;
    
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    /**
     * @Author: minster
     * @Date: 2021/11/10 15:16
     */
    @Controller
    //映射访问路径
    @RequestMapping("/HelloController")
    public class HelloController {
        //映射访问路径,真实访问路径为localhost:8080/HelloController/hello
        @RequestMapping("/hello")
        public String hello(Model model){
            //Spring MVC会自动实例化一个Model对象用于向视图中传值
            model.addAttribute("msg","helloSpringMVCAnnotation");
            return "hello";
        }
    }
    

    测试结果

    image03

  • @RequestMapping

    从注解名称上我们可以看到,@RequestMapping注解的作用就是将请求和处理请求的控制器方法关联起来,建立映射关系。

    SpringMVC 接收到指定的请求,就会来找到在映射关系中对应的控制器方法来处理这个请求。

    @RequestMapping可以在标注一个方法或者一个类

    1. @RequestMapping标识一个类:设置映射请求的请求路径的初始信息
    2. @RequestMapping标识一个方法:设置映射请求请求路径的具体信息
    @Controller
    @RequestMapping("/HelloController")
    public class HelloController {
        //此时请求映射所映射的请求的请求路径为:/HelloController/hello
        @RequestMapping("/hello")
        public String hello(Model model){
            model.addAttribute("msg","helloSpringMVCAnnotation");
            return "hello";
        }
    }
    

RestFul风格

REST:Representational State Transfer,表现层资源状态转移

概念

RestFul就是一个资源定位及资源操作的风格。不是标准也不是协议,只是一种风格。基于这个风格设计的软件可以更简洁,更有层次,更安全,更易于实现缓存等机制。

  • 资源

    资源是一种看待服务器的方式,即,将服务器看作是由很多离散的资源组成。每个资源是服务器上一个可命名的抽象概念。因为资源是一个抽象的概念,所以它不仅仅能代表服务器文件系统中的一个文件、数据库中的一张表等等具体的东西,可以将资源设计的要多抽象有多抽象,只要想象力允许而且客户端应用开发者能够理解。与面向对象设计类似,资源是以名词为核心来组织的,首先关注的是名词。一个资源可以由一个或多个URI来标识。URI既是资源的名称,也是资源在Web上的地址。对某个资源感兴趣的客户端应用,可以通过资源的URI与其进行交互。

    互联网所有的事物都可以被抽象为资源

  • 状态转移

    状态转移说的是:在客户端和服务器端之间转移(transfer)代表资源状态的表述。通过转移和操作资源的表述,来间接实现操作资源的目的。

RestFul的实现

具体说,就是 HTTP 协议里面,四个表示操作方式的动词:GET、POST、PUT、DELETE。

它们分别对应四种基本操作:GET 用来获取资源,POST 用来新建资源,PUT 用来更新资源,DELETE 用来删除资源

REST 风格提倡 URL 地址使用统一的风格设计,从前到后各个单词使用斜杠分开,不使用问号键值对方式携带请求参数,而是将要发送给服务器的数据作为 URL 地址的一部分,以保证整体风格的一致性。

操作 传统方式 REST风格
查询操作 getUserById?id=1 user/1–>get请求方式
保存操作 saveUser user–>post请求方式
删除操作 deleteUser?id=1 user/1–>delete请求方式
更新操作 updateUser user–>put请求方式

测试案例

  1. 创建Controller

    package com.heng.controller;
    
    
    import org.springframework.stereotype.Controller;
    import org.springframework.ui.Model;
    import org.springframework.web.bind.annotation.PathVariable;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    /**
     * @Author: minster
     * @Date: 2021/11/11 9:15
     */
    @Controller
    public class RestFulController {
        @RequestMapping("/restFul/{p1}/{p2}")
        public String restFul(@PathVariable int p1,@PathVariable int p2, Model model){
            int res = p1+p2;
            model.addAttribute("msg","结果为 "+res);
            return "test";
        }
    }
    
  2. 测试

    image05

  3. 注意:

    通过路径变量的类型可以约束访问参数,如果类型不一样,则访问不到对应的请求方法,如这里访问是的路径是/restFul/1/a,则路径与方法不匹配,而不会是参数转换失败。

使用method属性可以指定请求类型

@RequestMapping中的method属性可以用于约束请求的类型,可以收窄请求范围。

把上面的restFul方法改为

@Controller
public class RestFulController {
    @RequestMapping(value = "/restFul/{p1}/{p2}",method = RequestMethod.POST)
    public String restFul(@PathVariable int p1,@PathVariable int p2, Model model){
        int res = p1+p2;
        model.addAttribute("msg","结果为 "+res);
        return "test";
    }
}

我们再去浏览器请求

image06

可以发现网页报了405的错误;因为我们浏览器地址栏进行访问默认是Get请求!!!!

如果把POST改为GET就可以正常访问了!

@Controller
public class RestFulController {
    @RequestMapping(value = "/restFul/{p1}/{p2}",method = RequestMethod.GET)
    public String restFul(@PathVariable int p1,@PathVariable int p2, Model model){
        int res = p1+p2;
        model.addAttribute("msg","结果为 "+res);
        return "test";
    }
}

小结:

Spring MVC 的 @RequestMapping 注解能够处理 HTTP 请求的方法, 比如 GET, PUT, POST, DELETE 以及 PATCH。

所有的地址栏请求默认都会是 HTTP GET 类型的。

方法级别的注解变体有如下几个:组合注解

@GetMapping
@PostMapping
@PutMapping
@DeleteMapping
@PatchMapping

@GetMapping 是一个组合注解,平时使用的会比较多!

它所扮演的是 @RequestMapping(method =RequestMethod.GET) 的一个快捷方式。

数据处理及跳转

数据处理

处理提交的数据

  1. 提交的域名城和处理方法的参数名一致

    提交数据:http://localhost:8080/commit?name=minster

    处理方法:

    @Controller
    public class TestController1 {
        @RequestMapping("/commit")
        public String commitName(String name, Model model){
            model.addAttribute("msg","Name = "+name);
            System.out.println(name);
            return "test";
        }
    }
    

    后台输出minster

    前端显示页面

    image09

  2. 提交的域名称和处理方法的参数名不一致

    提交数据:http://localhost:8080/commit?username=minster

    处理方法:

    @Controller
    public class TestController1 {
        @RequestMapping("/commit")
        public String commitName(@RequestParam("username") String name, Model model){
            model.addAttribute("msg","Name = "+name);
            System.out.println(name);
            return "test";
        }
    }
    

    后台输出minster

    前端显示:

    image10

    如果此时我们提交的数据为 name=minster,服务器会报错

    image11

    因为我们使用@RequestParam会约束请求提交的参数名!

  3. 提交一个对象

    要求提交的表单域和对象的属性名一致 , 参数使用对象即可

    创建实体类User

    package com.heng.pojo;
    
    /**
     * @Author: minster
     * @Date: 2021/11/11 12:31
     */
    public class User {
        private int id;
        private String name;
        private int age;
    }
    

    提交数据:http://localhost:8080/user?id=1&name=minster&age=21

    处理方法:

    @RequestMapping("/user")
    public String commitUser(User user){
        System.out.println(user);
        return "test";
    }
    

    后台成功输出User{id=1, name=‘minster’, age=21}

    说明:如果使用对象的话,前端传递的参数名和对象名必须一致,否则就是null。

数据显示到前端

  1. ModelAndView

    public class ControllerTest1 implements Controller {
    
       public ModelAndView handleRequest(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse) throws Exception {
           //返回一个模型视图对象
           ModelAndView mv = new ModelAndView();
           mv.addObject("msg","ControllerTest1");
           mv.setViewName("test");
           return mv;
      }
    }
    
  2. ModelMap

    @RequestMapping("/commit")
    public String commitName(@RequestParam("username") String name, ModelMap map){
        map.addAttribute("msg","Name = "+name);
        System.out.println(name);
        return "test";
    }
    
  3. Model

    @RequestMapping("/ct2/hello")
    public String hello(@RequestParam("username") String name, Model model){
       //封装要显示到视图中的数据
       //相当于req.setAttribute("name",name);
       model.addAttribute("msg",name);
       System.out.println(name);
       return "test";
    }
    

对比:

Model 只有寥寥几个方法只适合用于储存数据,简化了新手对于Model对象的操作和理解;

ModelMap 继承了 LinkedMap ,除了实现了自身的一些方法,同样的继承 LinkedMap 的方法和特性;

ModelAndView 可以在储存数据的同时,可以进行设置返回的逻辑视图,进行控制展示层的跳转。

页面跳转

  • **转发:**由服务器端进行的页面跳转;

    image07

  • **重定向(Redirect):**由浏览器端进行的页面跳转;

    重定向是指当浏览器请求一个URL时,服务器返回一个重定向指令,告诉浏览器地址已经变了,麻烦使用新的URL再重新发送新请求。

    image08

ModelAndView

设置ModelAndView对象,根据View的名称和视图解析器可以跳转到指定的页面

页面 = {视图解析器前缀} + ViewName + {视图解析器后缀}

<!--视图解析器-->
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver" id="internalResourceViewResolver">
    <!-- 前缀 -->
    <property name="prefix" value="/WEB-INF/jsp/"></property>
    <!-- 后缀 -->
    <property name="suffix" value=".jsp"></property>
</bean>

对应的Controller类

@Controller
@RequestMapping("/HelloController")
public class HelloController {
    @RequestMapping("/hello")
    public String hello(Model model){
        model.addAttribute("msg","helloSpringMVCAnnotation");
        return "hello";
    }
}

访问的url为 localhost:8080/HelloController/hello

ServletAPI

通过设置ServletAPI , 不需要视图解析器 .

1、通过HttpServletResponse进行输出

2、通过HttpServletResponse实现重定向

3、通过HttpServletResponse实现转发

@Controller
public class ResultGo {

   @RequestMapping("/result/t1")
   public void test1(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
       rsp.getWriter().println("Hello,Spring BY servlet API");
  }

   @RequestMapping("/result/t2")
   public void test2(HttpServletRequest req, HttpServletResponse rsp) throws IOException {
       //重定向
       rsp.sendRedirect("/index.jsp");
  }

   @RequestMapping("/result/t3")
   public void test3(HttpServletRequest req, HttpServletResponse rsp) throws Exception {
       //转发
       req.setAttribute("msg","/result/t3");
       req.getRequestDispatcher("/WEB-INF/jsp/test.jsp").forward(req,rsp);
  }

}

SpringMVC

通过SpringMVC来实现转发和重定向 - 无需视图解析器;

测试前,需要将视图解析器注释掉

@Controller
public class ResultSpringMVC {
   @RequestMapping("/rsm/t1")
   public String test1(){
       //转发
       return "/index.jsp";
  }

   @RequestMapping("/rsm/t2")
   public String test2(){
       //转发二
       return "forward:/index.jsp";
  }

   @RequestMapping("/rsm/t3")
   public String test3(){
       //重定向
       return "redirect:/index.jsp";
  }
}

通过SpringMVC来实现转发和重定向 - 有视图解析器;

重定向 , 不需要视图解析器 , 本质就是重新请求一个新地方嘛 , 所以注意路径问题.

可以重定向到另外一个请求实现 .

@Controller
public class ResultSpringMVC2 {
   @RequestMapping("/rsm2/t1")
   public String test1(){
       //转发
       return "test";
  }

   @RequestMapping("/rsm2/t2")
   public String test2(){
       //重定向
       return "redirect:/index.jsp";
       //return "redirect:hello.do"; //hello.do为另一个请求/
  }

}

乱码问题

案例演示

  1. 我们在首页编写一个提交的表单

    <%@ page contentType="text/html;charset=UTF-8" language="java" %>
    <html>
    <head>
      <title>乱码问题</title>
    </head>
    <body>
    <form action="/commitForm" method="post">
      <input type="text" name="name">
      <input type="submit">
    </form>
    </body>
    </html>
    
  2. 后台编写对应的处理方法

    @RequestMapping("/commitForm")
    public String commitForm(Model model , String name){
        model.addAttribute("msg",name);
        return "test";
    }
    
  3. 测试

    image13

    输出结果,发现乱码

    image12

    以前乱码问题通过过滤器解决 , 而SpringMVC给我们提供了一个过滤器 , 可以在web.xml中配置 .

    修改了xml文件需要重启服务器!

    <filter>
       <filter-name>encoding</filter-name>
       <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
       <init-param>
           <param-name>encoding</param-name>
           <param-value>utf-8</param-value>
       </init-param>
    </filter>
    <filter-mapping>
       <filter-name>encoding</filter-name>
       <url-pattern>/*</url-pattern>
    </filter-mapping>
    

    运行

    image14

    问题解决!

如果我们使用上述过滤器还不能解决乱码问题的话,我们可以自定义一个强大的过滤器!(网上一个大佬写的过滤器,基本所有乱码问题都能解决!)

package com.heng.filter;

import javax.servlet.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletRequestWrapper;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.util.Map;

/**
 * 解决get和post请求 全部乱码的过滤器
 */
@SuppressWarnings({"all"})
public class GenericEncodingFilter implements Filter {

    @Override
    public void destroy() {
    }

    @Override
    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) throws IOException, ServletException {
        //处理response的字符编码
        HttpServletResponse myResponse=(HttpServletResponse) response;
        myResponse.setContentType("text/html;charset=UTF-8");

        // 转型为与协议相关对象
        HttpServletRequest httpServletRequest = (HttpServletRequest) request;
        // 对request包装增强
        HttpServletRequest myrequest = new MyRequest(httpServletRequest);
        chain.doFilter(myrequest, response);
    }

    @Override
    public void init(FilterConfig filterConfig) throws ServletException {
    }

}
@SuppressWarnings({"all"})
//自定义request对象,HttpServletRequest的包装类
class MyRequest extends HttpServletRequestWrapper {

    private HttpServletRequest request;
    //是否编码的标记
    private boolean hasEncode;
    //定义一个可以传入HttpServletRequest对象的构造函数,以便对其进行装饰
    public MyRequest(HttpServletRequest request) {
        super(request);// super必须写
        this.request = request;
    }

    // 对需要增强方法 进行覆盖
    @Override
    public Map getParameterMap() {
        // 先获得请求方式
        String method = request.getMethod();
        if (method.equalsIgnoreCase("post")) {
            // post请求
            try {
                // 处理post乱码
                request.setCharacterEncoding("utf-8");
                return request.getParameterMap();
            } catch (UnsupportedEncodingException e) {
                e.printStackTrace();
            }
        } else if (method.equalsIgnoreCase("get")) {
            // get请求
            Map<String, String[]> parameterMap = request.getParameterMap();
            if (!hasEncode) { // 确保get手动编码逻辑只运行一次
                for (String parameterName : parameterMap.keySet()) {
                    String[] values = parameterMap.get(parameterName);
                    if (values != null) {
                        for (int i = 0; i < values.length; i++) {
                            try {
                                // 处理get乱码
                                values[i] = new String(values[i]
                                        .getBytes("ISO-8859-1"), "utf-8");
                            } catch (UnsupportedEncodingException e) {
                                e.printStackTrace();
                            }
                        }
                    }
                }
                hasEncode = true;
            }
            return parameterMap;
        }
        return super.getParameterMap();
    }

    //取一个值
    @Override
    public String getParameter(String name) {
        Map<String, String[]> parameterMap = getParameterMap();
        String[] values = parameterMap.get(name);
        if (values == null) {
            return null;
        }
        return values[0]; // 取回参数的第一个值
    }

    //取所有值
    @Override
    public String[] getParameterValues(String name) {
        Map<String, String[]> parameterMap = getParameterMap();
        String[] values = parameterMap.get(name);
        return values;
    }
}

然后再web.xml中配置一下即可!

Licensed under CC BY-NC-SA 4.0
Built with Hugo
Theme Stack designed by Jimmy