blog
原创

Spring学习笔记——SpringMVC

SpringMVC

SpringMVC是一种基于Java的实现MVC设计模型的请求驱动类型的轻量级Web框架

SpringMVC已经成为目前最主流的MVC框架之一,伴随着Spring3.0的发布,已经完全超越Struts2,成为最优秀的MVC框架。它通过一套注解让一个简单的Java类称为处理请求的控制器,而无需实现任何接口

Spring集成Web环境

应用上下文是通过new ClasspathXmlApplicationContext()方式获取的,但是每当从容器中获得Bean时都需要加载一次配置文件,弊端是配置文件和应用上下文会被多次加载和创建

在Web项目中,可以通过ServletContextListener监听器来监听Web应用的启动,当Web应用启动时,加载配置文件,并创建应用上下文对象,并将其保存到servletContext域中,这样就可以在任意位置从域中获取应用上下文对象

监听器ServletContextListener

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

import javax.servlet.ServletContext;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;

public class ContextLoaderListener implements ServletContextListener {

    @Override
    public void contextInitialized(ServletContextEvent servletContextEvent) {
        ApplicationContext app = new ClassPathXmlApplicationContext("applicationContext.xml");
        // 将应用上下文存储到servletContext域中
        ServletContext servletContext = servletContextEvent.getServletContext();
        servletContext.setAttribute("app",app);
    }

    @Override
    public void contextDestroyed(ServletContextEvent servletContextEvent) {

    }
}

使用应用上下文对象

import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;

import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

public class UserServlet extends HttpServlet {
    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        ServletContext servletContext = this.getServletContext();
        ApplicationContext app = (ApplicationContext) servletContext.getAttribute("app");
        System.out.println(app);
    }
}

在Spring中,提供了一个监听器ContextLoaderListrner就是对上述内容的封装,该监听器内部会加载Spring配置文件,创建应用上下文对象,并存储到ServletContext域中,并提供了一个WebApplicationContextUtils类来获取应用上下文对象

首先,需要在web.xml中配置ContextLoaderListener监听器(需要导入spring-web坐标)

<listener>
    <listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>

使用WebApplicationContextUtils获取ApplicationContext

protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
    ServletContext servletContext = this.getServletContext();
    ApplicationContext app = WebApplicationContextUtils.getWebApplicationContext(servletContext);
    System.out.println(app);
}

SpringMVC开发步骤

  1. 导入SpringMVC坐标

    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>5.2.9.RELEASE</version>
    </dependency>
    
  2. web.xml中配置SpringMVC核心控制器DispathcerServlet

    <!-- 配置Servler -->
    <servlet>
        <servlet-name>DispatcherServlet</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring-mvc.xml</param-value>
        </init-param>
        <load-on-startup>1</load-on-startup>
    </servlet>
    <!-- 配置Servlet地址映射 -->
    <servlet-mapping>
        <servlet-name>DispatcherServlet</servlet-name>
        <!-- 拦截所有请求 -->
        <url-pattern>/</url-pattern>
    </servlet-mapping>
    
  3. 创建Controller类和视图页面

  4. 将Controller使用注解配置到Spring容器中

    @Controller // 将Controller导入Spring容器
    public class UserController {
        @RequestMapping("/quick")
        public String save(){
            System.out.println("Controller save running....");
            return "success.jsp";
        }
    }
    
  5. 配置SpringMVC核心文件spring-mvc.xml

    <?xml version="1.0" encoding="UTF-8"?>
    <beans xmlns="http://www.springframework.org/schema/beans"
           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/context
                               http://www.springframework.org/schema/context/spring-context.xsd">
    
        <!-- 配置注解扫描 -->
        <context:component-scan base-package="cn.hhxy.controller"/>
    
    </beans>
    

