SpringMVC

1 什么是SpringMVC?

1.1 三层架构

开发架构一般基于两种形式,C/S架构和B/S架构。而在B/S架构中,系统则分为标准的三层架构:表现层、业务层和持久层。

  • 表现层:即web层。负责接收客户端请求,向客户端响应结果,通常客户端使用http协议请求web层,web需要接收http请求,完成http响应。表现层包括展示层和控制层:控制层负责接收请求,展示层负责结果的展示。表现层依赖业务层,接收到客户端请求一般会调用业务层进行业务处理,并将处理结果响应给客户端。 表现层的设计一般都使用MVC模型。(MVC是表现层的设计模型,和其他层没有关系)
  • 业务层:即service层。负责业务逻辑处理,web层依赖业务层,但是业务层不依赖web层。业务层在业务处理时可能会依赖持久层。
  • 持久层:dao层。负责数据持久化,包括数据层即数据库和数据访问层,数据库是对数据进行持久化的载体,数据访问层是业务层和持久层交互的接口,业务层需要通过数据访问层将数据持久化到数据库中。

1.2 MVC模型

正如1.1中所说的:MVC是表现层的设计模型,和其他层没有关系。MVC是一种设计模式,应用于表现层,全名是Model View Controller,是模型(model)-视图(view)-控制器(controller)的缩写,是一种用于设计创建 Web 应用程序表现层的模式。

  • Model(模型):用于封装数据。
  • View(视图):用于展示数据(jsp或者html)。
  • Controller(控制器):用于处理程序逻辑。

1.3 SpringMVC

Spring MVC属于SpringFrameWork的后续产品,已经融合在Spring Web Flow里面。Spring 框架提供了构建 Web 应用程序的全功能 MVC 模块。

一张经典的三层架构的图如下:
在这里插入图片描述

2 SpringMVC的入门

IDEA中创建基于MavenWeb项目的细节如下:
在这里插入图片描述

2.1 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>HelloSpringMVC</artifactId>
    <version>1.0-SNAPSHOT</version>
    <packaging>war</packaging>

    <name>HelloSpringMVC Maven Webapp</name>
    <!-- FIXME change it to the project's website -->
    <url>http://www.example.com</url>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <maven.compiler.source>1.7</maven.compiler.source>
        <maven.compiler.target>1.7</maven.compiler.target>
        <spring.version>5.0.2.RELEASE</spring.version>
    </properties>

    <dependencies>
        <!--Spring核心依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-context</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <!--SpringMVC核心依赖-->
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-web</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-webmvc</artifactId>
            <version>${spring.version}</version>
        </dependency>

        <!--Servlet和JSP依赖-->
        <dependency>
            <groupId>javax.servlet</groupId>
            <artifactId>servlet-api</artifactId>
            <version>2.5</version>
        </dependency>
        <dependency>
            <groupId>javax.servlet.jsp</groupId>
            <artifactId>jsp-api</artifactId>
            <version>2.0</version>
        </dependency>

        <!--JUnit依赖-->
        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>
    </dependencies>

    <build>
        <finalName>HelloSpringMVC</finalName>
        <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) -->
            <plugins>
                <plugin>
                    <artifactId>maven-clean-plugin</artifactId>
                    <version>3.1.0</version>
                </plugin>
                <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_war_packaging -->
                <plugin>
                    <artifactId>maven-resources-plugin</artifactId>
                    <version>3.0.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <version>3.8.0</version>
                </plugin>
                <plugin>
                    <artifactId>maven-surefire-plugin</artifactId>
                    <version>2.22.1</version>
                </plugin>
                <plugin>
                    <artifactId>maven-war-plugin</artifactId>
                    <version>3.2.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-install-plugin</artifactId>
                    <version>2.5.2</version>
                </plugin>
                <plugin>
                    <artifactId>maven-deploy-plugin</artifactId>
                    <version>2.8.2</version>
                </plugin>
            </plugins>
        </pluginManagement>
    </build>
</project>

2.2 web.xml

<!DOCTYPE web-app PUBLIC
        "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
        "http://java.sun.com/dtd/web-app_2_3.dtd" >

<web-app>
    <display-name>Archetype Created Web Application</display-name>

    <!-- 配置SpringMVC的核心控制器 -->
    <servlet>
        <servlet-name>dispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- 配置初始化参数,用于读取SpringMVC的配置文件 -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:SpringMVC.xml</param-value>
        </init-param>
        <!-- 配置servlet的对象的创建时间点:应用加载时创建。 取值只能是非0正整数,表示启动顺序 -->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>dispatcherServlet</servlet-name>
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

