SpringMVC用法详解

SpringMVC执行流程

1.客户端向服务端发起请求到Spring MVC总体入口(前端控制器)DispatcherServlet

2.DispatcherServlet将URL交给映射器处理器HandlerMapping进行解析URL,寻找匹配对应控制单元路径,存在,则执行拦截器Interceptor的预处理方法preHandle,映射器处理器返回执行链到DispatcherServlet

3.DispatcherServlet调用处理器适配器HandlerAdapter,执行具体的控制单元方法。控制单元HandlerMethod执行完成后产生模型和视图组件ModelAndView返回给DispatcherServlet,期间执行拦截器postHandle方法

4.DispatcherServlet调用视图解析器ViewResolver解析视图,产生视图View对象返回给DispatcherServlet,渲染视图后,执行拦截器afterCompletion方法

5.DispatcherServlet把最终视图执行结果响应给客户端浏览器。

概念

M:在模型层包含:数据校验
V:在视图层包含:国际化、标签库
C:在控制层包含的功能就更多了:转发重定向、参数、拦截器、作用域等
在这里插入图片描述

hello world

1.导入依赖

//springmvc
<dependency>
     <groupId>org.springframework</groupId>
     <artifactId>spring-webmvc</artifactId>
     <version>5.3.16</version>
</dependency>

//tomcat7插件
</dependencies>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat7-maven-plugin</artifactId>
                <version>2.2</version>
                <configuration>
                    <path>/</path>//访问当前模块下的资源时在对口号后要加入的路径
                    <port>8080</port>//端口号
                </configuration>
            </plugin>
        </plugins>
    </build>

2.Spring MVC配置文件

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:mvc="http://www.springframework.org/schema/mvc"
       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
        https://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">
    <!-- 扫描控制器类,千万不要把service等扫描进来,也千万不要在Spring配置文件扫描控制器类所在包 -->
    <context:component-scan base-package="com.bjsxt.controller"></context:component-scan>
    <!-- 让Spring MVC的注解生效-->
    <mvc:annotation-driven></mvc:annotation-driven>
</beans>

3.在src/main下创建webapp/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">
    <servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <!-- 参数名称必须叫做:contextConfigLocation。单词和大小写错误都导致配置文件无法正确加载 -->
            <param-name>contextConfigLocation</param-name>
			<!-- 就是你刚才创建springmvc配置文件的名称 -->
            <param-value>classpath:springmvc.xml</param-value>
        </init-param>
        <!-- 配置上的效果:Tomcat启动立即加载Spring MVC框架的配置文件-->
        <load-on-startup>1</load-on-startup>
    </servlet>
    <servlet-mapping>
        <servlet-name>springmvc</servlet-name>
        //除jsp不能访问外,其余可以访问
        <url-pattern>/</url-pattern>
    </servlet-mapping>
</web-app>

4.创建控制器类

@Controller// 放入到Spring MVC容器中
public class DemoController {
    @RequestMapping("/fs")
    @ResponseBody
    public String show(){
        return "hello world";
    }
}

访问 localhost/8080/fs
页面上出现hello world

SpringMVC访问.html文件问题解决

我们在SpringMVC中的控制器(org.springframework.web.servlet.DispatcherServlet)中默认是jsp页面,
默认的配置DispatcherServlet屏蔽了html页面的访问。

在web.xml中加入

 <servlet-mapping>
        <servlet-name>default</servlet-name>
        <url-pattern>*.html</url-pattern>
    </servlet-mapping>

即可

@RequestMapping注解

