WebApplicationContext 中特殊的 bean 类型(一)--- 请求/异常处理

栏目: Java · 发布时间: 6年前

内容简介:其实 Spring 的基本思想就是“万物都是 bean”,那么为了满足 spring 工程的需要,spring 中有一些默认的 bean 选项,它们用于处理请求,渲染视图等。比如上一篇文章就用过的 viewResolver 的配置。当然,servlet 也允许你配置使用不同特定的 bean,但是,如果你没有配置,spring 将会按照默认的 bean 进行配置。本章将会详细说明文档中列出的 bean 的配置以及具体的使用例子,所讲述的 bean 类型包括:本章节将基于文档实践(一)的代码进行后续的操作,因

其实 Spring 的基本思想就是“万物都是 bean”,那么为了满足 spring 工程的需要,spring 中有一些默认的 bean 选项,它们用于处理请求,渲染视图等。比如上一篇文章就用过的 viewResolver 的配置。当然,servlet 也允许你配置使用不同特定的 bean,但是,如果你没有配置,spring 将会按照默认的 bean 进行配置。本章将会详细说明文档中列出的 bean 的配置以及具体的使用例子,所讲述的 bean 类型包括:

  • HandlerMapping 和 HandlerAdapter
  • HandlerExceptionResolver
  • LocaleResolver & LocaleContextResolver
  • ThemeResolver
  • MultipartResolver

HandlerAdapter 和 HandlerMapping 解析

前期准备

本章节将基于文档实践(一)的代码进行后续的操作,因此我们使用了单个 ContextConfig 来配置工程 Context 对象,也就是 root-context.xml 文件。另一方面,为了实现 HandlerMapping 在 xml 配置的功能,我们关掉了

<mvc:annotation-driven/>
复制代码

的功能,使得 @Controller 注解下的类不再会被自动配置并且做 url 的映射,现在再去试一下 localhost:8080/hello.do 的话,已经是 404 Not Found 了。之后再进行后续的实践过程。

这里 HandlerMapping 和 HandlerAdapter 一起讲是因为,HandlerMapping 需要 HandlerAdapter 的支持才能正常运行。HandlerMapping 用于将请求的 url 映射到对应的 controller 上面,如果没有进行配置的话,@Controller 注解即为 HandlerMapping,上一篇的 ExampleController 即有着和上述相似的功能。值得注意的是,Spring MVC 4.0 之后主推 Annotation Driven,也就是注解驱动模式下的工程,因此,对应的 adapter 已经标记为 deprecated,不推荐使用,这里只做帮助理解使用。

HandlerAdapter

由于工程中的 Controller 都是用注解配置的,因此,在 DispatcherServlet 根据 bean 的配置信息(root-context.xml,我们用 Context 对象来配置 bean 的信息)知道了自己所需要调用的 controller 之后,他需要根据注解来提取其他的所需要的信息。这时候就需要 HandlerAdapter 来做这些解析的事情。

然而,目前的 Spring MVC 的配置都基于注解,因此,HandlerAdapter 也退居幕后,@Controller 注解包含了其中逻辑,在 Annotation-driven 被我们关掉的场景下,也只要做好 HandlerMapping,就可以成功地映射你想要的 url

HandlerMapping

HandlerMapping 本质还是一个 Bean,他在 Spring MVC 装配完成之后,执行着将 URL 的请求转发到对应的 Controller 执行后续视图,数据等返回的工作。因此,在配置 HandlerMapping Bean 的时候,需要配置 property 的 mappings 字段,并且在 字段下面指定对应的请求映射。具体代码如下:

<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
    <property name="mappings">
     	<props>
         	<prop key="/handler-mapping.do">handlerMappingController</prop>
        </props>
	</property>
</bean>
复制代码

HandlerAdapter 和 HandlerMapping 的测试

为了同步一下,目前 root-context.xml (Spring Context 对象配置文件) 的配置加入了 HandlerMapping 的配置:

<?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" xmlns:p="http://www.springframework.org/schema/p"
       xmlns:mvc="http://www.springframework.org/schema/mvc" xmlns:tx="http://www.springframework.org/schema/c"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
            http://www.springframework.org/schema/beans/spring-beans-3.2.xsd
            http://www.springframework.org/schema/context
            http://www.springframework.org/schema/context/spring-context-3.2.xsd
            http://www.springframework.org/schema/mvc
            http://www.springframework.org/schema/mvc/spring-mvc.xsd">

    <context:component-scan base-package="com.test.myapp.example"/>

    <!--注册一个用于 handlerMapping 的 bean 用于检测 handlerMapping 效果-->
    <bean id="handlerMappingController" class="com.test.myapp.example.handlermapping.HandlerMappingController"/>

    <bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
        <property name="mappings">
            <props>
                <prop key="/handler-mapping.do">handlerMappingController</prop>
            </props>
        </property>
    </bean>
    
    <!--<bean id="simpleHandler" class="org.springframework.web.servlet.mvc.annotation.AnnotationMethodHandlerAdapter"/>-->
    <!--<mvc:annotation-driven/>-->
    
    <bean class="org.springframework.web.servlet.view.InternalResourceViewResolver"
          p:prefix="/WEB-INF/views/" p:suffix=".jsp" p:order="1">
    </bean>

</beans>
复制代码

并且新增了 HandlerMappingController.java 的配置:

package com.test.myapp.example.handlermapping;

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

/**
 * Usage: 测试 handler mapping 的有效性
 * @author: srfan
 * Date: 10/26/18 4:11 PM
 */
@Controller
public class HandlerMappingController {

    @RequestMapping(value="/handler-mapping.do", method = RequestMethod.GET)
    public String helloWorld() {
        return "handler_mapping_hello";
    }
}
复制代码

我们看到,HandlerMapping 下面配置了 /handler-mapping.do 的映射。因此,在运行工程之后,输入 localhost:8080/handler-mapping.do,就可以看到对应的 handler_mapping_hello.jsp 上的前端视图返回。

HandlerExceptionResolver 解析

HandlerExceptionResolver 是工程中用于捕获特定 Exception 的 Bean,可以提前设定自己需要捕获并且定向的 Exception,并且交由 HandlerExceptionResolver 映射到特定的视图页上面。 目前常用的方法有:

  • 实现 HandlerExceptionResolver 接口
  • 在方法上使用 @ExceptionHandler 注解

实现 HandlerExceptionResolver 接口

HandlerExceptionResolver 接口只有一个待实现的方法

ModelAndView resolveException(HttpServletRequest var1, HttpServletResponse var2, Object var3, Exception var4);
复制代码

为了工程上面比较直观简便的实现,我们只需要做最简单的实现:拿到 Exception 的具体类,并且返回对应的 error 的视图,并且记录下 Exception 的 message,显示在视图页面上面。因此我们的工序如下:

实现一个自定义的 Exception: MyCustomException

package com.test.myapp.example.handlermapping;

public class MyCustomException extends RuntimeException {
    public MyCustomException(String msg) {
        super(msg);
    }
}
复制代码

这个 Exception 类很简单,只是把 message 放进 Exception 中,无需赘述,主要是要让 ExceptionResolver 捕获该 Exception。

实现 HandlerExceptionResolver 接口:ExceptionResolver

package com.test.myapp.example.handlermapping;

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

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

@Component
public class ExceptionResolver implements HandlerExceptionResolver {
    @Override
    public ModelAndView resolveException(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) {
        if (e instanceof MyCustomException) {
            ModelAndView modelAndView = new ModelAndView("error");
            modelAndView.addObject("msg", e.getMessage());
            return modelAndView;
        }
        return null;
    }
}
复制代码

我们使用 ExceptionResolver 实现了 resolveException 方法,并且会解析 MyCustomException 并且在 ModelAndView 对象加入一个变量,并且返回名为 "error" 的 jsp 视图。我们也可以在 error.jsp 上显示这个 msg 字段的信息。

HandlerMappingController 添加两个会抛出 Exception 的接口

为了对照效果,我们实现两个接口,一个会抛出 MyCustomException,另一个则会抛出普通的 IllegalArgumentException,而我们需要捕获的则是 MyCustomException。

package com.test.myapp.example.handlermapping;

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

@Controller
public class HandlerMappingController {

    @RequestMapping(value="/handler-mapping.do", method = RequestMethod.GET)
    public String helloWorld() {
        return "handler_mapping_hello";
    }

    @RequestMapping(value="/custom-exception.do", method = RequestMethod.GET)
    public String throwException() {
        throw new MyCustomException("oh, you got custom exception message~!");
    }

    @RequestMapping(value="/argument-exception.do", method = RequestMethod.GET)
    public String throwArgumentException() {
        throw new IllegalArgumentException("oh, you got argument exception message~!");
    }
}
复制代码

视图文件 error.jsp 配置

视图文件 error.jsp 比较简单,只要体现 msg 字段即可:

<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
    <title>Ooooops, you meet MyCustomException</title>