SpringMVC的执行流程

  1. 客户发送请求到前端控制器DispatcherServlet
  2. 前端控制器DispatcherServlet收到请求后调用处理器映射器HandlerMapping
  3. 处理器映射器通过xml或注解寻找到具体的处理器,生成处理器对象和处理器拦截器,并一同返回给前端控制器DispatcherServlet
  4. 前端控制器调用处理器适配器HandlerAdapter
  5. 处理器适配器HandlerAdapter经过适配调用具体的后端控制器Controller
  6. 后端控制器Controller执行并返回ModelAndView
  7. 处理器适配器HandlerAdapter将后端控制器Controller返回的ModelAndView返回给前端控制器DispatcherServlet
  8. 前端控制器DispatcherServletModelAndView传给视图解析器ViewReslover
  9. 视图解析器ViewReslover解析后返回具体的视图View
  10. 前端控制器DispatcherServlet根据View渲染视图,响应用户

SpringMVC注解解析

@RequestMapping

用于建立请求URL和处理请求方法之间的对应关系

位置

在类上使用,为请求URL的第一级访问目录。若不写,则相当于应用的根目录

在方法上使用,为请求URL的第二级访问目录,与类上的@RequestMapping标注的一级目录一同组成访问虚拟路径

@Controller
@RequestMapping("/user")
public class UserController {
    
	// 请求地址 http://localhost:8080/user/quick
    @RequestMapping("/quick")
    public String save(){
        System.out.println("Controller save running....");
        return "/success.jsp";
    }

}
属性

① value:用于指定请求URL,和path的作用相同

② method:用于指定请求的方式

③ params:用于指定限制请求参数的条件,支持简单的表达式,要求请求参数的keyvalue必须和配置的一模一样

// 必须以GET方式请求,且请求参数中必须携带 username
@RequestMapping(value = "/quick",method = RequestMethod.GET,params = {"username"})

SpringMVC配置解析

组件扫描

除了使用<context:component-scan>来直接配置组件扫描之外,还可以通过两个子标签进一步配置

<context:component-scan base-package="cn.hhxy.web">
    <context:include-filter type="annotation" expression="org.springframework.stereotype.Controller"/>
    <!--
    <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Service"/>
	-->
</context:component-scan>

其中两个子标签共有的type属性为组件的类型,expression为组件的全类名

<context:include-filter>标签表示只扫描base-package中对应的组件,以上例子中则是只扫描cn.hhxy.web包下的@Controller注解

<context:exclude-filter>标签表示扫描除了对应组件之外的其他组件,以上例子中则是扫描cn.hhxy.web包下除了@Service注解的其他组件

SpringMVC的数据响应

SpringMVC的数据响应方式

页面跳转
直接返回字符串

将返回的字符串与视图解析器的前后缀拼接后跳转

配置内部视图解析器InternalResourceViewResolver

<bean id="viewResolver" class="org.springframework.web.servlet.view.InternalResourceViewResolver">
    <!-- 前缀 -->
    <property name="prefix" value="/jsp/"/>
    <!-- 后缀 -->
    <property name="suffix" value=".jsp"/>
</bean>
@RequestMapping(value = "/quick")
public String save(){
    System.out.println("Controller save running....");
    return "success";
}

通过将返回字符串与前后缀拼接,得到的转发资源地址为/jsp/success.jsp

通过ModelAndView对象返回
@RequestMapping(value = "/mv")
    public ModelAndView MAV(){
        // 创建 ModelAndView
        ModelAndView modelAndView = new ModelAndView();
        // 设置视图
        modelAndView.setViewName("success");
        // 设置模型数据
        modelAndView.addObject("name","Melonico");
        return modelAndView;
    }

当在ModelAndView中设置了数据后,可以在JSP中使用EL表达式来使用数据

<h1>Success! ${name}</h1>

除了在方法体内创建ModelAndView对象,还可以将ModelAndView添加到方法参数列表中

@RequestMapping(value = "/mv2")
public ModelAndView MAV2(ModelAndView modelAndView){
    modelAndView.setViewName("success");
    modelAndView.addObject("name","melonico");
    return modelAndView;
}

还可以将模型Model和视图View拆开

@RequestMapping(value = "/mv3")
public String MAV3(Model model){
    model.addAttribute("name","Model_Melonico");
    return "success";
}

通过返回字符串,视图解析器解析对应的视图View,同时在参数列表中加入模型Model,并通过这个Model来进行数据的设置