@RequestMapping注解可以写在控制器类上,也可以写在控制单元方法上。
所有属性:
1.value属性: 定义映射路径。(默认)
2.path属性和value属性使用方式是相同的,都是设置控制单元的映射路径。
3.name:给控制单元定义一个名称。可以理解name是控制单元的注释。
4.method属性类型是RequestMethod[],RequestMethod是枚举类型,支持HTTP协议中绝大多数请求类型。
例: @RequestMapping(value = “/testMethod”,method = {RequestMethod.DELETE,RequestMethod.POST})
Spring MVC 框架针对不同请求方式提供了5个专门请求方式的注解
​ @PostMapping(“/first”) 等效于 @RequestMapping(value = “/first”,method = RequestMethod.POST)
​ @GetMapping(“/first”)等效于 @RequestMapping(value = “/first”,method = RequestMethod.GET)
​ @DeleteMapping(“/first”)等效于 @RequestMapping(value = “/first”,method = RequestMethod.DELETE)
​ @PutMapping(“/first”)等效于 @RequestMapping(value = “/first”,method = RequestMethod.PUT)
​ @PatchMapping(“/first”)等效于 @RequestMapping(value = “/first”,method = RequestMethod.PATCH)
5.params属性类型是String[],表示请求中必须包含指定名称的请求参数。
6.headers属性类型是String[],表示请求头中必须包含指定的请求头参数。
7.consumers表示处理请求内容(Content-Type)的类型,平时多不设置,由Spring MVC自动判断。
8.produces类型是String[],作用是设置@ResponseBody注解的响应内容类型。且仅当请求头中Accept中包含的值才生效。
例: @RequestMapping(value=“/testProduces”,produces = “text/html;charset=utf-8”) 防止使用responseBody注解直接打印带浏览器上时中文乱码

映射路径

注意.html和.jsp放在webapp下不是WEB-INF下
如果访问某控制器的路径是/fs/hello.jsp,那么hello.jsp应在webapp/fs/下,最前面的 “/” 代表在webapp下

静态资源放行

<!--配置静态资源放行-->
<!--mapping:当URI是什么样格式时,不再执行控制器,而是寻找静态资源。 ** 是通配符,表示任意层路径 -->
<!--location:去哪个目录中寻找静态资源。mapping中**的值是什么,就去location目录中找对应资源-->
<!--例如URL是http://localhost:8080/bjsxt/js/jquery.js 其中mapping的**就是jquery.js,就会去location的/js/目录中寻找jquery.js -->
<mvc:resources mapping="/js/**" location="/js/"></mvc:resources>
<mvc:resources mapping="/css/**" location="/css/"></mvc:resources>
<mvc:resources mapping="/images/**" location="/images/"></mvc:resources>

注意上述mapping和location中最前面的 “/” 表示webapp路径下

Tomcat8配置步骤

直接点击Maven面板的plugin -> Tomcat8:run-war 。一定要注意,是tomcat8:run-war不是tomcat8:run

在pom.xml中加入

    <pluginRepositories>
        <pluginRepository>
            <id>mvnrepository</id>
            <url>https://artifacts.alfresco.com/nexus/content/repositories/public/</url>
        </pluginRepository>
    </pluginRepositories>
    <build>
        <plugins>
            <plugin>
                <groupId>org.apache.tomcat.maven</groupId>
                <artifactId>tomcat8-maven-plugin</artifactId>
                <version>3.0-r1756463</version>
                <configuration>
                    <port>8081</port>
                    <path>/bjsxt</path>
                </configuration>
            </plugin>
        </plugins>
    </build>

控制器接收请求参数

1.获取普通表单参数

只需要包含在控制单元中提供与请求参数同名的方法参数(八大基本数据类型建议使用封装类类型)即可。Spring MVC会自动进行类型转换

@RequestParam 注解
属性:
1.name:当请求参数名和控制单元参数名不对应时,可以使用name指定请求参数名。这样方法参数就可以不与请求参数对应了。
2.value:是name属性的别名。功能和name属性相同。之所以再次设置一个和name属性相同的value,(是因为在Java注解中,当需要设置value属性,且只需要设置value属性时可以省略value属性名,这样写起来更加简单。)
3.defaultValue:默认值。表示当请求参数中没有这个参数时给与的默认值。defaultValue类型是String类型,Spring MVC会对值进行类型转换,转换成参数类型。
例: @RequestParam(defaultValue = “16”) Integer age
4.required:boolean类型,表示请求中是否必须包含参数。如果设置为true,且请求中没有这个参数,响应时出现400状态码。

2.使用类对象作为参数

要求属性名和参数名对应,类型转换由Spring MVC自动完成。不支持@RequestParam注解。所以需要先建立一个类。且类中必须提供属性的getter和setter方法,因为Spring MVC就是通过getter和setter把请求参数的值设置到类的属性中。