2.3 SpringMVC.xml

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       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
       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">

    <!-- 配置创建Spring容器的注解支持 -->
    <context:component-scan base-package="com.hc"/>

    <!-- 配置视图解析器,即配置返回的位置映射 -->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="WEB-INF/pages/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <!-- 配置spring开启注解mvc的支持 -->
    <mvc:annotation-driven></mvc:annotation-driven>

</beans>

2.4 控制器

package com.hc.controller;

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

@Controller
public class HelloWorld {

    /**
     * 映射url
     *
     * @return
     */
    @RequestMapping(value = "hello")
    public String hello() {
        System.out.println("HelloWorld!!!");
        // 返回的页面名称
        return "hello";
    }
}

2.5 JSP页面

  • index.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>index</title>
</head>
<body>
<a href="/hello">登陆</a>
</body>
</html>
  • hello.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Hello</title>
</head>
<body>
<h1>Hello World!</h1>
</body>
</html>

3 SpringMVC的请求响应流程及入门案例细节

3.1 SpringMVC的请求响应流程

2中的SpringMVC的请求相应流程可以分为如下5步:

  • 服务器启动,应用被加载。读取到web.xml中的配置创建Spring容器并且初始化容器中的对象。
  • 浏览器发送请求,被DispatherServlet捕获,该Servlet并不处理请求,而是把请求转发出去。转发的路径是根据请求URL,匹配@RequestMapping中的内容。
  • 匹配到了后,执行对应方法。该方法有一个返回值。
  • 根据方法的返回值,借助InternalResourceViewResolver找到对应的结果视图。
  • 渲染结果视图,响应浏览器。

3.2 SpringMVC组件

完整的SpringMVC的请求响应流程如下:
在这里插入图片描述
涉及以下组件:

  • DispatcherServlet:前端控制器。用户请求到达前端控制器,它就相当于mvc模式中的cDispatcherServlet控制器是整个流程控制的中心,由它调用其它组件处理用户的请求,DispatcherServlet的存在降低了组件之间的耦合性。
  • HandlerMapping:处理器映射器。HandlerMapping负责根据用户请求找到Handler即处理器,SpringMVC提供了不同的映射器实现不同的映射方式,例如:配置文件方式,实现接口方式,注解方式等。
  • Handler:处理器,即Controller。它就是我们开发中要编写的具体业务控制器。由DispatcherServlet把用户请求转发到Handler。由Handler对具体的用户请求进行处理。
  • HandlAdapter:处理器适配器。通过HandlerAdapter对处理器进行执行,这是适配器模式的应用,通过扩展适配器可以对更多类型的处理器进行执行。
  • View Resolver:视图解析器。View Resolver负责将处理结果生成View视图,View Resolver首先根据逻辑视图名解析成物理视图名即具体的页面地址,再生成View视图对象,最后对View进行渲染将处理结果通过页面展示给用户。
  • View:视图。SpringMVC框架提供了很多的View视图类型的支持,包括:jstlViewfreemarkerViewpdfView等。我们最常用的视图就是jsp

3.3 RequestMapping

RequestMapping注解的作用是建立请求URL和处理方法之间的对应关系。

3.3.1 RequestMapping的使用位置

在这里插入图片描述
RequestMapping的源码可以看出,RequestMapping注解可以出现在方法上或类上。

  • 类上:请求URL的第一级访问目录。
  • 方法上:请求URL的第二级访问目录。

3.3.2 RequestMapping的属性

  • value:用于指定请求的URL。它和path属性的作用是一样的。
  • method:用于指定请求的方式。
  • params:用于指定限制请求参数的条件。它支持简单的表达式。要求请求参数的keyvalue必须和配置的一模一样。
  • headers:用于指定限制请求消息头的条件。
  • produces:设置返回数据的类型以及编码,可以是jsonxmlhtml等。例如,RequestMapping(value = "/toList", produces = "text/html;charset=utf-8")。注意,produces必须要和@ResponseBody注解一起使用才可以,不加@ResponseBody注解相当于按照和返回String同名jsp页面解析自然就会报错。当然,如果反过来,不加produces属性,只有@ResponseBody注解的话也是没有问题的,只是在浏览器中直接访问的时候有区别。

