• 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、接收普通参数

    普通方式,请求参数名跟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~";
        }
    
    }

    此时前端发送请求方法:

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

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

    6.2、将对象作为参数

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

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

    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;
        }
    
    }

    此时前端发送数据跟发送普通参数一致:

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

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

     

    6.3、接收复杂对象

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

    复杂对象类:

    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;
        }
    
    }

    此时前端发送参数格式如下:

    $.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)
        }
    })

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

     

     

  • 相关阅读:
    epii.js简约而不简单的JS模板引擎
    Acwing 165. 小猫爬山
    《将博客搬家到csdn》
    Tourism【codeforces 1200E】
    Middle-Out【codeforces 1231E】(字符串匹配问题)
    super_log (广义欧拉降幂)(2019南京网络赛)
    Different Circle Permutation (HDU
    Knapsack Cryptosystem(状压dp)
    Quadratic equation(二次剩余定理)
    分级(线性dp)
  • 原文地址:https://www.cnblogs.com/wenxuehai/p/14756744.html
Copyright © 2020-2023  润新知