JavaBean和简单数据类型混合使用,类中和简单类型重名时,Spring MVC会都给设置上。

3.接收多个同名参数

1.使用数组接收多个同名请求参数

@RequestMapping("/testHover")
    public String testHovers(String [] hovers)

2.使用List接收多个同名请求参数

    @RequestMapping("/testHover")
    public String testHovers(@RequestParam("hovers") List<String> hovers)

4.接收日期类型参数

使用Date类型接收客户端传递过来的数据,默认情况下必须保证客户端参数格式和服务器日期格式一致。可以在计算机屏幕右下角查看到日期格式,所以只要保证客户端传递过来的日期是yyyy/MM/dd hh:mm:ss的格式,Spring MVC会自动进行类型转换。其中小时分钟秒可以省略不写。
也可以在浏览器地址栏输入包含日期和小时分钟秒的数据 http://localhost:8081/bjsxt/testDate?date=2022/5/1%202012:13:14 其中2022/5/1和12:13:14之间是空格,当回车发送请求后,会自动把空格解析为%2020,因为空格是URL中的特殊字符。
默认情况下不支持输入小时分钟秒。

@DateTimeFormat
可以使用@DateTimeFormat自定义时间格式。@DateTimeFormat可以写在控制单元Date类型参数之前,也可以写在JavaBean(类对象)的属性上面。

@RequestMapping("/testDate")
    public String testDate(@DateTimeFormat(pattern = "yyyy-MM-dd") Date date)

5.接收请求头数据

@RequestHeader注解
上面这种写法中方法参数名和请求参数名完全对应。但是在Java中参数名的命名规范是首字母小写。Spring MVC对于接收请求参数时名称不区分大小写。所以下面的写法也是可以的。

@RequestMapping("/testHeader")
    public String testHeader(@RequestHeader("Accept-Encoding") String suiyi)

6.获取请求体中内容

Spring MVC 中文乱码问题

Tomcat默认的接收请求GET方式的是IOS-8859-1,所以无论客户端传递过来的数据是哪种编码方式,都会被Tomcat转换为ISO-8859-1.如果希望正确的显示中文,还需要把内容转换为UTF-8编码。
get请求乱码问题

@RequestMapping("/testEncoding")
    public String testEncoding(String name) throws UnsupportedEncodingException {
    	//转码
        String newName = new String(name.getBytes("iso-8859-1"),"utf-8");
        return "suiyi";
    }

POST方式中文乱码解决
web.xml文件中配置下面内容。表示所有的请求编码都设置为UTF-8编码。

<!--配置字符编码过滤器-->
<filter>
    <filter-name>code</filter-name>
  <filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
    <init-param>
        <param-name>encoding</param-name>
        <param-value>utf-8</param-value>
    </init-param>
</filter>
<filter-mapping>
    <filter-name>code</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

Restful请求格式

简单来说就是: 前端传递来的参数在访问路径上

@GetMapping("/user/{id}")
public String selUser(@PathVariable Integer id)

Spring MVC中的转发和重定向

在资源路径前面添加forward: 表示转发。因为写不写forward:都是转发,所以为了代码写起来简单一些,多省略forward:

@RequestMapping("/test/test2")
public String test2(){
    return "forward:/first.jsp";
}

如果希望使用重定向跳转到其他资源,只能在资源路径最前面明确添加redirect:,下面代码就是使用重定向方式的写法。

@RequestMapping("/test/test2")
public String test2(){
    return "redirect:/first.jsp";
}

Spring MVC转发和重定向时绝对路径: 在Spring MVC中无论是转发还是重定向,使用绝对路径时/都表示项目根目录。

@ResponseBody注解

1.简单使用
当方法上添加@ResponseBody注解后,控制单元方法返回值将不再被视图解析器进行解析。
而是把返回值放入到响应流中进行响应。

    @RequestMapping("/fs")
    @ResponseBody
    public String demo(){
        return "hello";
    }

