Spring MVC的基本使用1

1、Spring MVC的基本介绍

spring mvc 是基于 spring 的一个框架,实际上就是 spring 的一个模块,是专门用来做 web 开发的。spring mvc 的底层实际上还是 servlet ,只是在 servlet 的基础上面加入了一些功能,让 web 开发更加方便,可以理解为是 servlet 的升级。

Spring MVC 框架是围绕一个 DispatcherServlet(中央调度器) 来设计的,这个Servlet会把请求分发给各个处理器,由各个处理器来处理请求(处理器就是应用中注解了 @Controller 和 @RequestMapping 的类和方法)。DispatcherServlet其实就是个Servlet(它继承自HttpServlet基类),DispatcherServlet 也被称之为前端控制器。

DispatcherServlet处理请求的工作流如下:

 

2、SpringMVC的基本使用

2.1、springmvc的使用

先在 idea 中通过 maven 创建一个 web 项目,创建完成后我们可以手动添加 src/main/java 和 src/main/resource 目录:

然后引入依赖,依赖配置文件 pom.xml 类似下面:

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.example</groupId>
  <artifactId>maven_ee_test01</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <dependencies>
    <dependency>
      <groupId>junit</groupId>
      <artifactId>junit</artifactId>
      <version>4.11</version>
      <scope>test</scope>
    </dependency>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>
  </dependencies>
</project>

 

在 maven 的 web 项目的 web.xml 配置文件中配置中央调度器。默认生成的 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">

  <!-- SpringMVC的前端控制器 -->
  <servlet>
    <servlet-name>springmvcTest</servlet-name>
    <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>


    <!-- 可以自定义springmvc读取的配置文件的位置 -->
<!--    <init-param>-->
<!--      <param-name>contextConfigLocation</param-name>-->
<!--      <param-value>/WEB-INF/xxx.xml</param-value>  &lt;!&ndash;指定配置文件的位置&ndash;&gt;-->
<!--    </init-param>-->


    <!-- 指定该servlet对象在tomcat启动时即创建。
         load-on-startup:指定tomcat启动后创建对象的顺序,tomcat会根据各个servlet的该属性值按照顺序来创建各个servlet对象。它的值是大于等于0的整数,值越小创建时间越早。-->
    <load-on-startup>1</load-on-startup>
  </servlet>

  <servlet-mapping>
    <servlet-name>springmvcTest</servlet-name>

    <!-- 使用框架时,url-pattern一般来说有两种写法:
            1)使用自定义扩展名:*.do、*.action、*.mvc等等,如<url-pattern>*.do</url-pattern>
            2)直接使用斜杆:<url-pattern>/</url-pattern>,表示拦截所有请求
    -->
    <url-pattern>*.do</url-pattern>
  </servlet-mapping>
</web-app>

DispatcherServlet实际上就是个Servlet(它继承自HttpServlet基类),在被创建时,会执行该 servlet 的 init() 方法。在 DispatcherServlet 的初始化过程中,该框架会尝试拿到项目中的 WebContent/WEB-INF 目录文件下的名为 [servlet-name]-servlet.xml 的配置文件,并创建其中所定义的bean。(比如是上面的配置,则该配置文件将会是 webcontent/WEB-INF/springmvcTest-servlet.xml,springmvc 会从该配置文件中加载应用程序上下文)。当然,我们也可以通过 init-param 标签自定义配置文件的位置及名称,不使用默认的。

中央调度器  DispatcherServlet 负责创建 springmvc 容器 对象,读取 spring 的 xml 配置文件,创建文件中的 Controller 对象。并且负责接收用户的请求,分派给各个 Controller 对象。

 

然后我们需要在 webcontent(webapp)/WEB-INF 目录下创建一个 spring 配置文件,并在该文件中开启组件扫描。比如下面的 springmvcTest-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"
       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/context https://www.springframework.org/schema/context/spring-context.xsd">

    <!--开启组件扫描。base-package写包名,若要扫描多个包,可以用逗号隔开,或者直接写多个包共用的上级目录-->
    <context:component-scan base-package="controllerPackage"></context:component-scan>
</beans>

 

然后需要创建控制器,通过控制器来处理请求。比如下面我们创建了一个控制器类,通过注解 @RequestMapping 来将URL映射到处理方法中,即 /test.do 请求将会由 doTest() 方法来处理:

package controllerPackage;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class ControllerTest01 {
    /**
     *创建方法来处理请求,在springmvc中是通过方法来处理请求的。方法是自定义的,有多种返回值,多种参数
     * @RequestMapping:请求映射,作用是把请求地址和方法绑定在一起。属性的value可以是一个string,也可以是一个数组,不同方法间的值不能重复。
     * @RequestMapping 可以放在方法上,也可以直接放在类上
     */
    @RequestMapping(value = "/test.do")
    public ModelAndView doTest() {
        //Spring MVC 通过 ModelAndView 对象把模型和视图结合在一起
        ModelAndView modelView = new ModelAndView();

        //添加数据,框架最后会把数据放到request的作用域当中,类似于 request.setAttribute()
        modelView.addObject("name","张三");
        modelView.addObject("age","22");

        //指定视图,指定视图的完整路径。框架会对视图执行forward操作,类似于request.getRequestDispatcher("/show.jsp").forward();
        modelView.setViewName("/show.jsp");
        return modelView;
    }
}

 在项目 src/main/webapp 目录下创建 show.jsp,用来接收上面处理 /test.do 过后添加的数据:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
</head>
<body>
    <h1>这里是show.jsp页面</h1>

    <h2>hello:${name} ---- ${age}</h2>

</body>
</html>

 

然后我们就可以通过访问 test.do 来进行测试了。启动 tomcat,添加项目,访问 test.do 请求。比如项目名为 springmvcProject,则访问路径为:http://localhost:8080/springmvcProject/test.do。最后可以看到结果如下:

 

2.2、配置视图解析器(统一指定视图的前缀和后缀)

上面的示例,我们把 show.jsp 建在了 webapp 目录下,这样的话用户可以直接通过访问 jsp 页面的路径来访问页面,而不是通过接口 .do 的形式来访问,而不通过接口就无法拿到数据,可能会出现下面这种情况:

如果我们不希望用户可以直接访问到 jsp 页面,我们可以把页面文件建在 WEB-INF 目录下,这样用户就无法通过输入页面路径来直接访问页面,因为 WEB-INF 下的资源是无法通过浏览器直接访问的。比如:

这样我们指定视图就可以写成:

modelView.setViewName("/WEB-INF/view/show.jsp");

 

在指定视图的时候,有可能有大量的重复路径,例如:

mv.setViewName("/WEB-INF/view/show1.jsp");
mv.setViewName("/WEB-INF/view/show2.jsp");
mv.setViewName("/WEB-INF/view/show3.jsp");

我们可以在 spring 的配置文件中配置视图解析器,可以指定视图的前缀(路径)和后缀(扩展名),让框架来找到对应的视图文件。例如,修改 springmvcTest-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"
       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/context https://www.springframework.org/schema/context/spring-context.xsd">

    <!--开启组件扫描。base-package写包名,若要扫描多个包,可以用逗号隔开,或者直接写多个包共用的上级目录-->
    <context:component-scan base-package="controllerPackage"></context:component-scan>

    <!-- springmvc框架中的视图解析器-->
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 前缀:视图文件的路径-->
        <property name="prefix" value="/WEB-INF/view/"/>
        <!-- 后缀:视图文件的扩展名-->
        <property name="suffix" value=".jsp"/>
    </bean>
</beans>

在配置了视图解析器后就可以直接用逻辑名称(文件名)来指定视图,框架会使用 “视图解析器前缀+逻辑名称+视图解析器后缀” 来组成完整的路径。

示例:

mv.setViewName("show1");   //相当于 mv.setViewName("/WEB-INF/view/show1.jsp");
mv.setViewName("show2");
mv.setViewName("show3");

 

2.3、解决访问静态资源报404的问题

中央调度器的 url-pattern 可以使用自定义扩展名,如:*.do、*.action、*.mvc等等,如<url-pattern>*.do</url-pattern>;也可以直接使用斜杆:<url-pattern>/</url-pattern>,表示拦截所有请求。当我们直接使用斜杆来拦截所有请求时,你会发现,在使用浏览器访问静态资源(比如html、css、img等)时会报 404。(在某些时候如果我们不希望通过 .do 来访问后端接口,此时可以将 url-pattern 配置成 /,这样就不需要给接口名后面添加 .do 后缀,直接写即可,比如 @RequestMapping(value = "/test01"))