3.3.3 测试

在这里插入图片描述
在这里插入图片描述

4 请求参数绑定

4.1 绑定机制说明

  • SpringMVC的参数绑定过程是把表单提交的请求参数作为控制器中方法的参数进行绑定
  • 表单提交的数据都是key=value格式
  • 提交表单的name属性和参数的名称是相同

4.2 基本数据类型和字符串类型

要求参数名称必须和控制器中方法的形参名称保持一致。(严格区分大小写)

  • 控制器代码
package com.hc.controller;

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

import java.lang.reflect.Method;

@Controller
public class HelloWorld {

    /**
     * 映射url
     *
     * @return
     */
    @RequestMapping(value = "hello", params = {"username"}, method = RequestMethod.POST)
    public String hello(String username, String password) {
        System.out.println(username + " " + password);
        // 返回的页面名称
        return "hello";
    }
}

  • JSP页面代码
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" isELIgnored="false" %>
<html>
<head>
    <title>index</title>
</head>
<body>
<form action="hello" method="post">
    <input type="text" name="username">
    <p/>
    <input type="password" name="password">
    <p/>
    <input type="submit" value="DL">
</form>
</body>
</html>

4.3 POJO类型

要求表单中参数名称和POJO类的属性名称保持一致,并且控制器方法的参数类型是POJO类型。

  • 控制器
package com.hc.controller;

import com.hc.domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import java.lang.reflect.Method;

@Controller
public class HelloWorld {

    /**
     * 映射url
     *
     * @return
     */
    @RequestMapping(value = "hello", params = {"username"}, method = RequestMethod.POST)
    public String hello(User user) {
        System.out.println(user.toString());
        // 返回的页面名称
        return "hello";
    }
}


  • JSP代码
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" isELIgnored="false" %>
<html>
<head>
    <title>index</title>
</head>
<body>
<form action="hello" method="post">
    <input type="text" name="username">
    <p/>
    <input type="password" name="password">
    <p/>
    <input type="text" name="account.id">
    <p/>
    <input type="text" name="account.uid">
    <p/>
    <input type="text" name="account.money">
    <p/>
    <input type="submit" value="DL">
</form>
</body>
</html>

4.4 POJO中包含集合类型参数

  • 第一种: 要求集合类型的请求参数必须在POJO中。在表单中请求参数名称要和POJO中集合属性名称相同。 给List集合中的元素赋值,使用下标。 给Map集合中的元素赋值,使用键值对。
  • 第二种: 接收的请求参数是json格式数据。需要借助一个注解实现。
package com.hc.controller;

import com.hc.domain.User;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;

import java.lang.reflect.Method;

@Controller
public class HelloWorld {

    /**
     * 映射url
     *
     * @return
     */
    @RequestMapping(value = "hello", params = {"username"}, method = RequestMethod.POST)
    public String hello(User user) {
        System.out.println(user.toString());
        // 返回的页面名称
        return "hello";
    }
}

<%--
  Created by IntelliJ IDEA.
  User: HCong
  Date: 2022/5/9
  Time: 11:21
  To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" isELIgnored="false" %>
<html>
<head>
    <title>index</title>
</head>
<body>
<form action="hello" method="post">
    <input type="text" name="username">
    <p/>
    <input type="password" name="password">
    <p/>
    <input type="text" name="accounts[0].id">
    <p/>
    <input type="text" name="accounts[0].uid">
    <p/>
    <input type="text" name="accounts[0].money">
    <p/>
    <input type="text" name="accounts[1].id">
    <p/>
    <input type="text" name="accounts[1].uid">
    <p/>
    <input type="text" name="accounts[1].money">
    <p/>
    <input type="submit" value="DL">
</form>
</body>
</html>

4.5 请求参数乱码问题

在这里插入图片描述
当是POST请求时,会出现中文乱码问题。配置过滤器处理中文乱码:

<!-- 配置SpringMVC编码过滤器 -->
    <filter>
        <filter-name>characterEncodingFilter</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>
        <!-- 启动过滤器 -->
        <init-param>
            <param-name>forceEncoding</param-name>
            <param-value>true</param-value>
        </init-param>
    </filter>
    <!-- 过滤所有请求 -->
    <filter-mapping>
        <filter-name>characterEncodingFilter</filter-name>
        <url-pattern>/*</url-pattern>
    </filter-mapping>

当然,静态资源不过滤时可以在SpringMVC.xml中配置:

<!-- 
	在springmvc的配置文件中可以配置,静态资源不过滤:
	location表示路径,mapping表示文件,**表示该目录下的文件以及子目录的文件
--> 
<mvc:resources location="/css/" mapping="/css/**"/> 
<mvc:resources location="/images/" mapping="/images/**"/> 
<mvc:resources location="/scripts/" mapping="/javascript/**"/>

注:静态资源为什么会被拦截见此博文

4.6 自定义类型转换器

如果页面传入2020/1/1这样的日期字符串SpringMVC会直接将其解析为Date类型的对象,而当字符串是2020-1-1这样的,则会解析失败,需要自定义类型转换器,并将其加入到SpringMVC得自动转换中。

  • 自定义类型转换器
package com.hc.utils;

import org.springframework.core.convert.converter.Converter;

import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;

/**
 * 自定义日期转换器
 */