上面代码等效于直接使用PrintWriter对象进行打印

    @RequestMapping("/demo1")
    public void demo1(HttpServletResponse resp) throws IOException {
        PrintWriter out = resp.getWriter();
        out.print("bjsxt");
        out.flush();
        out.close();
    }

2.设置响应内容类型及编码格式
响应头中的的Content-Type表示响应类型及编码格式
在使用@ResponseBody注解时,只要返回值类型不是类或Map或List等满足键值对类型。
Spring MVC 都会设置响应内容类型为text/html;charset=ISO-8859-1。
**text/html;charset=ISO-8859-1中编码是不支持中文的。**所以返回值中包含中文,打印在浏览器中会出现乱码。

想要改变@ResonseBody注解的响应内容类型(Content-Type)只能通过@RequestMapping的produces属性进行设置。

    @RequestMapping(value="/demo1",produces = "text/html;charset=utf-8")
    @ResponseBody
    public String demo() {
        return "你好,世界!";
    }

3.自动转换为JSON字符串
如果返回值为JavaBean、JavaBean数组、List<JavaBean类型>、Map等满足键值对的类型,Jackson就会把对象转换为JSON字符串,设置到响应流中。同时会设置响应内容类型(Content-Type)为application/json;charset=utf-8,因为Spring MVC默认使用Jackson作为JSON转换工具,所以必须保证项目中存在Jackson的依赖。

        <dependency>
            <groupId>com.fasterxml.jackson.core</groupId>
            <artifactId>jackson-databind</artifactId>
            <version>2.9.10.8</version>
        </dependency>
    @RequestMapping("/demo")
    @ResponseBody // 千万不要忘记写了
    public Map<String,Object> demo(){
        HashMap<String, Object> map = new HashMap<>();
        map.put("name","张三");
        map.put("age",18);
        return map;
    }

4.@RestController
@RestController = @Controller + @ResponseBody
一旦类上使用了@RestController,所有控制单元返回都是XML或JSON数据。而无法实现页面跳转功能了。

@RequestBody注解

转换请求体
1.@RequestBody注解底层依赖的依然是Jackson工具包,其作用是把客户端传递过来的请求体中JSON或XML数据转换为Map、类、List<类>、List等类型。既然是转换为请求体数据,所以不能是GET类型请求,多用在POST类型的请求中。

2.contentType:必须设置。常见取值“application/json”或"application/xml"。
如果没有设置这个属性,取值默认是application/x-www-form-urlencoded,表示普通表单参数。
当设置为"application/json"时,会把data取值设置到请求体中,所以服务端接收参数时就不能按照普通表单参数进行接收。

3.客户端把JSON或XML设置到请求体,服务端使用JavaBean或Map接收请求体数据时,才能在控制单元参数前面添加@RequestBody注解。

@RequestMapping("/testContentType")
    @ResponseBody
    public People testContentType(@RequestBody People peo)

视图解析器

1.作用通过springmvc控制器跳转页面时,简化控制器中返回值的书写
配置了后,所有的控制器返回值都会进行拼接

2.自定义视图解析器

视图解析器支持程序员的自定义,只有在需要简化控制单元返回值的情况下才会自定义视图解析器。
springmvc.xml

<!--配置自定义试图解析器-->
<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    	<!-- 前缀 -->
        <property name="prefix" value="/WEB-INF/page/"></property>
    	<!-- 后缀 -->
        <property name="suffix" value=".jsp"></property>
</bean>
配置自定义视图解析器前
    @RequestMapping("/showSuiyi")
    public String showSuiyi(){
        return "/WEB-INF/page/suiyi.jsp";
    }
配置自定义视图解析器后
    @RequestMapping("/showSuiyi")
    public String showSuiyi(){
        return "suiyi";
    }

当自定义视图解析器后,返回值前面和后面都会固定拼接字符串(在没有使用其他注解情况下)。
配置了就会按照下面流程走,没有配置,控制器返回后就直接去找页面了
在这里插入图片描述