默认情况下,tomcat 会有一个 default 的 servlet(该 servlet 可以在 tomcat 安装目录下的 conf/web.xml 文件中找到),该 servlet 的 url-pattern 是 /,即拦截所有请求。当我们将中央调度器的 url-pattern 设置为 / 时,该中央调度器会替代 tomcat 的默认的 servlet,而因为中央调度器默认没有处理静态资源的能力,所以访问静态资源会报 404,但是访问动态资源比如 xxx.do 能正常访问。

 

所以当我们将中央调度器的 url-pattern 配置成 / 时,需要解决静态资源访问不到的问题。解决方法有以下两种:

1)使用<mvc:default-servlet-handler />

在 spring 的配置文件(比如springmvcTest-servlet.xml)中配置<mvc:default-servlet-handler />,如下:

<?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:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!--开启组件扫描。base-package写包名,若要扫描多个包,可以用逗号隔开,或者直接写多个包共用的上级目录-->
    <context:component-scan base-package="controllerPackage"></context:component-scan>

    <!--注解驱动-->
    <mvc:annotation-driven/>

    <mvc:default-servlet-handler />
</beans>

配置<mvc:default-servlet-handler />后,Spring MVC上下文中会定义一个org.springframework.web.servlet.resource.DefaultServletHttpRequestHandler,它会像一个检查员,对进入DispatcherServlet的URL进行筛查,如果发现是静态资源的请求,就将该请求转由Web应用服务器默认的 Servlet 处理,如果不是静态资源的请求,才由DispatcherServlet继续处理。

在配置了 <mvc:default-servlet-handler /> 后,访问 @RequestMapping 注解的方法可能报 404,这是因为 <mvc:default-servlet-handler /> 和 @RequestMapping 注解有冲突,此时我们在 spring  的配置文件上添加注解驱动即可。

 

2)使用<mvc:resources />

第一种方法即 <mvc:default-servlet-handler /> 是将静态资源的处理 由Spring MVC 框架交回给了 tomcat 处理。而使用 <mvc:resources /> 则是由Spring MVC框架自己处理静态资源,并可以添加一些有用的附加值功能。

在 spring 的配置文件下添加 <mvc:resources /> 标签,示例:

<?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:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!--开启组件扫描。base-package写包名,若要扫描多个包,可以用逗号隔开,或者直接写多个包共用的上级目录-->
    <context:component-scan base-package="controllerPackage"></context:component-scan>


    <!--<mvc:resources /> 标签 和 @RequestMapping 注解有冲突,所以需要添加注解驱动-->
    <mvc:annotation-driven/>

    <!--
        mapping指的是浏览器访问资源的路径,比如http:localhost:8080/应用名/static/home.html
        locatioon指的是在服务器内,静态资源存放的位置,比如下面的即表示静态资源是在 webapp/static 目录下
    -->
    <mvc:resources mapping="/static/**" location="/static/" />

    <!-- 可以定义多个mvc:resource标签,为不同目录下的静态资源定义不同的mapping和location,但是建议将静态资源都放在同一个文件夹下,比如 webapp/static 下,这样就可以只保留上面一个标签-->
    <mvc:resources mapping="/css/**" location="/css/" />

</beans>

使用 <mvc:resources /> 允许静态资源放在任何地方,如 WEB-INF目录下、类路径下等。通过 location属性指定静态资源的位置,由于location属性是Resources类型,因此可以使用诸如"classpath:"等的资源前缀指定资源位置。传统Web容器的静态资源只能放在Web容器的根路径下,<mvc:resources />完全打破了这个限制。

其次,<mvc:resources />依据当前著名的Page Speed、YSlow等浏览器优化原则对静态资源提供优化。你可以通过cacheSeconds属性指定静态资源在浏览器端的缓存时间,一般可将该时间设置为一年,以充分利用浏览器端的缓存。在输出静态资源时,会根据配置设置好响应报文头的Expires 和 Cache-Control值。在接收到静态资源的获取请求时,会检查请求头的Last-Modified值,如果静态资源没有发生变化,则直接返回303相应状态码,提示客户端使用浏览器缓存的数据,而非将静态资源的内容输出到客户端,以充分节省带宽,提高程序性能。

 

3、@RequestMapping注解

@RequestMapping 将请求地址和方法绑定在一起。属性的value可以是一个string,也可以是一个数组,不同方法间的值不能重复。

 