public class StringToDateConverter implements Converter<String, Date> {
    @Override
    public Date convert(String source) {
        try {
            DateFormat df = new SimpleDateFormat("yyyy-MM-dd");
            return df.parse(source);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}

  • SpringMVC.xml配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans" xmlns:mvc="http://www.springframework.org/schema/mvc"
       xmlns:context="http://www.springframework.org/schema/context"
       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
       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">

    <!-- 配置创建Spring容器的注解支持 -->
    <context:component-scan base-package="com.hc"/>

    <!-- 配置视图解析器,即配置返回的位置映射 -->
    <bean id="internalResourceViewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <property name="prefix" value="WEB-INF/pages/"/>
        <property name="suffix" value=".jsp"/>
    </bean>

    <!-- 配置类型转换器工厂 -->
    <bean id="conversionServiceFactoryBean" class="org.springframework.context.support.ConversionServiceFactoryBean">
        <!-- 给工厂注入一个新的类型转换器 -->
        <property name="converters">
            <array>
                <!-- 配置自定义类型转换器 -->
                <bean class="com.hc.utils.StringToDateConverter"></bean>
            </array>
        </property>
    </bean>

    <!--
        配置spring开启注解mvc的支持
        在annotation-driven标签中引用配置的类型转换服务
    -->
    <mvc:annotation-driven conversion-service="conversionServiceFactoryBean"></mvc:annotation-driven>

</beans>

5 常用注解

5.1 RequestParam注解

  • 作用:把请求中指定名称的参数给控制器中的形参赋值。
  • 属性:value表示请求参数中的名称。 required表示请求参数中是否必须提供此参数。默认值:true,表示必须提供,如果不提供将报错。
    在这里插入图片描述
    上图表示,将提交的url中的name映射为username

5.2 RequestBody注解

  • 作用:(1)用于获取请求体内容。直接使用得到是key=value&key=value这样的结构的数据。 get请求方式不适用。(2)放在方法上返回不同格式的数据,例如返回xml格式的页面数据。
  • 属性:equired表示是否必须有请求体。默认值是true。当取值为true时,get请求方式会报错。如果取值为falseget请求得到是null
    在这里插入图片描述

5.3 PathVaribale注解

  • 作用:用于绑定url中的占位符。例如,请求url/delete/{id},这个{id}就是url占位符。 url支持占位符是Spring3.0之后加入的,是SpringMVC支持rest风格URL的一个重要标志。RESTFUL风格见此。
  • 属性:value表示用于指定url中占位符名称。 required表示否必须提供占位符。
    在这里插入图片描述
    在这里插入图片描述

该注解与RESUFUL编程风格息息相关。

5.4 RequestHeader注解

  • 作用:用于获取请求消息头。
  • 属性:value表示提供消息头名称。required表示否必须有此消息头。

5.5 CookieValue注解

  • 作用:用于把指定cookie名称的值传入控制器方法参数。
  • 属性:value表示指定cookie的名称。required表示是否必须有此cookie
    在这里插入图片描述

5.6 ModelAttribute注解

  • 作用:该注解是SpringMVC4.3版本以后新加入的。它可以用于修饰方法和参数。 ①出现在方法上,表示当前方法会在控制器的方法执行之前,先执行。它可以修饰没有返回值的方法,也可以修饰有具体返回值的方法。② 出现在参数上,获取指定的数据给参数赋值。
  • 属性:value表示用于获取数据的keykey可以是POJO的属性名称,也可以是map结构的key

5.6.1 作用于方法之上,方法带返回值

在这里插入图片描述

5.6.2 作用于参数上,方法不带返回值

在这里插入图片描述

5.7 SessionAttribute注解

  • 作用: 用于多次执行控制器方法间的参数共享
  • 属性:value表示用于指定存入的属性名称 。type表示用于指定存入的数据类型。
    在这里插入图片描述

6 响应数据与结果视图

6.1 返回值分类

6.1.1 String

在这里插入图片描述
controller方法返回字符串可以指定逻辑视图名,通过视图解析器解析为物理视图地址。

6.1.2 void

  • 如果controller方法没有返回值,会通过视图解析器访问以方法名作为页面名称的页面
    在这里插入图片描述

在这里插入图片描述

  • 利用Servlet原始API进行页面跳转
    @RequestMapping(value = "/testReturnVoid")
    public void testReturnVoid(HttpServletRequest request, HttpServletResponse response) throws Exception {
        System.out.println("TestReturnVoid is running!");

        // 1.请求转发,一次请求,需要将页面的路径补全,但是不需要项目路径
        // request.getRequestDispatcher("/WEB-INF/pages/hello.jsp").forward(request, response);

        // 2.重定向,两次请求,例如可以重定向至前面的String返回,仅需要写低一级的 @RequestMapping即可
        // response.sendRedirect("testReturnString");

        // 3.使用response直接返回结果
        response.setCharacterEncoding("UTF-8");
        response.setContentType("text/html;charset=utf-8");
        response.getWriter().write("testReturnVoid!!!");

        // 不往下执行
        return;
    }

6.1.3 ModelAndView

ModelAndViewSpringMVC提供的一个对象,该对象也可以用作控制器方法的返回值。

    @RequestMapping(value = "/testReturnModelAndView")
    public ModelAndView testReturnModelAndView() {
        System.out.println("TestReturnString is running!");
        ModelAndView mv = new ModelAndView();
        // 将键值对封装放入request域中
        mv.addObject("str","Hello World!");
        // 设置逻辑视图名称
        mv.setViewName("hello");

        return mv;
    }
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
    <title>Hello</title>
</head>
<body>
<h1>Hello World!</h1>
<hr>
${pageContext.request.getAttribute("str")}
</body>
</html>

结果:
在这里插入图片描述

6.2 请求转发和重定向

6.2.1 forward转发

controller方法在提供了String类型的返回值之后,默认就是请求转发。使用请求转发,既可以转发到jsp,也可以转发到其他的控制器方法。

6.2.2 Redirect重定向

contrller方法提供了一个String类型返回值之后,它需要在返回值里使用:redirect:,例如:
在这里插入图片描述
注:如果是重定向到jsp页面,则jsp页面不能写在WEB-INF目录中,否则无法找到。而且,在redirect:也不需要加项目的全路径(request.getContextPath())。

6.3 返回json数据

  • 传入json格式参数:使用RequestBody注解,具体见5.2
  • 返回json格式参数:使用ResponseBody注解,该注解用于将Controller的方法返回的对象,通过HttpMessageConverter接口转换为指定格式的数据如jsonxml等,通过Response响应给客户端。
    通过AJAX异步请求发送JSON数据,使用RequestBody解析为JavaBean对象,修改其中数据后,使用ResponseBody返回JSON数据。
    @RequestMapping(value = "/testJson")
    public @ResponseBody  // 使用@ResponseBody注解把JavaBean对象转换成json字符串,直接响应
    User testJson(@RequestBody User user) {  // 使用@RequestBody注解把json的字符串转换成JavaBean的对象
        System.out.println("testJson is running!");
        System.out.println(user);

        user.setUsername("HP");
        return user;
    }
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" isELIgnored="false" %>
<html>
<head>
    <title>index</title>
    <script src="${pageContext.request.contextPath}/js/jquery.min.js"></script>
    <script>
        $(function () {
            $("#btn").click(function () {
                $.ajax({
                    url:"User/testJson",
                    contentType:"application/json;charset=UTF-8",
                    data:'{"username":"HC","password":"123456","birthday":"1998-09-02"}',
                    dataType:"json",
                    type:"post",
                    success:function (data) {
                        alert(data);
                    }
                })
            })
        })
    </script>
</head>
<body>
<button id="btn">修改</button>
</body>
</html>

注:json字符串和JavaBean对象互相转换的过程中,需要使用jacksonjar包如下:

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

7 文件上传

7.1 SpringMVC结合第三方组件实现文件上传

文件上传的前提

  • form表单的enctype属性取值必须是:multipart/form-data (默认值是:application/x-www-form-urlencoded)
  • method属性取值必须是Post
  • 提供一个文件选择域<input type=”file” />
    第三方jar包依赖:
        <dependency>
            <groupId>commons-io</groupId>
            <artifactId>commons-io</artifactId>
            <version>2.4</version>
        </dependency>
        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.3.1</version>
        </dependency>

实现如下:

  • JSP页面
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" isELIgnored="false" %>
<html>
<head>
    <title>index</title>
</head>
<body>
<form action="/User/fileUpload01" method="post" enctype="multipart/form-data">
    选择文件:<input type="file" name="uploadFile"><br/>
    <input type="submit" value="上传">
</form>
</body>
</html>
  • 控制器如下:
@Controller
@RequestMapping(value = "/User")
public class HelloWorld {
    @RequestMapping(value = "/fileUpload01")
    public String fileUpload01(HttpServletRequest request) throws Exception {
        System.out.println("FileUpload01 is running!!!");

        // 1、设置上传的位置,如果该文件夹不存在则创建
        String path = request.getSession().getServletContext().getRealPath("/uploads");
        File file = new File(path);
        if (!file.exists())
            file.mkdirs();

        // 2、解析request对象,获取上传文件项
        DiskFileItemFactory factory = new DiskFileItemFactory();
        ServletFileUpload upload = new ServletFileUpload(factory);

        List<FileItem> fileItems = upload.parseRequest(request);

        for (FileItem fileItem : fileItems) {
            // 进行判断,当前item对象是否是上传文件项
            if (fileItem.isFormField()) {

            } else {
                // 获取上传文件的名称,并依据uuid把文件的名称设置唯一值
                String fileName = fileItem.getName();
                String uuid = UUID.randomUUID().toString().replace("-", "");
                fileName = uuid + "_" + fileName;

                // 完成文件上传
                fileItem.write(new File(path, fileName));
                // 删除临时文件
                fileItem.delete();
            }
        }

        return "hello";
    }
}

7.2 SpringMVC实现文件上传

SpringMVC本身提供了一个文件解析器用来解析request,需要在SpringMVC.xml中配置如下:

    <!--
        配置文件上传解析器,id的值是固定的,只能是multipartResolver
    -->
    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="maxUploadSize">
            <value>1048576</value>
        </property>
    </bean>

在配置了SpringMVC自带的文件解析器后就不需要用第三方jar包进行request中的文件解析。如下:

@Controller
@RequestMapping(value = "/User")
public class HelloWorld {
    @RequestMapping(value = "/fileUpload02")
    public String fileUpload02(MultipartFile upload, HttpServletRequest request) throws Exception {
        System.out.println("FileUpload02 is running!!!");

        // 1、设置上传的位置,如果该文件夹不存在则创建
        String path = request.getSession().getServletContext().getRealPath("/uploads");
        File file = new File(path);
        if (!file.exists())
            file.mkdirs();

        // 2、获取上传文件的名称,并依据uuid把文件的名称设置唯一值
        String fileName = upload.getOriginalFilename();
        String uuid = UUID.randomUUID().toString().replace("-", "");
        fileName = uuid + "_" + fileName;
        // 3、完成文件上传
        upload.transferTo(new File(path, fileName));

        return "hello";
    }
}

注:public String fileUpload02(MultipartFile upload, HttpServletRequest request)中的upload与下面前端文件上传的<input type="file" name="upload">name属性要一致。

<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" isELIgnored="false" %>
<html>
<head>
    <title>index</title>
</head>
<body>
<form action="/User/fileUpload02" method="post" enctype="multipart/form-data">
    选择文件:<input type="file" name="upload"><br/>
    <input type="submit" value="上传">
</form>
</body>
</html>

7.3 SpringMVC实现跨服务器文件上传

7.3.1 分服务器

在这里插入图片描述
不同的服务器处理不同的额功能需求。

7.3.2 文件服务器的搭建

  • 配置项目

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  • 配置Tomcat服务器
    在这里插入图片描述
    在这里插入图片描述

7.3.3 应用服务器向文件服务器存储文件

  • 控制器
@RequestMapping(value = "/fileUpload03")
    public String fileUpload03(MultipartFile upload) throws Exception {
        System.out.println("FileUpload03 is running!!!");

        // 1、设置上传的位置
        String path = "http://localhost:9090/uploads/";

        // 2、获取上传文件的名称,并依据uuid把文件的名称设置唯一值
        String fileName = upload.getOriginalFilename();
        String uuid = UUID.randomUUID().toString().replace("-", "");
        fileName = uuid + "_" + fileName;

        // 3、完成跨服务器文件上传
        // 创建客户端的对象
        Client client = Client.create();
        // 和图片服务器进行连接
        WebResource resource = client.resource(path + fileName);
        // 上传文件
        resource.put(upload.getBytes());

        return "hello";
    }
  • JSP页面
<%@ page contentType="text/html;charset=UTF-8" language="java" pageEncoding="UTF-8" isELIgnored="false" %>
<html>
<head>
    <title>index</title>
</head>
<body>
<form action="/User/fileUpload03" method="post" enctype="multipart/form-data">
    选择文件:<input type="file" name="upload"><br/>
    <input type="submit" value="上传">
</form>
</body>
</html>

注:
①需要在文件服务器下创建相应的文件夹
在这里插入图片描述
②在应用服务器的conf/web.xml中配置
在这里插入图片描述

8 异常处理

8.1 异常处理流程

在这里插入图片描述
dao层、service层和controller层出现异常都通过throws Exception向上抛出,最后由springmvc前端控制器交由异常处理器进行异常处理。

8.2 异常处理

8.2.1 自定义异常类

package com.hc.exception;

public class SystemException extends Exception {
    private String message;

    public SystemException(String message) {
        this.message = message;
    }

    @Override
    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}

8.2.2 自定义异常处理器

package com.hc.exception;

import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class SystemExceptionResolver implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {

        SystemException systemException = null;

        // 如果抛出的是系统自定义异常则直接转换
        if (e instanceof SystemException) {
            systemException = (SystemException) e;
        } else {
            // 如果抛出的不是系统自定义异常则重新构造一个系统错误异常。
            systemException = new SystemException("系统错误!!!");
        }

        ModelAndView mv = new ModelAndView();
        mv.addObject("message", systemException.getMessage());
        mv.setViewName("error");

        return mv;
    }
}

8.2.3 配置异常处理器