3. 自定义视图解析器下跳转到其他控制器
配置自定义视图解析器后,返回值前面和后面都会固定拼接字符串(在没有使用其他注解情况下)。
但是如果控制单元执行完,并不希望跳转到视图,而是跳转到控制器,这时需要在返回值前面明确添加forward:或redirect: ,
这样就不走视图解析器了。

    @RequestMapping("/showSuiyi3")
    public String showSuiyi3(){
        return "suiyi3";// 跳转到 /WEB-INF/page/suiyi3.jsp
    }
    @RequestMapping("/demo")
    public String demo(){
        return "forward:/showSuiyi3"; // 不走视图解析器了。跳转到/showSuiyi3控制器单元
    }

4.使用Restful显示页面

    @RequestMapping("/page/{yemian}")
    public String showPage(@PathVariable String yemian){
        return "/WEB-INF/page/"+yemian+".jsp";
    }

Spring MVC文件上传

默认的表单内容类型application/x-www-form-urlencoded还不支持传递文件流。所以需要在<form>的enctype中设置enctype="multipart/form-data"才表示把文件和其他表单参数设置到请求体中。

<form action="/upload" method="post" enctype="multipart/form-data">

在使用CommonsMultipartResovler因为底层是Apache的File-Upload,所以需要导入两个jar包。
commons-io-2.2.jar // 工具包,里面有需要使用的工具类
commons-fileupload-1.3.1.jar // Apache 文件上传的jar包。
1.导入依赖

        <dependency>
            <groupId>commons-fileupload</groupId>
            <artifactId>commons-fileupload</artifactId>
            <version>1.4</version>
        </dependency>

2.配置上传解析器bean
只有配置了MultipartResovler,Spring MVC 才会解析上传文件流数据。
同时<bean>的id必须叫做multipartResovler,叫其他名字无效。

<!-- 文件上传时,必须配置文件解析器 -->
<bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver"></bean>

3.编写控制器的形参
MultipartFile对象中,我们声明MultipartFile类型的形参接即可,但是形参名必须和file标签的name(前端中的属性)属性值一致。然后我们在单元方法中将接收到的上传资源通过流存储到服务器的硬盘中即可。

​ 如果客户端就一个文件域使用一个MultipartFile对象接收就可以了。
​ 如果客户端是多个同名的文件域使用MultipartFile 数组接收。
​ 如果客户端是多个不同名的文件域使用多个MultipartFile对象接收就可以了。

在这里插入图片描述

    @RequestMapping("/upload")
    public String upload(String name, String address, MultipartFile photo) throws IOException {
        photo.transferTo(new File("D:/images", photo.getOriginalFilename()));
        return "/upload.jsp";
    }

4.限制上传文件大小
在很多项目中是对上传文件做严格大小限制的。当文件太大会占用服务器存储空间。当文件太小(尤其是图片)可能显示不清晰。
在CommonsMultipartResolver中提供了setmaxUploadSize(long)方法,表示设置上传文件的大小。
单位是字节byte。
默认值为-1,表示无限制。
因为是setter方法,所以可以在配置Bean时直接进行设置注入。下面演示效果为限制上传文件大小为1KB。

    <bean id="multipartResolver" class="org.springframework.web.multipart.commons.CommonsMultipartResolver">
        <property name="maxUploadSize" value="1024"></property>
    </bean>

Spring MVC文件下载

当超链接访问的是浏览器本身能打开的资源。浏览器直接打开。
这个特点就是响应头参数Content-Disposition控制的,其默认值为inline,表示能打开就打开,不能打开就下载。

Content-Disposition可取值有两个:
(1)inline。直接在浏览器中打开(默认)(能打开就打开,不能打开就下载)
(2)attachment。以附件形式下载(恒下载)

2.测试inline效果