3.1、@RequestMapping作用在类上

@RequestMapping 既可以作用在方法上,也可以作用在类上。当@RequestMapping 标记在Controller 类上的时候,类里面使用@RequestMapping 标记的方法的请求地址都是相对于类上的@RequestMapping 而言的。简单来说,就是给类上的方法统一加了一个前缀。

示例:

package controllerPackage;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping("/user")
public class ControllerTest02 {
    @RequestMapping(value = "/test1.do")
    public ModelAndView doTest() {
        ...
    }

    @RequestMapping(value = "/test2.do")
    public ModelAndView doTest2() {
        ...
    }
}

此时访问 test1.do 或者 test2.do 实际上应该访问的路径为:localhost:8080/项目名/user/test1.do。注意,需要在前面加上 /user。

 

3.2、method 属性

@RequestMapping 的 method 属性可以指定方法只能被指定的请求方式访问,如果不指定请求方式时,则表示不限制请求方式,即任何方式都行。该属性的值是 RequestMethod 类枚举值,比如:RequestMethod.GET、RequestMethod.POST 等等。

示例:

package controllerPackage;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping("/user")
public class ControllerTest02 {
    @RequestMapping(value = "/test1.do", method = RequestMethod.GET)
    public ModelAndView doTest() {
        ...
    }

    @RequestMapping(value = "/test2.do", method = {RequestMethod.GET, RequestMethod.DELETE})
    public ModelAndView doTest2() {
         ...
    }
}

指定了请求方式后,如果使用非指定的请求方式来访问接口的话,浏览器会报 405,提示该请求方式不被支持。

 

4、处理器方法接收前端请求的参数

4.1、通过HttpServletRequest对象获取参数

可以给 @RequestMapping 注解的方法添加 HttpServletRequest 对象的属性,通过该对象可以获取到前端请求的参数:

@Controller
public class ControllerTest03 {
    @RequestMapping(value = "/test01.do")
    public ModelAndView doTest(HttpServletRequest request, HttpServletResponse response, HttpSession httpSessiont) {
        System.out.println(request.getParameter("name"));  // 获取到前端发送的name属性值
        
        //...
    }
}

我们可以通过访问类似 http://localhost:8080/springmvcProject/test01.do?name=zhangsan 的地址给后台接口发送数据,后台接口即可接收到 name 属性的值。

 

4.2、通过同名形参直接获取参数

可以给 @RequestMapping 注解的方法添加跟前端发送的参数同名的形参,即可通过形参来直接获取前端发送的同名参数:

@Controller
public class ControllerTest03 {
    @RequestMapping(value = "/test01.do")
    public ModelAndView doTest(String name, int age) {
        System.out.println(name + "----" + age);  //直接获取到前端发送的name和age参数值

        //...
    }
}

我们可以通过访问类似 http://localhost:8080/springmvcProject/test01.do?name=zhangsan&age=22 的地址给后台接口发送数据,后台接口即可接收到对应的参数值。

这种通过同名形参直接获取前端同名参数的方法,实际上是 springmvc 框架通过 HttpServletRequest 对象的 getParameter() 方法来将前端参数赋值给对应的形参的。并且框架同时还提供类型转换的功能,例如上面,会先将 age 的值转换为 int 类型,然后再赋值给形参 age。

 

4.3、形参和实参不同名(@RequestParam())

通过与参数同名的形参能够直接获取前端参数,但如果不同名,我们也可以通过 @RequestParam() 注解来指定参数的别名来获取参数。

@Controller
public class ControllerTest02 {
    @RequestMapping(value = "/test3.do")
    public ModelAndView doTest2(@RequestParam(name = "othername") String name) {
        System.out.println(name);

        //...
    }
}

如上,前端请求的参数可以是 othername。通过 @RequestParam(name = "othername") 和 @RequestParam(value = "othername")  或者不加属性值即 @RequestParam("othername") 设置都一样。

@RequestParam() 还可以设置属性值 required 为 false,即 @RequestParam(required = false),此时前端可以不用传该参数 java 程序也不会报错。

 

4.4、通过对象来获取参数

如果参数过多,一个个写参数可能比较麻烦,此时我们可以定义一个 Java 实体类,该类的属性名跟前端参数名对应,通过类的对象作为形参来获取参数。

示例:

定义一个实体类:

public class Person {
    String name;
    int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

将该类的对象作为形参,springmvc 框架会自动创建形参中类的对象,并且调用该对象的 setter 方法给属性赋值:

@Controller
public class ControllerTest02 {
    @RequestMapping(value = "/test4.do")
    public ModelAndView doTest4(Person person) {
        System.out.println(person.getName());

        //...
    }
}

 

4.5、配置字符编码过滤器(解决中文乱码问题)

在获取前端中文参数时,可能会出现乱码问题。如果使用 HttpServletResponse 对象来返回数据,可以使用 resp.setContentType("text/html;charset=utf-8"); 来解决中文乱码问题,但需要在每个请求里都设置,比较麻烦。

我们可以在 WEB-INF/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">

  ...

  <!-- characterEncodingFilter字符编码过滤器 -->
  <filter>
    <filter-name>characterEncodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param> <!--要使用的字符集,一般使用UTF-8--> <param-name>encoding</param-name> <param-value>UTF-8</param-value> </init-param>
<init-param> <!--是否强制设置request的编码为encoding,默认为false--> <param-name>forceRequestEncoding</param-name> <param-value>true</param-value> </init-param>
<init-param> <!--是否强制设置response的编码为encoding--> <param-name>forceResponseEncoding</param-name> <param-value>true</param-value> </init-param> </filter>
<filter-mapping> <filter-name>characterEncodingFilter</filter-name> <!--这里不能留空或者直接写'/',否则不起作用--> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>

上面设置编码过后,返回给前端的数据编码将会是 utf-8,解决中文乱码问题。

但是设置了上面字符编码过滤器后,如果直接访问服务器中的 html 页面,你会发现 html 页面里的中文变乱码了。此时将 forceRequestEncoding 和 forceResponseEncoding 去掉即可。

 

5、响应请求返回数据

5.1、返回ModelAndView类型(响应一个jsp页面)

若处理器方法处理完,需要跳转其它 jsp 资源,并且需要传递数据,则可以返回 ModelAndView 。此时JSP页面可以使用 JSTL 标签来匹配解析后端返回的数据。

示例:

package controllerPackage;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;
@Controller
public class ControllerTest01 {
    @RequestMapping(value = "/test.do")
    public ModelAndView doTest() {
        ModelAndView modelView = new ModelAndView();
        modelView.addObject("name","张三");
        modelView.addObject("age","22");
        modelView.setViewName("/show.jsp");
        return modelView;
    }
}

 

5.2、返回String(响应一个jsp页面)

处理器方法可以返回字符串,该字符串可以是逻辑名称(文件名称),也可以是完整的视图路径。最终该处理方法会跳转到该字符串所指定的资源。

示例:

package controllerPackage;

import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.servlet.ModelAndView;

@Controller
public class ControllerTest02 {

    @RequestMapping(value = "/returnStringTest.do")
    public String doTest() {
        //框架实际上是对视图执行forward操作
        return "/WEB-INF/view/show1.jsp";   //或者如果我们配置了视图解析器,则应该直接写逻辑名称,比如:show1
    }
}

 

5.3、返回值为void(响应ajax请求)

在返回值为 void 时,我们可以用来响应 ajax 请求。

package controllerPackage;

import com.alibaba.fastjson.JSON;
...

@Controller
public class ControllerTest02 {

    //直接给 ajax 请求返回字符串
    @RequestMapping("/ajaxResponse.do")
    public void ajaxResponse(HttpServletRequest request, HttpServletResponse response) throws Exception{
        PrintWriter out = response.getWriter();
        out.write("hello");
    }

    //给 ajax 请求返回一个json格式字符串
    @RequestMapping("/ajaxJson.do")
    public void ajaxJson(HttpServletRequest request, HttpServletResponse response) throws Exception{
        PrintWriter out = response.getWriter();

        //通过 com.alibaba.fastjson.JSON 包来将对象转换成json格式字符串
        Student student = new Student(1, "wen");
        String stuStr = JSON.toJSONString(student);

        out.write(stuStr);
    }
}

 

5.4、返回Object类型(包括Integer、String、Map、List、自定义对象等等,响应ajax请求)

处理器也可以返回 Object 对象,这个对象可以是 Integer、String、Map、List、自定义对象等等。但返回的对象不是作为逻辑视图出现的,而是直接给前端请求返回数据的。

首先需要先引入处理 json 的工具库,springmvc 中默认使用的是 jackson:

<?xml version="1.0" encoding="UTF-8"?>

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>

  <groupId>org.example</groupId>
  <artifactId>springmvc_maven_test01</artifactId>
  <version>1.0-SNAPSHOT</version>
  <packaging>war</packaging>

  <dependencies>
    <dependency>
      <groupId>javax.servlet</groupId>
      <artifactId>javax.servlet-api</artifactId>
      <version>3.1.0</version>
      <scope>provided</scope>
    </dependency>
    <dependency>
      <groupId>org.springframework</groupId>
      <artifactId>spring-webmvc</artifactId>
      <version>5.2.5.RELEASE</version>
    </dependency>

    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-core</artifactId>
      <version>2.9.4</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-databind</artifactId>
      <version>2.9.4</version>
    </dependency>
    <dependency>
      <groupId>com.fasterxml.jackson.core</groupId>
      <artifactId>jackson-annotations</artifactId>
      <version>2.9.4</version>
    </dependency>

  </dependencies>

    ...
</project>

然后给 spring 的配置文件添加注解驱动。

<?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:context="http://www.springframework.org/schema/context"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <!--开启组件扫描。base-package写包名,若要扫描多个包,可以用逗号隔开,或者直接写多个包共用的上级目录-->
    <context:component-scan base-package="controllerPackage"></context:component-scan>

    <!--注解驱动-->
    <mvc:annotation-driven/>
</beans>

添加注解驱动时需要注意是添加 mvc 的,不要添加成其他同名的。

 

注解驱动的作用是将 java 对象转换为 json、xml、text、二进制等格式数据,框架会自动识别 java 对象能转换为什么数据格式,然后进行返回。<mvc:annotation-driven/> 在加入到配置文件后,会自动创建 HttpMessageConverter 接口的实现类对象,这个接口的实现类完成了 java 对象到其他数据格式的转换功能。

 

在使用时需要在方法前添加 @ResponseBody 注解,springmvc 通过该注解将会把处理器方法已经转换后生成的数据通过 HttpServletResponse 返回给前端的 ajax 请求。

代码示例:

package controllerPackage;
import entity.Student;
...

@Controller
public class ControllerTest03 {

    @RequestMapping("/objJson.do")
    @ResponseBody   //把处理器方法返回的对象转换为json后,通过HttpServletResponse返回给前端
    public Student objJson(HttpServletRequest request, HttpServletResponse response){

        Student student = new Student(1, "wen");
        return student;
    }
}

返回一个类对象,前端接收到的字符串转换为 json 格式后将会是一个对象 obj。

如果返回的是一个 Map,则前端接收到的字符串经转换为 json 后也是一个对象 obj;如果返回的是一个 list,则前端接收到的字符串经转换为 json 后将会是一个数组,并且顺序跟 list 添加元素的顺序一致;如果是一个字符串 String,前端接收到的也是字符串。

代码示例:

package controllerPackage;

import entity.Student;
...

@Controller
public class ControllerTest03 {

    //返回一个list
    @RequestMapping("/objList.do")
    @ResponseBody   //把处理器方法返回的对象转换为json后,通过HttpServletResponse返回给前端
    public List<Student> objList(HttpServletRequest request, HttpServletResponse response){
        List<Student> list = new ArrayList<>();
        Student student1 = new Student(1, "wen");
        Student student2 = new Student(2, "wen2");

        list.add(student1);
        list.add(student2);

        return list;
    }

    //返回一个map
    @RequestMapping("/objMap.do")
    @ResponseBody   //把处理器方法返回的对象转换为json后,通过HttpServletResponse返回给前端
    public Map<String, String> objMap(HttpServletRequest request, HttpServletResponse response){
        Map<String, String> testMap = new HashMap<>();
        testMap.put("hello", "你好");
        testMap.put("world", "世界");
        return testMap;
    }

    //直接返回字符串
    @RequestMapping("/objString.do")
    @ResponseBody   //把处理器方法返回的对象转换为json后,通过HttpServletResponse返回给前端
    public String objString(HttpServletRequest request, HttpServletResponse response){
        return "你好啊hello~";
    }

}

 

5.4.1、解决返回 String 时中文乱码问题

使用上述方法来返回 String 时,如果字符串中有中文,则前端接收到的数据可能会有中文乱码问题(注意,此时返回字符串是直接返回数据,而不是返回视图 jsp 资源)。出现中文乱码问题这是默认返回的响应头 content-type 中指定的编码格式是 ISO-8859-1,如下:

 