</head>
<body>
    <h1>${msg}</h1>
</body>
</html>
复制代码

测试

运行工程后,在浏览器分别输入:

使用 @ExceptionHandler 注解

另一种方法是使用 @ExceptionHandler 的注解,该注解用于 method 的签名上面,我们可以实现一个 Controller 的基类并让实际接收 url 请求的 Controller 继承该基类。值得注意的是,这个方法实现的 ExceptionResolver 只会在该 Controller 内部有效,而来自其他 Controller 类的 Exception 则无法得到解析。具体代码步骤如下:

设置自定义 Exception: CustomExceptionForAnnotation

我们为这一次测试也设置了自定义的 Exception 类,实现方法也很简单,可以自定义 Exception 中的信息:

package com.test.myapp.example.exceptionresolver;

public class CustomExceptionForAnnotation extends RuntimeException {
    public CustomExceptionForAnnotation(String msg) {
        super(msg);
    }
}
复制代码

实现有 @ExceptionHandler 注解的 Controller 基类

我们的 Controller 基类需要 Resolve CustomExceptionForAnnotation,需要用 @ExceptionHandler(CustomExceptionForAnnotation.class) 进行配置,具体方法如下:

package com.test.myapp.example.exceptionresolver;

import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;

public abstract class BaseExceptionResolver {
    @ExceptionHandler({CustomExceptionForAnnotation.class})
    public ModelAndView handleCustomException(CustomExceptionForAnnotation ex) {
        ModelAndView modelAndView = new ModelAndView("error");
        modelAndView.addObject("msg", ex.getMessage());
        return modelAndView;
    }
}
复制代码

可以看到,该类中所含有的方法仅会解析 CustomExceptionForAnnotation 类,并且将其重新导向 error.jsp 视图,最后输出对应的 message 信息到前端。

实现两个 Controller 类

为了使测试结果有对照性,我们实现了两个 Controller 类,一个继承自 BaseExceptionResolver,另一个则没有。理论上说,继承了 BaseExceptionResolver 的 Controller 将可以解析上面的 Exception,而另一个则不能。具体的配置方法如下:

  • 继承了 BaseExceptionResolver 的 Controller 类 package com.test.myapp.example.exceptionresolver;

    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @Controller
    public class MyExceptionController extends BaseExceptionResolver {
    
        @RequestMapping("exception-for-annotation.do")
        public void exceptionForAnnotation() {
            throw new CustomExceptionForAnnotation("Oooops, you get CustomExceptionForAnnotation message");
        }
    }
    复制代码
  • 未继承 BaseExceptionResolver: package com.test.myapp.example.exceptionresolver;

    import org.springframework.stereotype.Controller;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    @Controller
    public class MyExceptionOutsideController {
    
        @RequestMapping("exception-for-annotation-outside.do")
        public void exceptionForAnnotation() {
            throw new CustomExceptionForAnnotation("Oooops, you get CustomExceptionForAnnotation message");
        }
    }
    复制代码

测试

我们仍然使用了 error.jsp 视图来做最后的测试工作,我们看到 BaseExceptionResolver 在捕获异常后,仍然会输出 error.jsp 的视图。我们将会请求两个具体 Controller 类的 url,观察是否会有我们想要的视图的输出:

  • localhost:8080/exception-for-annotation.do: 成功输出了我们放入 CustomExceptionForAnnotation 的信息。
  • localhost:8080/exception-for-annotation-outside.do: 页面输出了 500 的错误信息,并且带上了 Exception 中的信息,因为其没有继承 BaseExceptionResolver,因此也没有对应的 Exception 解析器了。

小结

本章主要讲述了 HandlerMapping 和 HandlerExceptionResolver 的具体实现代码,一个是处理正常的 url 请求的映射工具,而另一个则是专门处理工程在运行过程中出现 Exception 的处理方法。下一次我将继续介绍后面这几个特殊 Bean 的用法。


以上所述就是小编给大家介绍的《WebApplicationContext 中特殊的 bean 类型(一)--- 请求/异常处理》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

The Shallows

The Shallows

Nicholas Carr / W. W. Norton & Company / 2011-6-6 / USD 15.95

"Is Google making us stupid?" When Nicholas Carr posed that question, in a celebrated Atlantic Monthly cover story, he tapped into a well of anxiety about how the Internet is changing us. He also crys......一起来看看 《The Shallows》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

RGB HSV 转换
RGB HSV 转换

RGB HSV 互转工具

HEX HSV 转换工具
HEX HSV 转换工具

HEX HSV 互换工具