D:\OneDrive\����\fs\���\ideaTest\Demo\springmvc\target\springmvc-1.0-SNAPSHOT
req.getServletContext().getRealPath(“/”)//编译的字节码的绝对路径
req.getServletContext().getRealPath(“/images”)//编译的字节码的绝对路径 + “/images”

    @RequestMapping("/download")
    public void download(HttpServletRequest req, HttpServletResponse response, String filename) {
        try {
            // filename=的值就是客户端看到的下载文件名称
            attachment。以附件形式下载(恒下载)
            response.setHeader("Content-Disposition", "attachment;filename=" + filename);
            指定服务器下载目录
            File file = new File(req.getServletContext().getRealPath("/images"), filename);
            FileInputStream fis = new FileInputStream(file);
            //下载到客户端
            ServletOutputStream os = response.getOutputStream();
            IOUtils.copy(fis, os);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

文件下载中包含中文名称解决办法

 @RequestMapping("/download")
    public void download(HttpServletRequest req, HttpServletResponse response, String filename) {
        try {
            // 因为是GET请求,所以要解决请求参数中文乱码问题
            String fileNameUtf8 = new String(filename.getBytes("iso-8859-1"), "utf-8");
            // 图片名称满足固定格式
            String newFilenameUtf8 = "下载的文件"+fileNameUtf8;
            String newFilenameISO = new String(newFilenameUtf8.getBytes("utf-8"),"iso-8859-1");
            // 此处是ISO-8859-1编码的内容
            response.setHeader("Content-Disposition", "attachment;filename=" + newFilenameISO);
            // 此处必须是UTF-8解决参数乱码问题的名称
            File file = new File(req.getServletContext().getRealPath("/images"), fileNameUtf8);
            FileInputStream fis = new FileInputStream(file);
            ServletOutputStream os = response.getOutputStream();
            IOUtils.copy(fis, os);
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

拦截器

过滤器的作用是保护请求的服务器资源,在请求资源被执行之前,如果请求地址符合拦截范围,则会先执行过滤器。
过滤器的执行时机,是在Servlet之前执行的。
但是在使用了SpringMVC后,Servlet只有一个了,也就是DisptcherServlet,SpringMVC给出了拦截器来实现单元方法的拦截,
拦截器的执行是在DispatcherServlet之后和单元方法之前的,这样我们就可以在单元方法被之前之前对请求进行自定义的拦截处理了。

创建拦截器类

1.通过实现HandlerInterceptor接口,或继承HandlerInterceptor接口的实现类(如HandlerInterceptorAdapter)来定义。
2.通过实现WebRequestInterceptor接口,或继承WebRequestInterceptor接口的实现类来定义。

两者不同点是:
1.WebRequestInterceptor的入参WebRequest是包装了HttpServletRequest 和HttpServletResponse的,通过WebRequest获取Request中的信息更简便。
2.WebRequestInterceptor的preHandle是没有返回值的,说明该方法中的逻辑并不影响后续的方法执行,所以这个接口实现就是为了获取Request中的信息,或者预设一些参数供后续流程使用。
3.HandlerInterceptor的功能更强大也更基础,可以在preHandle方法中就直接拒绝请求进入controller方法。

推荐使用方式一
1.在src下创建包,包名随意,并创建java类实现HandlerInterceptor接口。并实现preHandle、postHandle、afterCompletion三个方法。

public class MyInterceptor implements HandlerInterceptor {
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("prohandle");
        return false;
    }
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {

    }
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {

    }
}

2.配置拦截器

<?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
            http://www.springframework.org/schema/context/spring-context-4.0.xsd
            http://www.springframework.org/schema/mvc
            http://www.springframework.org/schema/mvc/spring-mvc-4.0.xsd
        ">
    <!--配置注解扫描路径-->
        <context:component-scan base-package="com.bjsxt.controller"></context:component-scan>
    <!--配置拦截器-->
        <mvc:interceptors>
            <!--配置具体的拦截器的bean及其拦截范围-->
            <mvc:interceptor>
            	注意是requestMapping上叠加的路径
                <mvc:mapping path="/myController/demo"/><!--配置拦截的单元方法的访问路径,第一个/表示项目根目录-->
                <bean class="com.bjsxt.interceptor.MyInterceptor"></bean><!--配置拦截器的bean对象,只在当前mvc:interceptor内有效-->
            </mvc:interceptor>
        </mvc:interceptors>
</beans>

拦截方法介绍

1.preHandle方法
作用:真正执行拦截的方法,返回false表示拦截此次请求,返回true表示放行。
执行时机:单元方法之前。
参数:
HttpServletRequest request:此次拦截的请求的request对象
HttpServletResponse response:此次拦截的请求的response对象
Object handler:HandlerMethod类型,存储了拦截的单元方法的method对象。

返回值: boolean类型,false表示拦截,true表示放行。

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    HandlerMethod hm=(HandlerMethod)handler;//强转为HandlerMethod类型
    Method method = hm.getMethod();//获取此次请求的单元方法的方法对象*/
    System.out.println(method.getName());
    System.out.println("MyInterceptor.preHandle");
    return false;
}

2.postHandle方法(preHandle返回true才执行)
作用:对单元方法转发的资源进行拦截拦截,可以对model中的数据进行校验等。
执行时机:单元方法之后,转发的视图资源之前。
参数:
HttpServletRequest request:此次拦截的请求的request对象。
HttpServletResponse response:此次拦截的请求的response对象。
​Object handler:HandlerMethod类型,存储了拦截的单元方法的method对象。
​ModelAndView: 存储了model和view信息的对象。

public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    //重置跳转的资源路径
    String viewName = modelAndView.getViewName();
    //modelAndView.setViewName("forward:/bb.jsp");
    //获取Model对象中存储的流转数据
    Map<String, Object> model = modelAndView.getModel();
    System.out.println("MyInterceptor.postHandle----"+viewName+"-----"+model.get("str"));
}

3.afterCompletion方法(preHandle返回true才执行)
作用:对整个拦截流程中的异常信息进行捕捉处理。
执行时机:处理完视图和模型数据,渲染视图完毕之后执行。
参数:
HttpServletRequest request:此次拦截的请求的request对象。
HttpServletResponse response:此次拦截的请求的response对象。
​Object handler:HandlerMethod类型,存储了拦截的单元方法的method对象。
​Exception:存储异常信息的对象,如果没有异常信息则默认为null。

@Override
public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
  System.out.println("MyInterceptor.afterCompletion");
}