如果希望向域中存放数据,可以在方法参数列表中添加HttpServletRequest

@RequestMapping(value = "/mv4")
public String MAV4(HttpServletRequest request){
    request.setAttribute("name","request_melonico");
    return "success";
}

以上所有在参数列表内添加的对象,SpringMVC框架会自动注入对应的对象

回写数据
直接返回字符串

~~最简单的方式,可以直接通过respones.gerWriter().print()方法来直接回写字符串~~

@RequestMapping(value = "/res1")
public void res1(HttpServletResponse response){
    response.getWriter().print("Melonico");
}

可以将需要回写的字符串直接返回,但是此时需要通过@ResponseBody注解告知SpringMVC,该方法返回的字符串不是逻辑视图名,而是直接在Http响应体中返回

@RequestMapping(value = "/res2")
@ResponseBody // 告知SpringMVC该方法不进行视图跳转,直接进行数据响应
public String res2() throws IOException {
    return "Melonico";
}
~~返回JSON字符串~~

可以通过以上直接返回字符串的方式手动返回JSON字符串,如

return "{'name':'A','age':'20'}";

更高级的方法可以通过Jackson将对象转换为Json字符串,然后返回

首先导入Jackson的依赖

<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-databind</artifactId>
    <version>2.9.0</version>
</dependency>
<dependency>
    <groupId>com.fasterxml.jackson.core</groupId>
    <artifactId>jackson-annotations</artifactId>
    <version>2.9.0</version>
</dependency>
</dependencies>

在方法中创建对象,并使用ObjectMapper对象将其转为Json字符串

@RequestMapping(value = "/res3")
@ResponseBody
public String res3() throws IOException {
    User user = new User();
    user.setUsername("Melonico");
    user.setPassword("123456");
    // 使用 Jackson 将 User 对象转换为 JSON 字符串
    ObjectMapper objectMapper = new ObjectMapper();
    String s = objectMapper.writeValueAsString(user);
    return s;
}
返回对象、集合

在处理器适配器HandlerAdapter的实现类RequestMappingHandlerAdapter中,存在一个消息转换器MassageConverters,可以配置这个消息转换器来让SpringMVC帮我们将对象或集合转为JSON字符串并返回

<!-- 配置处理器映射器 -->
<bean id="handlerAdapter" class="org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerAdapter">
    <property name="messageConverters">
        <list>
            <bean class="org.springframework.http.converter.json.MappingJackson2HttpMessageConverter"/>
        </list>
    </property>
</bean>

然后再方法中即可直接将对象返回,SpringMVC会将其转为Json字符串

@RequestMapping(value = "/res4")
@ResponseBody
public User res4(){
    User user = new User("Melonico","123456");
    return user;
}

以上的代码配置起来比较麻烦,可以通过使用mvc的注解驱动代替以上配置

<!-- 命名空间 -->
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/mvc
           http://www.springframework.org/schema/mvc/spring-mvc.xsd"

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

==处理器映射器,处理器适配器,视图解析器==被称为SpringMVC的三大组件,在使用<mvc:annotation-driven>注解时会自动加载处理器映射器RequestMappingHandlerMapping和处理器适配器RequestMappingHandlerAdapter,可以在SpringMVC的配置文件中代替注解处理器和适配器的配置,同时,使用<mvc:annotation-driven>后默认底层就会继承Jackson进行对对象或集合的Json字符串格式的转变

SpringMVC数据响应总结

SpringMVC的数据响应方式有

  • 页面跳转
  • 回写数据

在页面跳转中,可以直接返回字符串,或者返回一个ModelAndView

在回写数据中,可以直接回写字符串,需要使用@ResponseBody告知SpringMVC对应方法的返回值不进行页面跳转;

返回对象或集合,可以通过<mvc:annotation-driven>来使SpringMVC进行对象/集合与Json字符串之间的转变

SpringMVC获得请求数据

客户端的请求参数一般会以键值对方式拼接在请求url之后,格式为name=nameValue&password=passwordValue

服务器端要获得请求参数,有时需要对数据进行封装,SpringMVC可以接收的参数类型有

基本类型

Controller中的业务方法的参数名要与请求参数的name一致,参数值会自动映射匹配

