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 类似下面:

复制代码
  1. <?xml version="1.0" encoding="UTF-8"?>
  2.  
  3. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <modelVersion>4.0.0</modelVersion>
  6.  
  7. <groupId>org.example</groupId>
  8. <artifactId>maven_ee_test01</artifactId>
  9. <version>1.0-SNAPSHOT</version>
  10. <packaging>war</packaging>
  11.  
  12. <dependencies>
  13. <dependency>
  14. <groupId>junit</groupId>
  15. <artifactId>junit</artifactId>
  16. <version>4.11</version>
  17. <scope>test</scope>
  18. </dependency>
  19. <dependency>
  20. <groupId>javax.servlet</groupId>
  21. <artifactId>javax.servlet-api</artifactId>
  22. <version>3.1.0</version>
  23. <scope>provided</scope>
  24. </dependency>
  25. <dependency>
  26. <groupId>org.springframework</groupId>
  27. <artifactId>spring-webmvc</artifactId>
  28. <version>5.2.5.RELEASE</version>
  29. </dependency>
  30. </dependencies>
  31. </project>
复制代码

 

在 maven 的 web 项目的 web.xml 配置文件中配置中央调度器。默认生成的 web.xml 配置文件跟下面不太一样,可以直接照着下面进行修改:

复制代码
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
  5. version="4.0">
  6.  
  7. <!-- SpringMVC的前端控制器 -->
  8. <servlet>
  9. <servlet-name>springmvcTest</servlet-name>
  10. <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
  11.  
  12.  
  13. <!-- 可以自定义springmvc读取的配置文件的位置 -->
  14. <!-- <init-param>-->
  15. <!-- <param-name>contextConfigLocation</param-name>-->
  16. <!-- <param-value>/WEB-INF/xxx.xml</param-value> &lt;!&ndash;指定配置文件的位置&ndash;&gt;-->
  17. <!-- </init-param>-->
  18.  
  19.  
  20. <!-- 指定该servlet对象在tomcat启动时即创建。
  21. load-on-startup:指定tomcat启动后创建对象的顺序,tomcat会根据各个servlet的该属性值按照顺序来创建各个servlet对象。它的值是大于等于0的整数,值越小创建时间越早。-->
  22. <load-on-startup>1</load-on-startup>
  23. </servlet>
  24.  
  25. <servlet-mapping>
  26. <servlet-name>springmvcTest</servlet-name>
  27.  
  28. <!-- 使用框架时,url-pattern一般来说有两种写法:
  29. 1)使用自定义扩展名:*.do、*.action、*.mvc等等,如<url-pattern>*.do</url-pattern>
  30. 2)直接使用斜杆:<url-pattern>/</url-pattern>,表示拦截所有请求
  31. -->
  32. <url-pattern>*.do</url-pattern>
  33. </servlet-mapping>
  34. </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 标签自定义配置文件的位置及名称,不使用默认的。

然后我们需要在 webcontent(webapp)/WEB-INF 目录下创建一个 spring 配置文件,并在该文件中开启组件扫描。比如下面的 springmvcTest-servlet.xml:

复制代码
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. 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">
  6.  
  7. <!--开启组件扫描。base-package写包名,若要扫描多个包,可以用逗号隔开,或者直接写多个包共用的上级目录-->
  8. <context:component-scan base-package="controllerPackage"></context:component-scan>
  9. </beans>
复制代码

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

 

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

复制代码
  1. package controllerPackage;
  2. import org.springframework.stereotype.Controller;
  3. import org.springframework.web.bind.annotation.RequestMapping;
  4. import org.springframework.web.servlet.ModelAndView;
  5. @Controller
  6. public class ControllerTest01 {
  7. /**
  8. *创建方法来处理请求,在springmvc中是通过方法来处理请求的。方法是自定义的,有多种返回值,多种参数
  9. * @RequestMapping:请求映射,作用是把请求地址和方法绑定在一起。属性的value可以是一个string,也可以是一个数组,不同方法间的值不能重复。
  10. * @RequestMapping 可以放在方法上,也可以直接放在类上
  11. */
  12. @RequestMapping(value = "/test.do")
  13. public ModelAndView doTest() {
  14. //Spring MVC 通过 ModelAndView 对象把模型和视图结合在一起
  15. ModelAndView modelView = new ModelAndView();
  16. //添加数据,框架最后会把数据放到request的作用域当中,类似于 request.setAttribute()
  17. modelView.addObject("name","张三");
  18. modelView.addObject("age","22");
  19. //指定视图,指定视图的完整路径。框架会对视图执行forward操作,类似于request.getRequestDispatcher("/show.jsp").forward();
  20. modelView.setViewName("/show.jsp");
  21. return modelView;
  22. }
  23. }
复制代码

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

复制代码
  1. <%@ page contentType="text/html;charset=UTF-8" language="java" %>
  2. <html>
  3. <head>
  4. <title>Title</title>
  5. </head>
  6. <body>
  7. <h1>这里是show.jsp页面</h1>
  8. <h2>hello:${name} ---- ${age}</h2>
  9. </body>
  10. </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 下的资源是无法通过浏览器直接访问的。比如:

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

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

 

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

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

我们可以在 spring 的配置文件中配置视图解析器,可以指定视图的前缀(路径)和后缀(扩展名),让框架来找到对应的视图文件。例如,修改 springmvcTest-servlet.xml 文件:

复制代码
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. 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">
  6.  
  7. <!--开启组件扫描。base-package写包名,若要扫描多个包,可以用逗号隔开,或者直接写多个包共用的上级目录-->
  8. <context:component-scan base-package="controllerPackage"></context:component-scan>
  9.  
  10. <!-- springmvc框架中的视图解析器-->
  11. <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
  12. <!-- 前缀:视图文件的路径-->
  13. <property name="prefix" value="/WEB-INF/view/"/>
  14. <!-- 后缀:视图文件的扩展名-->
  15. <property name="suffix" value=".jsp"/>
  16. </bean>
  17. </beans>
复制代码

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

示例:

  1. mv.setViewName("show1"); //相当于 mv.setViewName("/WEB-INF/view/show1.jsp");
  2. mv.setViewName("show2");
  3. 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 />,如下:

复制代码
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:mvc="http://www.springframework.org/schema/mvc"
  6. 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">
  7.  
  8. <!--开启组件扫描。base-package写包名,若要扫描多个包,可以用逗号隔开,或者直接写多个包共用的上级目录-->
  9. <context:component-scan base-package="controllerPackage"></context:component-scan>
  10.  
  11. <!--注解驱动-->
  12. <mvc:annotation-driven/>
  13.  
  14. <mvc:default-servlet-handler />
  15. </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 /> 标签,示例:

复制代码
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:mvc="http://www.springframework.org/schema/mvc"
  6. 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">
  7.  
  8. <!--开启组件扫描。base-package写包名,若要扫描多个包,可以用逗号隔开,或者直接写多个包共用的上级目录-->
  9. <context:component-scan base-package="controllerPackage"></context:component-scan>
  10.  
  11.  
  12. <!--<mvc:resources /> 标签 和 @RequestMapping 注解有冲突,所以需要添加注解驱动-->
  13. <mvc:annotation-driven/>
  14.  
  15. <!--
  16. mapping指的是浏览器访问资源的路径,比如http:localhost:8080/应用名/static/home.html
  17. locatioon指的是在服务器内,静态资源存放的位置,比如下面的即表示静态资源是在 webapp/static 目录下
  18. -->
  19. <mvc:resources mapping="/static/**" location="/static/" />
  20.  
  21. <!-- 可以定义多个mvc:resource标签,为不同目录下的静态资源定义不同的mapping和location,但是建议将静态资源都放在同一个文件夹下,比如 webapp/static 下,这样就可以只保留上面一个标签-->
  22. <mvc:resources mapping="/css/**" location="/css/" />
  23.  
  24. </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 而言的。简单来说,就是给类上的方法统一加了一个前缀。

示例:

复制代码
  1. package controllerPackage;
  2. import org.springframework.stereotype.Controller;
  3. import org.springframework.web.bind.annotation.RequestMapping;
  4. import org.springframework.web.servlet.ModelAndView;
  5. @Controller
  6. @RequestMapping("/user")
  7. public class ControllerTest02 {
  8. @RequestMapping(value = "/test1.do")
  9. public ModelAndView doTest() {
  10. ...
  11. }
  12. @RequestMapping(value = "/test2.do")
  13. public ModelAndView doTest2() {
  14. ...
  15. }
  16. }
复制代码

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

 

3.2、method 属性

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

示例:

复制代码
  1. package controllerPackage;
  2. import org.springframework.stereotype.Controller;
  3. import org.springframework.web.bind.annotation.RequestMapping;
  4. import org.springframework.web.bind.annotation.RequestMethod;
  5. import org.springframework.web.servlet.ModelAndView;
  6. @Controller
  7. @RequestMapping("/user")
  8. public class ControllerTest02 {
  9. @RequestMapping(value = "/test1.do", method = RequestMethod.GET)
  10. public ModelAndView doTest() {
  11. ...
  12. }
  13. @RequestMapping(value = "/test2.do", method = {RequestMethod.GET, RequestMethod.DELETE})
  14. public ModelAndView doTest2() {
  15. ...
  16. }
  17. }
复制代码

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

 

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

4.1、通过HttpServletRequest对象获取参数

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

复制代码
  1. @Controller
  2. public class ControllerTest03 {
  3. @RequestMapping(value = "/test01.do")
  4. public ModelAndView doTest(HttpServletRequest request, HttpServletResponse response, HttpSession httpSessiont) {
  5. System.out.println(request.getParameter("name")); // 获取到前端发送的name属性值
  6. //...
  7. }
  8. }
复制代码

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

 

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

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

复制代码
  1. @Controller
  2. public class ControllerTest03 {
  3. @RequestMapping(value = "/test01.do")
  4. public ModelAndView doTest(String name, int age) {
  5. System.out.println(name + "----" + age); //直接获取到前端发送的name和age参数值
  6. //...
  7. }
  8. }
复制代码

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

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

 

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

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

复制代码
  1. @Controller
  2. public class ControllerTest02 {
  3. @RequestMapping(value = "/test3.do")
  4. public ModelAndView doTest2(@RequestParam(name = "othername") String name) {
  5. System.out.println(name);
  6. //...
  7. }
  8. }
复制代码

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

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

 

4.4、通过对象来获取参数

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

示例:

定义一个实体类:

复制代码
  1. public class Person {
  2. String name;
  3. int age;
  4. public String getName() {
  5. return name;
  6. }
  7. public void setName(String name) {
  8. this.name = name;
  9. }
  10. public int getAge() {
  11. return age;
  12. }
  13. public void setAge(int age) {
  14. this.age = age;
  15. }
  16. }
复制代码

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

复制代码
  1. @Controller
  2. public class ControllerTest02 {
  3. @RequestMapping(value = "/test4.do")
  4. public ModelAndView doTest4(Person person) {
  5. System.out.println(person.getName());
  6. //...
  7. }
  8. }
复制代码

 

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

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

我们可以在 WEB-INF/web.xml 的配置文件里设置字符编码过滤器来统一解决编码问题。

示例:

复制代码
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
  5. version="4.0">
  6. ...
  7. <!-- characterEncodingFilter字符编码过滤器 -->
  8. <filter>
  9. <filter-name>characterEncodingFilter</filter-name>
    <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
  10. <init-param>
  11. <!--要使用的字符集,一般使用UTF-8-->
  12. <param-name>encoding</param-name>
  13. <param-value>UTF-8</param-value>
  14. </init-param>
  15. <init-param>
  16. <!--是否强制设置request的编码为encoding,默认为false-->
  17. <param-name>forceRequestEncoding</param-name>
  18. <param-value>true</param-value>
  19. </init-param>
  20. <init-param>
  21. <!--是否强制设置response的编码为encoding-->
  22. <param-name>forceResponseEncoding</param-name>
  23. <param-value>true</param-value>
  24. </init-param>
  25. </filter>
  26. <filter-mapping>
  27. <filter-name>characterEncodingFilter</filter-name>
  28. <!--这里不能留空或者直接写'/',否则不起作用-->
  29. <url-pattern>/*</url-pattern>
  30. </filter-mapping>
  31.  
  32. </web-app>
复制代码

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

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

 

5、响应请求返回数据

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

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

示例:

复制代码
  1. package controllerPackage;
  2. import org.springframework.stereotype.Controller;
  3. import org.springframework.web.bind.annotation.RequestMapping;
  4. import org.springframework.web.servlet.ModelAndView;
  5. @Controller
  6. public class ControllerTest01 {
  7. @RequestMapping(value = "/test.do")
  8. public ModelAndView doTest() {
  9. ModelAndView modelView = new ModelAndView();
  10. modelView.addObject("name","张三");
  11. modelView.addObject("age","22");
  12. modelView.setViewName("/show.jsp");
  13. return modelView;
  14. }
  15. }
复制代码

 

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

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

示例:

复制代码
  1. package controllerPackage;
  2. import org.springframework.stereotype.Controller;
  3. import org.springframework.web.bind.annotation.RequestMapping;
  4. import org.springframework.web.servlet.ModelAndView;
  5. @Controller
  6. public class ControllerTest02 {
  7. @RequestMapping(value = "/returnStringTest.do")
  8. public String doTest() {
  9. //框架实际上是对视图执行forward操作
  10. return "/WEB-INF/view/show1.jsp"; //或者如果我们配置了视图解析器,则应该直接写逻辑名称,比如:show1
  11. }
  12. }
复制代码

 

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

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

复制代码
  1. package controllerPackage;
  2. import com.alibaba.fastjson.JSON;
  3. ...
  4. @Controller
  5. public class ControllerTest02 {
  6. //直接给 ajax 请求返回字符串
  7. @RequestMapping("/ajaxResponse.do")
  8. public void ajaxResponse(HttpServletRequest request, HttpServletResponse response) throws Exception{
  9. PrintWriter out = response.getWriter();
  10. out.write("hello");
  11. }
  12. //给 ajax 请求返回一个json格式字符串
  13. @RequestMapping("/ajaxJson.do")
  14. public void ajaxJson(HttpServletRequest request, HttpServletResponse response) throws Exception{
  15. PrintWriter out = response.getWriter();
  16. //通过 com.alibaba.fastjson.JSON 包来将对象转换成json格式字符串
  17. Student student = new Student(1, "wen");
  18. String stuStr = JSON.toJSONString(student);
  19. out.write(stuStr);
  20. }
  21. }
复制代码

 

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

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

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

复制代码
  1. <?xml version="1.0" encoding="UTF-8"?>
  2.  
  3. <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  5. <modelVersion>4.0.0</modelVersion>
  6.  
  7. <groupId>org.example</groupId>
  8. <artifactId>springmvc_maven_test01</artifactId>
  9. <version>1.0-SNAPSHOT</version>
  10. <packaging>war</packaging>
  11.  
  12. <dependencies>
  13. <dependency>
  14. <groupId>javax.servlet</groupId>
  15. <artifactId>javax.servlet-api</artifactId>
  16. <version>3.1.0</version>
  17. <scope>provided</scope>
  18. </dependency>
  19. <dependency>
  20. <groupId>org.springframework</groupId>
  21. <artifactId>spring-webmvc</artifactId>
  22. <version>5.2.5.RELEASE</version>
  23. </dependency>
  24.  
  25. <dependency>
  26. <groupId>com.fasterxml.jackson.core</groupId>
  27. <artifactId>jackson-core</artifactId>
  28. <version>2.9.4</version>
  29. </dependency>
  30. <dependency>
  31. <groupId>com.fasterxml.jackson.core</groupId>
  32. <artifactId>jackson-databind</artifactId>
  33. <version>2.9.4</version>
  34. </dependency>
  35. <dependency>
  36. <groupId>com.fasterxml.jackson.core</groupId>
  37. <artifactId>jackson-annotations</artifactId>
  38. <version>2.9.4</version>
  39. </dependency>
  40.  
  41. </dependencies>
  42. ...
  43. </project>
复制代码

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

复制代码
  1. <?xml version="1.0" encoding="UTF-8"?>
  2. <beans xmlns="http://www.springframework.org/schema/beans"
  3. xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  4. xmlns:context="http://www.springframework.org/schema/context"
  5. xmlns:mvc="http://www.springframework.org/schema/mvc"
  6. 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">
  7.  
  8. <!--开启组件扫描。base-package写包名,若要扫描多个包,可以用逗号隔开,或者直接写多个包共用的上级目录-->
  9. <context:component-scan base-package="controllerPackage"></context:component-scan>
  10.  
  11. <!--注解驱动-->
  12. <mvc:annotation-driven/>
  13. </beans>
复制代码

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

 

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

 

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

代码示例:

复制代码
  1. package controllerPackage;
  2. import entity.Student;
  3. ...
  4. @Controller
  5. public class ControllerTest03 {
  6. @RequestMapping("/objJson.do")
  7. @ResponseBody //把处理器方法返回的对象转换为json后,通过HttpServletResponse返回给前端
  8. public Student objJson(HttpServletRequest request, HttpServletResponse response){
  9. Student student = new Student(1, "wen");
  10. return student;
  11. }
  12. }
复制代码

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

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

代码示例:

复制代码
  1. package controllerPackage;
  2. import entity.Student;
  3. ...
  4. @Controller
  5. public class ControllerTest03 {
  6. //返回一个list
  7. @RequestMapping("/objList.do")
  8. @ResponseBody //把处理器方法返回的对象转换为json后,通过HttpServletResponse返回给前端
  9. public List<Student> objList(HttpServletRequest request, HttpServletResponse response){
  10. List<Student> list = new ArrayList<>();
  11. Student student1 = new Student(1, "wen");
  12. Student student2 = new Student(2, "wen2");
  13. list.add(student1);
  14. list.add(student2);
  15. return list;
  16. }
  17. //返回一个map
  18. @RequestMapping("/objMap.do")
  19. @ResponseBody //把处理器方法返回的对象转换为json后,通过HttpServletResponse返回给前端
  20. public Map<String, String> objMap(HttpServletRequest request, HttpServletResponse response){
  21. Map<String, String> testMap = new HashMap<>();
  22. testMap.put("hello", "你好");
  23. testMap.put("world", "世界");
  24. return testMap;
  25. }
  26. //直接返回字符串
  27. @RequestMapping("/objString.do")
  28. @ResponseBody //把处理器方法返回的对象转换为json后,通过HttpServletResponse返回给前端
  29. public String objString(HttpServletRequest request, HttpServletResponse response){
  30. return "你好啊hello~";
  31. }
  32. }
复制代码

 

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

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

 

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

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

 

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

6.1、接收普通参数

前端发送请求方法:

复制代码
  1. $.ajax({
  2. url: 'getStr.do',
  3. type: 'post', //此时get请求和post请求发送参数的写法一样
  4. data: {
  5. name: 'wen',
  6. age: 11
  7. },
  8. success: function (data) {
  9. console.log('后端返回数据', data); //这里将接收到后端返回的字符串
  10. }
  11. })
复制代码

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

复制代码
  1. @Controller
  2. public class ControllerTest01 {
  3. @RequestMapping(value = "/getStr.do", produces = "text/html;charset=utf-8")
  4. @ResponseBody
  5. public String doTest(String name, int age) {
  6. System.out.println(name + "----" + age); //直接获取到前端发送的name和age参数值
  7.  
  8. return "你好啊hello~";
  9. }
  10. }
复制代码

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

 

6.2、将对象作为参数

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

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

复制代码
  1. $.ajax({
  2. url: 'getObj.do',
  3. type: 'post',
  4. data: {
  5. name: 'wen',
  6. age: 11
  7. },
  8. success: function (data) {
  9. console.log('后端返回数据', data)
  10. }
  11. })
复制代码

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

复制代码
  1. package domain;
  2. public class Student {
  3. String name;
  4. int age;
  5. public String getName() {
  6. return name;
  7. }
  8. public void setName(String name) {
  9. this.name = name;
  10. }
  11. public int getAge() {
  12. return age;
  13. }
  14. public void setAge(int age) {
  15. this.age = age;
  16. }
  17. public Student(String name, int age) {
  18. System.out.println(11112222);
  19. this.name = name;
  20. this.age = age;
  21. }
  22. }
复制代码

controller 类:

复制代码
  1. @Controller
  2. public class ControllerTest01 {
  3. @RequestMapping(value = "/getObj.do") //此时不能写produces = "text/html;charset=utf-8" 因为此时返回的是对象,默认将是json格式,如果写成text/html将导致接口有问题
  4. @ResponseBody
  5. public Student doTest02(Student student) {
  6. System.out.println(student.getName() + "----" + student.getAge());
  7. return student;
  8. }
  9. }
复制代码

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

 

 

6.3、接收复杂对象

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

前端发送参数格式如下:

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

复杂对象类:

复制代码
  1. package domain;
  2. public class ComplexDomain {
  3. private Student student; //注意,此时student类中不能有自定义构造函数
  4. private String complexName;
  5. public Student getStudent() {
  6. return student;
  7. }
  8. public void setStudent(Student student) {
  9. this.student = student;
  10. }
  11. public String getComplexName() {
  12. return complexName;
  13. }
  14. public void setComplexName(String complexName) {
  15. this.complexName = complexName;
  16. }
  17. }
复制代码

controller类:

复制代码
  1. @Controller
  2. public class ControllerTest01 {
  3. @RequestMapping(value = "/getComplexObj.do")
  4. @ResponseBody
  5. public ComplexDomain doTest03(@RequestBody ComplexDomain complexDomain) {
  6. System.out.println(complexDomain.getComplexName() + "----" + complexDomain.getStudent().getName() + "----" + complexDomain.getStudent().getAge());
  7. return complexDomain;
  8. }
  9. }
复制代码

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

 

 

 

posted @   wenxuehai  阅读(458)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 25岁的心里话
· 闲置电脑爆改个人服务器(超详细) #公网映射 #Vmware虚拟网络编辑器
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· 零经验选手,Compose 一天开发一款小游戏!
· 通过 API 将Deepseek响应流式内容输出到前端
//右下角添加目录
点击右上角即可分享
微信分享提示