拦截器配置

拦截器类创建好后需要在springmvc.xml文件中配置拦截器的bean以及其拦截范围。

 <!--
    配置拦截器
        作用:
            让声明的拦截器类生效完成单元方法请求的拦截
        使用:
            在springmvc.xml文件中使用<mvc:interceptors>标签声明拦截的配置信息
            在<mvc:interceptors>标签下使用子标签完成拦截器的配置
                全局拦截
                     在<mvc:interceptors>直接声明bean标签配置拦截器的bean,拦截所有的单元方法请求。
                局部拦截
                    在<mvc:interceptors>标签下使用子标签<mvc:interceptor>来声明局部拦截
                    在<mvc:interceptor>标签下使用子标签配置拦截返回以及拦截器的bean
                        <mvc:mapping path="/demo"/> 要拦截的范围,可以声明多个
                        <bean id="mm" class="com.bjsxt.inter.MyInter"></bean> 拦截器
-->
<!--配置拦截器-->
<mvc:interceptors>
    <!--配置拦截器的bean对象,拦截所有的单元方法-->
        <bean class="com.bjsxt.interceptor.MyInterceptor2"></bean>
    <!--配置具体的拦截器的bean极其拦截范围,可以配置多个-->
    <mvc:interceptor>
        <mvc:mapping path="/myController/demo"/><!--配置拦截的单元方法的访问路径,第一个/表示项目根目录,可以多个-->
        <mvc:mapping path="/myController/kk/*"/><!--支持*通配符表示任意个数的任意字符,**表示路径及子路径-->
        <bean class="com.bjsxt.interceptor.MyInterceptor"></bean><!--配置拦截器的bean对象,只在当前mvc:interceptor内有效-->
    </mvc:interceptor>
</mvc:interceptors>

注意:

​ 拦截器中是存在多重拦截的,当我们访问一个单元方法时,如果该单元方法符合多个拦截器的拦截范围则符合的拦截器都会执行,执行顺序按照配置文件中配置的拦截器自上而下执行:preHandle配置上—>prehandle配置下—>单元方法—>postHandle配置下—>postHandle配置上—>afterCompletion配置上—>afterCompletion配置下

配置多个拦截器,拦截器栈

全局配置

    <mvc:interceptors>
        <bean class="com.bjsxt.inteceptor.MyInteceptor"></bean>
        <bean class="com.bjsxt.inteceptor.MyInteceptor2"></bean>
    </mvc:interceptors>