 解决中文乱码问题我们可以通过 @RequestMapping 注解的 produces 属性来指定响应头的 content-type,如下:

@Controller
public class ControllerTest03 {
    @RequestMapping(value = "/objString.do", produces = "text/html;charset=utf-8")  //produces相当于给response指定了Content-Type响应头
    @ResponseBody
    public String objString(HttpServletRequest request, HttpServletResponse response){
        return "你好啊hello~";
    }
}

 

6、Springmvc接收各种参数(字符串、json)

6.1、接收普通参数

前端发送请求方法:

$.ajax({
    url: 'getStr.do',
    type: 'post',  //此时get请求和post请求发送参数的写法一样
    data: {
        name: 'wen',
        age: 11
    },
    success: function (data) {
        console.log('后端返回数据', data);  //这里将接收到后端返回的字符串
    }
})

springmvc 代码,请求参数名跟Controller的方法参数一致:

@Controller
public class ControllerTest01 {

    @RequestMapping(value = "/getStr.do", produces = "text/html;charset=utf-8")
    @ResponseBody
    public String doTest(String name, int age) {
        System.out.println(name + "----" + age);  //直接获取到前端发送的name和age参数值

        return "你好啊hello~";
    }

}

此时可以看到前端发送的 url、响应头 content-type、发送头 content-type 和参数如下:

 

6.2、将对象作为参数

当请求参数过多时,后端可以以对象作为参数接收前端发送的数据,而此时前端发送数据跟发送普通参数一样。

前端代码如下,此时发送数据跟发送普通参数一致:

$.ajax({
    url: 'getObj.do',
    type: 'post',
    data: {
        name: 'wen',
        age: 11
    },
    success: function (data) {
        console.log('后端返回数据', data)
    }
})

实体类如下,实体类可以写构造函数也可以不写。

package domain;

public class Student {
    String name;
    int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }

    public Student(String name, int age) {
        System.out.println(11112222);
        this.name = name;
        this.age = age;
    }
}

controller 类:

@Controller
public class ControllerTest01 {

    @RequestMapping(value = "/getObj.do")  //此时不能写produces = "text/html;charset=utf-8" 因为此时返回的是对象,默认将是json格式,如果写成text/html将导致接口有问题
    @ResponseBody
    public Student doTest02(Student student) {
        System.out.println(student.getName() + "----" + student.getAge());

        return student;
    }
}

此时可以看到前端发送的 url、响应头 content-type、发送头 content-type 和参数如下:

 

 

6.3、接收复杂对象

如果接收的是复杂对象,比如对象当中还嵌套着对象。此时前端发送请求时需指定 content-type 为 'application/json;charset=utf-8',并且后端需要用 @RequestBody 进行接收。

前端发送参数格式如下:

$.ajax({
    url: 'getComplexObj.do',
    contentType: 'application/json;charset=utf-8',
    type: 'post',
    data: JSON.stringify({
        complexName: '复杂对象名',    //这里也可以是一个对象,这样的话此时后端的实体类中该字段也应该是一个实体类
        student: {
            name: '学生名',
            age: 111
        }
    }),
    success: function (data) {
        console.log('后端返回数据', data)
    }
})

复杂对象类:

package domain;

public class ComplexDomain {
    private Student student;  //注意,此时student类中不能有自定义构造函数
    private String complexName;

    public Student getStudent() {
        return student;
    }

    public void setStudent(Student student) {
        this.student = student;
    }

    public String getComplexName() {
        return complexName;
    }

    public void setComplexName(String complexName) {
        this.complexName = complexName;
    }
}

controller类:

@Controller
public class ControllerTest01 {

    @RequestMapping(value = "/getComplexObj.do")
    @ResponseBody
    public ComplexDomain doTest03(@RequestBody ComplexDomain complexDomain) {
        System.out.println(complexDomain.getComplexName() + "----" + complexDomain.getStudent().getName() + "----" + complexDomain.getStudent().getAge()); 

        return complexDomain;
    }

}

此时可以看到前端发送的 url、响应头 content-type、发送头 content-type 和参数如下:

 

 

 

posted @ 2021-05-11 18:21  wenxuehai  阅读(400)  评论(0编辑  收藏  举报
//右下角添加目录