http://localhost:8080/SpringMVC/Controller?username=Melonico&sex=M
@RequestMapping(value = "/req1")
@ResponseBody // 不进行页面跳转
public void req1(String username,String sex){
    System.out.println(username);
    System.out.println(sex);
}

POJO(Plain Ordinary Java Object 简单的Java对象)

Controller中的业务方法的POJO参数的属性名与请求参数的name一致,参数值会自动映射匹配

http://localhost:8080/SpringMVC/Controller?username=Melonico&password=123456
@RequestMapping(value = "/req2")
@ResponseBody // 不进行页面跳转
public void req2(User user){
    System.out.println(user);
}

User类中应有与参数name相同的属性名以及对应的GetterSetter

public class User {
    private String username;
    private String password;
  
    Getter/Setter
}

数组

Controller中的业务方法数组名称与请求参数的name一致,参数值会自动映射匹配

http://localhost:8080/SpringMVC/Controller?hobbies=swim&hobbies=sing
@RequestMapping(value = "/req3")
@ResponseBody // 不进行页面跳转
public void req3(String[] hobbies){
    System.out.println(Arrays.asList(hobbies));
}

集合

获取集合参数时,需要将集合参数包装到一个POJO中

import java.util.List;

public class UserList {
    private List<User> userList;

    public List<User> getUserList() {
        return userList;
    }

    public void setUserList(List<User> userList) {
        this.userList = userList;
    }

    @Override
    public String toString() {
        return "UserList{" +
            "userList=" + userList +
            '}';
    }
}
@RequestMapping(value = "/req4")
@ResponseBody // 不进行页面跳转
public void req4(UserList userList){
    System.out.println(userList);
}

当使用Ajax提交数据时,可以指定contentType为Json格式,这样在方法参数位置使用@RequestBody注解就可以直接接受集合数据,无需再使用POJO包装

@RequestMapping(value = "/req5")
@ResponseBody // 不进行页面跳转
public void req5(@RequestBody List<User> userList){
    System.out.println(userList);
}
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Title</title>
    <script src="${pageContext.request.contextPath}/js/jquery-3.5.1.js"></script>
    <script>
        var userList = new Array();
        userList.push({username:"AAA",age:20});
        userList.push({username:"BBB",age:22});
        $.ajax({
            type:"POST",
            url:"${pageContext.request.contextPath}/jsp/req5",
            data:JSON.stringify(userList),
            contentType:"application/json;charset=utf-8"
        })
    </script>
</head>
<body>
</body>
</html>

在上述代码执行时,会出现错误,查询网页控制台后可以发现错误原因是找不到$的定义,而造成这个错误的原因是因为找不对对应的jQuery文件,这时,我们需要开启SpringMVC的静态资源访问

通过使用<mvc:resources>标签开启静态资源访问

<mvc:resources mapping="/js/**" location="/js/">

属性mapping是请求映射地址,location表示资源具体存在的目录

也可以通过<mvc:default-servlet-handler>实现,当SpringMVC在@RequsetMapping注解中找不到对应地址时,会交给原始容器(Tomcat)寻找资源

请求数据乱码解决

当POST请求参数中存在中文数据时,会出现乱码问题

(Tomcat服务器会解决GET请求方式的乱码问题)

除了通过request.setCharacterEncoding()方法解决,也可以设置过滤器来进行编码过滤