局部配置。
因为在<mvc:interceptor>中只能有一个<bean>,所有如果希望使用局部方式配置拦截器栈,需要配置多个<mvc:interceptor>标签

    <mvc:interceptors>
        <mvc:interceptor>
            <mvc:mapping path="/show2"/>
            <bean class="com.bjsxt.inteceptor.MyInteceptor2"></bean>
        </mvc:interceptor>
        <mvc:interceptor>
            <mvc:mapping path="/show2"/>
            <bean class="com.bjsxt.inteceptor.MyInteceptor"></bean>
        </mvc:interceptor>
    </mvc:interceptors>

拦截器和过滤器的区别(面试题)

  1. 来源不同
    拦截器是SpringMVC中的技术,过滤器是Java EE中的技术
  2. 生效位置不同
    拦截器是进入DispatcherServlet后才能执行,过滤器是进入到DispatcherServlet容器前就可以触发。
  3. 目标不同
    拦截器拦截的目标是HandlerMethod(控制单元,控制器方法),过滤器可以过滤所有的URL。
  4. 运行机制不同
    拦截器是在HandlerMethod之前前后和视图处理完成后执行,分为三部分。过滤器只能在目标资源前后执行。
  5. 接口中方法类型不同
    拦截器中的方法都是default方法,可以重写也可以不重写。过滤器中的方法都是abstract方法,如果当前类不是抽象类,必须重写。
  6. 上下文不同
    拦截器可以获取到Spring容器中内容,但是Filter因为是被Tomcat管理,所以无法获取Spring容器内容。

跨域

当前项目的协议、ip、端口和访问的URL的协议、IP、端口中有一个不同,这种访问就叫跨域。

跨域只发生在Ajax请求中。
Ajax研发之初为了保证安全性,设置默认情况下不允许跨域访问。
同源策略:当使用ajax请求时,不允许跨域访问。只能访问当前域的资源。

解决跨域的常见的解决方案:
jsonp : Spring 4的支持。
设置响应头:Spring 5支持。只需要在允许被跨域访问的方法上面添加@CrossOrigin注解即可。
原因: 只要在控制单元方法上添加了@CrossOrigin注解后,会在响应头中添加Access-Control-Allow-Origin:*
Access-Control-Allow-Origin是HTTP协议中允许哪些IP的项目跨域访问,星号表示所有IP
等效于在响应头中直接添加允许跨域参数:

    @RequestMapping("/demo")
    @ResponseBody
    public String demo(HttpServletResponse response){
        response.setHeader("Access-Control-Allow-Origin","*");
        return "hello";
    }

为了简单直接使用@CrossOrigin注解

    @RequestMapping("/demo")
    @ResponseBody
    @CrossOrigin
    public String demo(){
        return "hello";
    }

Spring MVC异常处理

1.局部配置
只有当前这个控制器类的控制单元出现异常时才能执行
每个控制器类中可以有多个处理异常的方法。每个方法上面只需要有@ExceptionHandler

@Controller
public class DemoController {
    @RequestMapping("/demo")
    public String demo2(){
        int a = 1/0;
        return "demo2.html";
    }
    @ExceptionHandler(value = ArithmeticException.class)
    public String myexception(){
        System.out.println("Demo-1");
        return "forward:/exception.jsp";
    }
    @ExceptionHandler(value = Exception.class)
    public String myexception2(){
        System.out.println("Demo-2");
        return "forward:/exception2.jsp";
    }
}

2.全局配置
因为@ControllerAdvice已经继承了@Component注解,所以类上只添加这个注解就可以了。
不需要在添加@Controller注解了。
如果配置了局部异常处理器和全局异常处理器,优先匹配局部异常处理器。

@ControllerAdvice
public class MyExceptionController {
    @ExceptionHandler(value = ArithmeticException.class)
    public String myexception(){
        System.out.println("ArithmeticException");
        return "forward:/fail.html";
    }
    @ExceptionHandler(value = Exception.class)
    public String myexception2(){
        System.out.println("Exception");
        return "forward:/fail.html";
    }
}
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值