    <!-- 配置自定义异常处理器 -->
    <bean id="systemExceptionResolver" class="com.hc.exception.SystemExceptionResolver"/>

8.2.4 配置错误页面

<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
    <title>Error</title>
</head>
<body>
<h1>${pageContext.request.getAttribute("message")}</h1>
</body>
</html>

8.2.5 编写控制器

@RequestMapping(value = "testException")
    public String testException() throws SystemException {
        System.out.println("TestException is running!");

        try {
            // 构造异常
            int num = 1 / 0;
        } catch (Exception e) {
            e.printStackTrace();
            throw new SystemException("查询失败!!!");
        }

        return "hello";
    }

8.2.6 测试

在这里插入图片描述

9 SpringMVC拦截器

9.1 什么是SpringMVC的拦截器

  • SpringMVC框架中的拦截器用于对处理器进行预处理和后处理的技术。

  • SpringMVC可以定义拦截器链,连接器链就是将拦截器按着一定的顺序结成一条链,在访问被拦截的方法时,拦截器链中的拦截器会按着定义的顺序执行。

  • 拦截器和过滤器的功能比较:
    ①过滤器是Servlet规范的一部分,任何框架都可以使用过滤器技术。而拦截器是SpringMVC框架独有的。
    ②过滤器配置了/*,可以拦截任何资源。而拦截器只会对控制器中的方法进行拦截。

  • 自定义拦截器需要实现HandlerInterceptor接口。

9.2 SpringMVC拦截器配置

9.2.1 配置控制器方法

    @RequestMapping(value = "testInterceptor")
    public String testInterceptor() {
        System.out.println("TestInterceptor is running!");
        return "hello";
    }

9.2.2 自定义拦截器

package com.hc.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class SystemInterceptor implements HandlerInterceptor {

    /**
     * 如何调用:按拦截器定义顺序调用
     * 何时调用:只要配置了都会调用
     * 有什么用:如果程序员决定该拦截器对请求进行拦截处理后还要调用其他的拦截器,或者是业务处理器去进行处理,则返回true。
     * 如果程序员决定不需要再调用其他的组件去处理请求,则返回false。
     *
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("SystemInterceptor preHandle is running!");
        return true;
    }

    /**
     * 如何调用:按拦截器定义逆序调用
     * 何时调用:在拦截器链内所有拦截器返成功调用
     * 有什么用:在业务处理器处理完请求后,但是DispatcherServlet向客户端返回响应前被调用,在该方法中对用户请求request进行处理。
     *
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("SystemInterceptor postHandle is running!");
    }

    /**
     * 如何调用:按拦截器定义逆序调用
     * 何时调用:只有preHandle返回true才调用
     * 有什么用:可以在该方法中进行一些资源清理的操作。
     * 注:如果有多个拦截器,这时拦截器1的preHandle方法返回true,但是拦截器2的preHandle方法返回false,而此时拦截器1的afterCompletion方法是执行的
     *
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("SystemInterceptor afterCompletion is running!");
    }
}

9.2.3 配置自定义拦截器

    <mvc:interceptors>
        <mvc:interceptor>
            <!--用于指定对拦截的url-->
            <mvc:mapping path="/User/*"/>
            <!--用于指定排除的url-->
            <!-- <mvc:exclude-mapping path=""/>-->
            <bean id="systemInterceptor" class="com.hc.interceptor.SystemInterceptor"/>
        </mvc:interceptor>
    </mvc:interceptors>

9.2.4 测试

在这里插入图片描述

9.3 多个SpringMVC拦截器执行顺序

9.3.1 再次自定义一个拦截器

package com.hc.interceptor;

import org.springframework.web.servlet.HandlerInterceptor;
import org.springframework.web.servlet.ModelAndView;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public class SystemInterceptor02 implements HandlerInterceptor {

    /**
     * 如何调用:按拦截器定义顺序调用
     * 何时调用:只要配置了都会调用
     * 有什么用:如果程序员决定该拦截器对请求进行拦截处理后还要调用其他的拦截器,或者是业务处理器去进行处理,则返回true。
     * 如果程序员决定不需要再调用其他的组件去处理请求,则返回false。
     *
     * @param request
     * @param response
     * @param handler
     * @return
     * @throws Exception
     */
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("SystemInterceptor02 preHandle is running!");
        return true;
    }

    /**
     * 如何调用:按拦截器定义逆序调用
     * 何时调用:在拦截器链内所有拦截器返成功调用
     * 有什么用:在业务处理器处理完请求后,但是DispatcherServlet向客户端返回响应前被调用,在该方法中对用户请求request进行处理。
     *
     * @param request
     * @param response
     * @param handler
     * @param modelAndView
     * @throws Exception
     */
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("SystemInterceptor02 postHandle is running!");
    }

    /**
     * 如何调用:按拦截器定义逆序调用
     * 何时调用:只有preHandle返回true才调用
     * 有什么用:可以在该方法中进行一些资源清理的操作。
     * 注:如果有多个拦截器,这时拦截器1的preHandle方法返回true,但是拦截器2的preHandle方法返回false,而此时拦截器1的afterCompletion方法是执行的
     *
     * @param request
     * @param response
     * @param handler
     * @param ex
     * @throws Exception
     */
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("SystemInterceptor02 afterCompletion is running!");
    }
}

9.3.2 配置自定义拦截器

    <mvc:interceptors>
        <mvc:interceptor>
            <!--用于指定对拦截的url-->
            <mvc:mapping path="/User/*"/>
            <!--用于指定排除的url-->
            <!-- <mvc:exclude-mapping path=""/>-->
            <bean id="systemInterceptor" class="com.hc.interceptor.SystemInterceptor"/>
        </mvc:interceptor>
        <mvc:interceptor>
            <mvc:mapping path="/User/*"/>
            <bean id="systemInterceptor02" class="com.hc.interceptor.SystemInterceptor02"/>
        </mvc:interceptor>
    </mvc:interceptors>

9.3.3 测试

在这里插入图片描述

9.4 测试afterCompletion方法

如果有多个拦截器,这时拦截器1preHandle方法返回true,但是拦截器2preHandle方法返回false,而此时拦截器1afterCompletion方法会不会执行?
在这里插入图片描述
会执行!!!

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

是聪聪黄吖

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值