<!-- 配置全局过滤器 -->
<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>    
</filter>
<filter-mapping>
    <filter-name>characterEncodingFilter</filter-name>
    <url-pattern>/*</url-pattern>
</filter-mapping>

参数绑定注解 @RequestParam

当页面传送的请求参数名与Controller内业务方法中的参数名不同时,如

http://localhost:8080/SpringMVC/Controller?name=123
@RequestMapping(value = "/Controller")
@ResponseBody
public void Controller(String username){
    System.out.println(username);
}

这时控制台的输出为null,这种情况下需要使用@RequsetParam注解来绑定参数

@RequestMapping(value = "/Controller")
@ResponseBody
public void Controller(@RequestParam("name") String username){
    System.out.println(username);
}

除了绑定参数之外,@RequestParam的属性和功能有

  • value:设置请求参数名称
  • requierd:指定此参数是否必须包括,默认为ture,即请求时不包含此参数会报错
  • defaultValue:当没有指定该请求参数时,使用默认值给请求参数赋值

获取Restful风格参数

Restful是一种软件架构/设计风格,它提供了一组设计原则和约束条件,主要用于客户端和服务器交互类的软件

Restful风格的请求是使用url + 请求方式表示一次请求的目的,HTTP协议中四个操作方式为

  • GET:获取资源
  • POST:新建资源
  • PUT:更新资源
  • DELETE:删除资源

如:/Controller/Param GET:获取 id 为 Param 的 Controller

其中,Param是需要获取的请求参数,在SpringMVC中,可以使用占位符 {} 进行参数绑定,并在业务方法中使用@PathVariable进行占位符的匹配和获取

http://localhost:8080/SpringMVC/Restful/Melonico
@RequestMapping("/Restful/{username}")
@ResponseBody
public void Restful(@PathVariable(value = "username",required = true) String username){
    System.out.println(username);
}

自定义类型转换器

步骤

① 定义类型转换器类——实现Converter接口

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

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

// Converter<转换前对象,转换后对象>
public class DateConverter implements Converter<String, Date> {
    @Override
    public Date convert(String s) {
        // 将日期字符串转换为Date对象
        SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd");
        Date parse = null;
        try {
            parse = simpleDateFormat.parse(s);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return parse;
    }
}

② 在SpringMVC配置文件中声明转换器

<!-- 声明日期转换器 -->
<bean class="org.springframework.context.support.ConversionServiceFactoryBean" id="serviceFactoryBean">
    <property name="converters">
        <list>
            <bean class="cn.hhxy.converter.DateConverter" id="dateConverter"></bean>
        </list>
    </property>
</bean>

③ 在<mvc:annotation-driven>中引用转换器

<mvc:annotation-driven conversion-service="serviceFactoryBean"/>

获取Servlet的原生API

若希望在Controller的业务方法中使用Servlet的原生API,如request,response,在方法参数列表中直接声明即可

同时可以通过request获取到session,进而获取到ServletContext

@RequestMapping("/servlet")
@ResponseBody
public void Servlet_api(HttpServletRequest request,HttpServletResponse response){
    System.out.println(request);
    System.out.println(response);
    // 获取session
    HttpSession session = request.getSession();
    System.out.println(session);
    // 获取ServletContext
    ServletContext servletContext = session.getServletContext();
    System.out.println(servletContext);
}

获取请求头

@RequestHeader

@RequestHeader可以获得请求头信息,相当于

request.getHeader(name);

其中的属性有

  • value:请求头名称
  • required:是否必须携带该请求头
@RequestMapping("/header")
@ResponseBody
public void Header(@RequestHeader(value = "cookie",required = true) String cookies){
    System.out.println(cookies);
}
@CookieValue

@CookieValue可以直接获取指定Cookie的值

其中的属性有

  • value:Cookie名称
  • required:是否必须携带该Cookie
@RequestMapping("/header")
@ResponseBody
public void Header(@CookieValue(value = "JSESSIONID",required = true) String cookies){
    System.out.println(cookies);
}

文件上传

文件上传的三个要点

  • 表单项的typefile
  • 表单的提交方式methodPOST
  • 表单的entypemultipart/form-data
<form action="${pageContext.request.contextPath}/jsp/file" method="post" enctype="multipart/form-data">
    <input type="text" name="name">
    <input type="file" name="file">
    <input type="submit" value="submit">
</form>

当form表单type修改为multipart/form-data时,request.getParameter()等方法会失效,请求体正文为

-----------------------------7e46dca0510
Content-Disposition: form-data; name="name"

123
-----------------------------7e46dca0510
Content-Disposition: form-data; name="file"; filename="C:\Users\Melonico\Desktop\a.txt"
Content-Type: text/plain


-----------------------------7e46dca0510--

单文件上传步骤

① 导入fileuploadio坐标

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

② 配置文件上传解析器CommonsMultipartResolver

<!-- 配置文件上传解析器 -->
<bean class="org.springframework.web.multipart.commons.CommonsMultipartResolver" id="multipartResolver">
    <property name="defaultEncoding" value="utf-8"/>
    <property name="maxUploadSize" value="548000"/>
</bean>

③ 编写上传代码

业务方法参数列表中String name对应上传名称,MultipartFile file对应上传文件,两者的参数名必须同表单中的name相同

@RequestMapping("/file")
@ResponseBody
public void file(String name, MultipartFile file) throws IOException {
    System.out.println(name);
}
文件存储步骤

若希望将上传的文件保存到其他位置,可以使用MultipartFile中的transferTo()方法

public void transferTo(File file) throws IOException, IllegalStateException

@RequestMapping("/file")
@ResponseBody
public void file(String name, MultipartFile file) throws IOException {
    System.out.println(name);
    // 获得上传文件的名称
    String filename = file.getOriginalFilename();
    file.transferTo(new File("C:\\file\\" + filename));
}
多文件上传

可以添加多个表单项来上传多个文件,同时在业务方法中可以使用MultipartFile数组来获取文件

@RequestMapping("/file")
@ResponseBody
public void file(String name, MultipartFile[] file) throws IOException {
    System.out.println(name);
    for (MultipartFile uploadFile: file) {
        System.out.println(uploadFile);
    }
}

SpringMVC拦截器

SpringMVC的拦截器(interceptor)类似于Servlet中的Filter,用来对处理器进行预处理后处理,拦截器也是AOP思想的具体实现

将拦截器按一定的顺序连接成一条链,这条链称为拦截器链(Interceptor Chain),在访问被拦截的方法或字段时,拦截器链中的拦截器会按照之前定义的顺序被调用

拦截器 Interceptor 与 过滤器 Filter 的区别

过滤器 Filter 拦截器 Interceptor
使用范围 在任何Java Web工程中都可以使用 只有使用SpringMVC框架的工程才可以使用
拦截范围 根据url-pattern中配置的路径拦截访问资源 只会拦截访问的控制器方法,不会拦截访问的JSP,HTML,JS等

使用步骤

① 创建拦截器类 实现HandlerInterceptor接口

import org.omg.PortableInterceptor.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 MyInterceptor implements HandlerInterceptor {

    // 在目标方法执行之前执行
    @Override
    public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
        System.out.println("preHandle....");
        return true;
    }

    // 在目标方法执行之后 视图返回之前执行
    @Override
    public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
        System.out.println("postHandle....");
    }

    // 在所有流程执行完毕后执行
    @Override
    public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception {
        System.out.println("afterCompletion....");
    }

}

② 配置拦截器

<!-- 配置拦截器 -->
<mvc:interceptors>
    <mvc:interceptor>
        <!-- 对哪些资源执行拦截 -->
        <mvc:mapping path="/**"/>
        <bean class="cn.hhxy.interceptor.MyInterceptor"/>
    </mvc:interceptor>
</mvc:interceptors>

③ 编写代码测试

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

@Controller
public class TargetController {
    @RequestMapping("/target")
    public ModelAndView show(){
        System.out.println("目标方法执行....");
        ModelAndView modelAndView = new ModelAndView();
        modelAndView.addObject("name","Melonico");
        modelAndView.setViewName("success");
        return modelAndView;
    }
}

控制台输出

preHandle....             // preHandle方法执行
target....                // 目标方法执行
postHandle....            // postHandler方法执行
View....                  // 视图显示
afterCompletion....       // afterCompletion方法执行

方法详解

在自定义拦截器继承HandlerInterceptor后,有三个方法需要重写

preHandle

public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception

该方法在目标方法执行之前执行,当返回值为true时,表示放行,false表示不放行

同时可以通过其参数中的requestresponse获取一些请求内容

例如,通过request进行请求参数判断

@Override
public boolean preHandle(HttpServletRequest request, HttpServletResponse response, Object handler) throws Exception {
    System.out.println("preHandle....");
    String name = request.getParameter("name");
    if (name != null){
        return true;
    } else {
        // 错误转发页面
        request.getRequestDispatcher("/error.jsp").forward(request,response);
        return false;
    }
}
postHandle

public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception

该方法在目标方法执行之后,视图返回之前执行

其参数中的modelAndView可以获取或更改模型和视图

@Override
public void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {
    modelAndView.addObject("name","melonico252");
    System.out.println("postHandle....");
}

由于postHandle方法在目标方法之后,视图返回之前执行,所以其中对modelAndView的修改会覆盖目标方法之前的内容

afterCompletion

public void afterCompletion(HttpServletRequest request, HttpServletResponse response, Object handler, Exception ex) throws Exception

该方法在整个流程之后执行

可以进行一些收尾工作

拦截器链

当存在多个拦截器时,根据拦截器在配置文件中的配置顺序,执行顺序为

如配置顺序为:拦截器1 拦截器2

执行顺序为

拦截器1 perHandle...
拦截器2 perHandle...
目标方法
拦截器2 postHandle...
拦截器1 postHandle...
拦截器2 afterCompletion...
拦截器1 afterCompletion...

SpringMVC拦截器总结

使用步骤

  1. 创建拦截器实现HandlerInterceptor接口
  2. 在SpringMVC配置文件中配置拦截器 <mvc:interceptors>
  3. 测试拦截效果

方法

方法名 说明
preHandle() 方法将在请求处理之前调用,该方法的返回值为布尔类型,当返回为false时,表示请求结束,后续的InterceptorController都不会继续执行;当返回值为true时,会继续调用下一个InterceptorperHandle方法(若存在)或Controller中的目标方法
postHandle() 该方法在当前请求进行处理之后被调用,前提是preHandle方法的返回值为true,且会在DispatcherServlet进行视图返回渲染之前被调用,可以在该方法中对Controller处理之后的ModelAndView对象进行进一步操作
afterCompletion() 该方法在整个请求结束之后,即DispatcherServlet渲染对应视图之后执行,前提是perHandle的返回值为true时才能被调用

SpringMVC异常处理

系统中包括两类异常,预期异常运行期异常预期异常通过捕获获取异常信息,运行期异常通过规范代码开发,测试等手段减少运行期异常的发生

系统的DaoServiceController出现异常时通过throw向上抛出异常,最后由SpringMVC前端控制器交由异常处理器进行异常处理

在这里插入图片描述

异常处理的方式

简单异常处理器SimpleMappingExceptionResolver

由SpringMVC提供的,在使用时可以根据具体情况进行相应异常与视图的映射配置

<!-- 配置异常处理器 -->
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
    <!-- 默认异常视图 -->
    <property name="defaultErrorView" value="error"/>
    <!-- 具体异常试图 -->
    <property name="exceptionMappings">
        <map>
            <entry key="java.lang.ClassCastException" value="error1"/>
        </map>
    </property>
</bean>

SimpleMappingExceptionResolver中有一些属性可以注入

defaultErrorView用来配置默认的错误视图,value为跳转需要到的页面

exceptionMappings用来配置具体的错误视图,其中需要注入一个Map,用来配置多个异常视图,其中key是异常类型,value为该异常要跳转到的页面

自定义异常处理器

通过实现HandlerExceptionResolver接口,可以自定义异常处理器

public class MyExceptionResolver implements HandlerExceptionResolver {
    /**
     * @param e 异常对象
     * @return 跳转到错误视图信息
     */
    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {

        ModelAndView modelAndView = new ModelAndView();
        if (e instanceof ClassCastException){
            modelAndView.addObject("error","ClassCastException");
        } else if (e instanceof NullPointerException) {
            modelAndView.addObject("error","NullPointerException");
        }
        modelAndView.setViewName("error");
        return null;
    }
}

创建自定义异常处理器后,需要在SpringMVC配置文件中配置

<!-- 配置自定义异常处理器 -->
<bean class="cn.hhxy.resolver.MyExceptionResolver"/>
Spring
SpringMVC
  • 作者:Melonico
  • 发表时间:2021-03-15 17:57
  • 更新时间:2021-03-15 17:57

评论

暂无评论,快来发表第一个评论吧!
留言
TOP