其实 Spring 的基本思想就是“万物都是 bean”,那么为了知足 spring 工程的须要,spring 中有一些默认的 bean 选项,它们用于处理请求,渲染视图等。好比上一篇文章就用过的 viewResolver 的配置。固然,servlet 也容许你配置使用不一样特定的 bean,可是,若是你没有配置,spring 将会按照默认的 bean 进行配置。本章将会详细说明文档中列出的 bean 的配置以及具体的使用例子,所讲述的 bean 类型包括:html
本章节将基于文档实践(一)的代码进行后续的操做,所以咱们使用了单个 ContextConfig 来配置工程 Context 对象,也就是 root-context.xml 文件。另外一方面,为了实现 HandlerMapping 在 xml 配置的功能,咱们关掉了前端
<mvc:annotation-driven/>
复制代码
的功能,使得 @Controller 注解下的类再也不会被自动配置而且作 url 的映射,如今再去试一下 localhost:8080/hello.do 的话,已是 404 Not Found 了。以后再进行后续的实践过程。java
这里 HandlerMapping 和 HandlerAdapter 一块儿讲是由于,HandlerMapping 须要 HandlerAdapter 的支持才能正常运行。HandlerMapping 用于将请求的 url 映射到对应的 controller 上面,若是没有进行配置的话,@Controller 注解即为 HandlerMapping,上一篇的 ExampleController 即有着和上述类似的功能。值得注意的是,Spring MVC 4.0 以后主推 Annotation Driven,也就是注解驱动模式下的工程,所以,对应的 adapter 已经标记为 deprecated,不推荐使用,这里只作帮助理解使用。web
因为工程中的 Controller 都是用注解配置的,所以,在 DispatcherServlet 根据 bean 的配置信息(root-context.xml,咱们用 Context 对象来配置 bean 的信息)知道了本身所须要调用的 controller 以后,他须要根据注解来提取其余的所须要的信息。这时候就须要 HandlerAdapter 来作这些解析的事情。spring
然而,目前的 Spring MVC 的配置都基于注解,所以,HandlerAdapter 也退居幕后,@Controller 注解包含了其中逻辑,在 Annotation-driven 被咱们关掉的场景下,也只要作好 HandlerMapping,就能够成功地映射你想要的 url浏览器
HandlerMapping 本质仍是一个 Bean,他在 Spring MVC 装配完成以后,执行着将 URL 的请求转发到对应的 Controller 执行后续视图,数据等返回的工做。所以,在配置 HandlerMapping Bean 的时候,须要配置 property 的 mappings 字段,而且在 字段下面指定对应的请求映射。具体代码以下:spring-mvc
<bean class="org.springframework.web.servlet.handler.SimpleUrlHandlerMapping">
<property name="mappings">
<props>
<prop key="/handler-mapping.do">handlerMappingController</prop>
</props>
</property>
</bean>
复制代码
为了同步一下,目前 root-context.xml (Spring Context 对象配置文件) 的配置加入了 HandlerMapping 的配置:mvc
<?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 的配置:app
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 上的前端视图返回。jsp
HandlerExceptionResolver 是工程中用于捕获特定 Exception 的 Bean,能够提早设定本身须要捕获而且定向的 Exception,而且交由 HandlerExceptionResolver 映射到特定的视图页上面。 目前经常使用的方法有:
HandlerExceptionResolver 接口只有一个待实现的方法
ModelAndView resolveException(HttpServletRequest var1, HttpServletResponse var2, Object var3, Exception var4);
复制代码
为了工程上面比较直观简便的实现,咱们只须要作最简单的实现:拿到 Exception 的具体类,而且返回对应的 error 的视图,而且记录下 Exception 的 message,显示在视图页面上面。所以咱们的工序以下:
package com.test.myapp.example.handlermapping;
public class MyCustomException extends RuntimeException {
public MyCustomException(String msg) {
super(msg);
}
}
复制代码
这个 Exception 类很简单,只是把 message 放进 Exception 中,无需赘述,主要是要让 ExceptionResolver 捕获该 Exception。
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 字段的信息。
为了对照效果,咱们实现两个接口,一个会抛出 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 比较简单,只要体现 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 的注解,该注解用于 method 的签名上面,咱们能够实现一个 Controller 的基类并让实际接收 url 请求的 Controller 继承该基类。值得注意的是,这个方法实现的 ExceptionResolver 只会在该 Controller 内部有效,而来自其余 Controller 类的 Exception 则没法获得解析。具体代码步骤以下:
咱们为这一次测试也设置了自定义的 Exception 类,实现方法也很简单,能够自定义 Exception 中的信息:
package com.test.myapp.example.exceptionresolver;
public class CustomExceptionForAnnotation extends RuntimeException {
public CustomExceptionForAnnotation(String msg) {
super(msg);
}
}
复制代码
咱们的 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 类,一个继承自 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,观察是否会有咱们想要的视图的输出:
本章主要讲述了 HandlerMapping 和 HandlerExceptionResolver 的具体实现代码,一个是处理正常的 url 请求的映射工具,而另外一个则是专门处理工程在运行过程当中出现 Exception 的处理方法。下一次我将继续介绍后面这几个特殊 Bean 的用法。