在使用基于注解的spring mvc时,使用情况如下:
@Controller
@RequestMapping("/test")
public class TestController {
@RequestMapping(value="/index/{userId}", params="username")
public ModelAndView index(@PathVariable("userId") String userId,
@RequestParam("password") String password,
@RequestParam("id") int id, Map<String, Object> map) throws Exception {
System.out.println(userId);
System.out.println(password);
System.out.println(id);
JSONObject obj = new JSONObject();
obj.put("data", "");
obj.put("status", "");
return new ModelAndView("index");
}
}
出现异常会有一下几种情况:
1 参数不匹配, spring mvc有自己的异常处理,会调到一个也没,HttpStatus 400
2 用户在调用的service中,自己抛出异常或者代码异常,这个最好统一捕捉,不要在每个Controller方法里进行捕捉
看spring mvc的源码,可以知道spring mvc处理步骤如下:
1 spring mvc的入口为:
<servlet>
<servlet-name>spring-mvc-servlet</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:/spring-mvc-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>spring-mvc-servlet</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
2 DispatcherServlet 是一个Servlet,DispatcherServlet首先处理请求时会调用doService方法:
@Override
protected void doService(HttpServletRequest request, HttpServletResponse response) throws Exception {
if (logger.isDebugEnabled()) {
String requestUri = urlPathHelper.getRequestUri(request);
String resumed = WebAsyncUtils.getAsyncManager(request).hasConcurrentResult() ? " resumed" : "";
logger.debug("DispatcherServlet with name '" + getServletName() + "'" + resumed +
" processing " + request.getMethod() + " request for [" + requestUri + "]");
}
// Keep a snapshot of the request attributes in case of an include,
// to be able to restore the original attributes after the include.
Map<String, Object> attributesSnapshot = null;
if (WebUtils.isIncludeRequest(request)) {
logger.debug("Taking snapshot of request attributes before include");
attributesSnapshot = new HashMap<String, Object>();
Enumeration<?> attrNames = request.getAttributeNames();
while (attrNames.hasMoreElements()) {
String attrName = (String) attrNames.nextElement();
if (this.cleanupAfterInclude || attrName.startsWith("org.springframework.web.servlet")) {
attributesSnapshot.put(attrName, request.getAttribute(attrName));
}
}
}
// Make framework objects available to handlers and view objects.
request.setAttribute(WEB_APPLICATION_CONTEXT_ATTRIBUTE, getWebApplicationContext());
request.setAttribute(LOCALE_RESOLVER_ATTRIBUTE, this.localeResolver);
request.setAttribute(THEME_RESOLVER_ATTRIBUTE, this.themeResolver);
request.setAttribute(THEME_SOURCE_ATTRIBUTE, getThemeSource());
FlashMap inputFlashMap = this.flashMapManager.retrieveAndUpdate(request, response);
if (inputFlashMap != null) {
request.setAttribute(INPUT_FLASH_MAP_ATTRIBUTE, Collections.unmodifiableMap(inputFlashMap));
}
request.setAttribute(OUTPUT_FLASH_MAP_ATTRIBUTE, new FlashMap());
request.setAttribute(FLASH_MAP_MANAGER_ATTRIBUTE, this.flashMapManager);
try {
//上面做了一系列的操作是想获取必要的资源,在这里分发了请求
doDispatch(request, response);
}
finally {
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
return;
}
// Restore the original attribute snapshot, in case of an include.
if (attributesSnapshot != null) {
restoreAttributesAfterInclude(request, attributesSnapshot);
}
}
}
doDispatch方法代码如下:
protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {
HttpServletRequest processedRequest = request;
HandlerExecutionChain mappedHandler = null;
boolean multipartRequestParsed = false;
WebAsyncManager asyncManager = WebAsyncUtils.getAsyncManager(request);
try {
ModelAndView mv = null;
Exception dispatchException = null;
try {
processedRequest = checkMultipart(request);
multipartRequestParsed = processedRequest != request;
// Determine handler for the current request.
mappedHandler = getHandler(processedRequest, false);
if (mappedHandler == null || mappedHandler.getHandler() == null) {
noHandlerFound(processedRequest, response);
return;
}
// Determine handler adapter for the current request.
HandlerAdapter ha = getHandlerAdapter(mappedHandler.getHandler());
// Process last-modified header, if supported by the handler.
String method = request.getMethod();
boolean isGet = "GET".equals(method);
if (isGet || "HEAD".equals(method)) {
long lastModified = ha.getLastModified(request, mappedHandler.getHandler());
if (logger.isDebugEnabled()) {
String requestUri = urlPathHelper.getRequestUri(request);
logger.debug("Last-Modified value for [" + requestUri + "] is: " + lastModified);
}
if (new ServletWebRequest(request, response).checkNotModified(lastModified) && isGet) {
return;
}
}
if (!mappedHandler.applyPreHandle(processedRequest, response)) {
return;
}
try {
// Actually invoke the handler.在此处真实的调用了我们的Controller
mv = ha.handle(processedRequest, response, mappedHandler.getHandler());
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
return;
}
}
applyDefaultViewName(request, mv);
mappedHandler.applyPostHandle(processedRequest, response, mv);
}
catch (Exception ex) {//当出现错误的时候,捕捉到请求在下面的processDispatchResult处理异常信息
dispatchException = ex;
}
processDispatchResult(processedRequest, response, mappedHandler, mv, dispatchException);//处理异常信息
}
catch (Exception ex) {
triggerAfterCompletion(processedRequest, response, mappedHandler, ex);
}
catch (Error err) {
triggerAfterCompletionWithError(processedRequest, response, mappedHandler, err);
}
finally {
if (asyncManager.isConcurrentHandlingStarted()) {
// Instead of postHandle and afterCompletion
mappedHandler.applyAfterConcurrentHandlingStarted(processedRequest, response);
return;
}
// Clean up any resources used by a multipart request.
if (multipartRequestParsed) {
cleanupMultipart(processedRequest);
}
}
}
processDispatchResult方法的代码为:
private void processDispatchResult(HttpServletRequest request, HttpServletResponse response,
HandlerExecutionChain mappedHandler, ModelAndView mv, Exception exception) throws Exception {
boolean errorView = false;
if (exception != null) {
if (exception instanceof ModelAndViewDefiningException) {
logger.debug("ModelAndViewDefiningException encountered", exception);
mv = ((ModelAndViewDefiningException) exception).getModelAndView();
}
else {//此处处理真正的异常
Object handler = (mappedHandler != null ? mappedHandler.getHandler() : null);
mv = processHandlerException(request, response, handler, exception);
errorView = (mv != null);
}
}
// Did the handler return a view to render?
if (mv != null && !mv.wasCleared()) {
render(mv, request, response);
if (errorView) {
WebUtils.clearErrorRequestAttributes(request);
}
}
else {
if (logger.isDebugEnabled()) {
logger.debug("Null ModelAndView returned to DispatcherServlet with name '" + getServletName() +
"': assuming HandlerAdapter completed request handling");
}
}
if (WebAsyncUtils.getAsyncManager(request).isConcurrentHandlingStarted()) {
// Concurrent handling started during a forward
return;
}
if (mappedHandler != null) {
mappedHandler.triggerAfterCompletion(request, response, null);
}
}
processHandlerException方法如下,这里会选取HandlerExceptionResolver来处理对应的异常
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
// Check registered HandlerExceptionResolvers...
ModelAndView exMv = null;
//遍历HandlerExceptionResolver,获取合适来处理异常
for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
if (exMv != null) {
if (exMv.isEmpty()) {
return null;
}
// We might still need view name translation for a plain error model...
if (!exMv.hasView()) {
exMv.setViewName(getDefaultViewName(request));
}
if (logger.isDebugEnabled()) {
logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex);
}
WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
return exMv;
}
throw ex;
}
spring mvc 在Dispatcher里容器onRefresh()方法的时候加载了HandlerExceptionResolver:
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
}
protected void initStrategies(ApplicationContext context) {
initMultipartResolver(context);
initLocaleResolver(context);
initThemeResolver(context);
initHandlerMappings(context);
initHandlerAdapters(context);
//初始化HandlerExceptionResolvers
initHandlerExceptionResolvers(context);
initRequestToViewNameTranslator(context);
initViewResolvers(context);
initFlashMapManager(context);
}
private void initHandlerExceptionResolvers(ApplicationContext context) {
this.handlerExceptionResolvers = null;
if (this.detectAllHandlerExceptionResolvers) {
// Find all HandlerExceptionResolvers in the ApplicationContext, including ancestor contexts.
//从spring mvc的容器中获取类型为HandlerExceptionResolver的bean
Map<String, HandlerExceptionResolver> matchingBeans = BeanFactoryUtils
.beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);
if (!matchingBeans.isEmpty()) {
this.handlerExceptionResolvers = new ArrayList<HandlerExceptionResolver>(matchingBeans.values());
// We keep HandlerExceptionResolvers in sorted order.
OrderComparator.sort(this.handlerExceptionResolvers);
}
}
else {
try {
HandlerExceptionResolver her =
context.getBean(HANDLER_EXCEPTION_RESOLVER_BEAN_NAME, HandlerExceptionResolver.class);
this.handlerExceptionResolvers = Collections.singletonList(her);
}
catch (NoSuchBeanDefinitionException ex) {
// Ignore, no HandlerExceptionResolver is fine too.
}
}
// Ensure we have at least some HandlerExceptionResolvers, by registering
// default HandlerExceptionResolvers if no other resolvers are found.
if (this.handlerExceptionResolvers == null) {
this.handlerExceptionResolvers = getDefaultStrategies(context, HandlerExceptionResolver.class);
if (logger.isDebugEnabled()) {
logger.debug("No HandlerExceptionResolvers found in servlet '" + getServletName() + "': using default");
}
}
}
由上面可知:spring mvc 从容器中获取了处理异常的HandlerExceptionResolver,spring mvc提供三种异常处理器:
DefaultHandlerExceptionResolver, ResponseStatusExceptionResolver, ExceptionHandlerExceptionResolver,用户可以在spring mvc的配置文件中配置自己的异常处理类:
<bean id="diyExceptionHandler" class="com.malone.handler.MyExceptionHandler"/>
<bean id="myDefaultHandlerExceptionResolver" class="com.malone.handler.MyDefaultHandlerExceptionResolver"/>
为了统一异常处理,我们可以自定义一个HandlerExceptionResolver,所有的异常均由自定义的异常处理, 由此我们可以重写DispatcherServlet类的
processHandlerException()方法:
public class MyDispatcherServlet extends DispatcherServlet {
private Map<String, HandlerExceptionResolver> matchingBeans = new HashMap<String, HandlerExceptionResolver>();
private Map<String, HandlerExceptionResolver> getMatchingBeans () {
if (matchingBeans.isEmpty()) {
System.out.println(SpringUtils.getContext());
synchronized (this) {
matchingBeans = BeanFactoryUtils
.beansOfTypeIncludingAncestors(SpringUtils.getContext(), HandlerExceptionResolver.class, true, false);
}
}
return matchingBeans;
}
@Override
protected void onRefresh(ApplicationContext context) {
initStrategies(context);
//把spring mvc容器缓存起来
SpringUtils.setContext(context);
}
@Override
protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
Object handler, Exception ex) throws Exception {
//获取HandlerExceptionResolver
Map<String, HandlerExceptionResolver> matchingBeans = this.getMatchingBeans();
if (matchingBeans == null || matchingBeans.size() == 0) {//没有配置处理异常的Controller
return new ModelAndView();
}
Map<String, HandlerExceptionResolver> newMatchingBeans = new HashMap<String, HandlerExceptionResolver>();
for (Iterator<String> iterator = matchingBeans.keySet().iterator(); iterator.hasNext();) {//使用指定handlerResolver
String key = iterator.next();
if (Objects.equals(key, "diyExceptionHandler") || Objects.equals(key, "myDefaultHandlerExceptionResolver")) {
newMatchingBeans.put(key, matchingBeans.get(key));
}
}
// Check registered HandlerExceptionResolvers...
ModelAndView exMv = null;
for (HandlerExceptionResolver handlerExceptionResolver : new ArrayList<HandlerExceptionResolver>(newMatchingBeans.values())) {
exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
if (exMv != null) {
break;
}
}
if (exMv != null) {
if (exMv.isEmpty()) {
return null;
}
// We might still need view name translation for a plain error model...
if (!exMv.hasView()) {
exMv.setViewName(getDefaultViewName(request));
}
if (logger.isDebugEnabled()) {
logger.debug("Handler execution resulted in exception - forwarding to resolved error view: " + exMv, ex);
}
WebUtils.exposeErrorRequestAttributes(request, ex, getServletName());
return exMv;
}
throw ex;
}
}
自定义了MyDispatcherServlet类,此类继承了DispatcherServlet,重写了一下方法:
onRefresh():重写此方法的目的是在原来的代码基础上增加缓存spring mvc容器的方法
processHandlerException ():重写此方法是为了修改获取HandlerExceptionResolver的逻辑,由上面代码可知,我们使用onRefresh()方法中缓存的spring mvc容器,
获取到自己注册的HandlerExceptionResolver,然后使用自己的注册HandlerExceptionResolver来处理所有异常
附完整的测试代码: