zoukankan      html  css  js  c++  java
  • Spring MVC错误页面配置

    当前SpringMVC非常流行,在大多数情况,我们都需要自定义一些错误页面(例如:401, 402, 403, 500...),以便更友好的提示。对于spring mvc,这些当然是支持自定义的,spring是怎么做的? 还是去看看spring的源码吧:

    原理

    DispatcherServlet

    众所周知,springmvc的入口是DispatcherServlet, 在DispatcherServlet的源码中,不知你是否注意到了以下方法:

    protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
    		Object handler, Exception ex) throws Exception {
    
    	// Check registered HandlerExceptionResolvers...
    	ModelAndView exMv = null;
    	for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) {
    		exMv = handlerExceptionResolver.resolveException(request, response, handler, ex);
    		if (exMv != null) {
    			break;
    		}
    	}
    	if (exMv != null) {
    		if (exMv.isEmpty()) {
    			request.setAttribute(EXCEPTION_ATTRIBUTE, ex);
    			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;
    }
    

    这个方法就是springmvc对于异常的处理,其调用了HandlerExceptionResolver的resolveException方法。HandlerExceptionResolver有众多实现类,其中,重点看看
    SimpleMappingExceptionResolver(我们就是要通过它来配置自定义错误页面)。

    SimpleMappingExceptionResolver

    public class SimpleMappingExceptionResolver extends AbstractHandlerExceptionResolver {
    	...
    	private Properties exceptionMappings;
    
    	private Class<?>[] excludedExceptions;
    	
    	private Map<String, Integer> statusCodes = new HashMap<String, Integer>();
    	...
    	
    	public void setExceptionMappings(Properties mappings) {
    		this.exceptionMappings = mappings;
    	}
    	
    	public void setStatusCodes(Properties statusCodes) {
    		for (Enumeration<?> enumeration = statusCodes.propertyNames(); enumeration.hasMoreElements();) {
    			String viewName = (String) enumeration.nextElement();
    			Integer statusCode = new Integer(statusCodes.getProperty(viewName));
    			this.statusCodes.put(viewName, statusCode);
    		}
    	}
    	
    }
    
    @Override
    protected ModelAndView doResolveException(HttpServletRequest request, HttpServletResponse response,
    		Object handler, Exception ex) {
    
    	// Expose ModelAndView for chosen error view.
    	String viewName = determineViewName(ex, request);
    	if (viewName != null) {
    		// Apply HTTP status code for error views, if specified.
    		// Only apply it if we're processing a top-level request.
    		Integer statusCode = determineStatusCode(request, viewName);
    		if (statusCode != null) {
    			applyStatusCodeIfPossible(request, response, statusCode);
    		}
    		return getModelAndView(viewName, ex, request);
    	}
    	else {
    		return null;
    	}
    }
    
    protected String determineViewName(Exception ex, HttpServletRequest request) {
    	String viewName = null;
    	if (this.excludedExceptions != null) {
    		for (Class<?> excludedEx : this.excludedExceptions) {
    			if (excludedEx.equals(ex.getClass())) {
    				return null;
    			}
    		}
    	}
    	// Check for specific exception mappings.
    	if (this.exceptionMappings != null) {
    		viewName = findMatchingViewName(this.exceptionMappings, ex);
    	}
    	// Return default error view else, if defined.
    	if (viewName == null && this.defaultErrorView != null) {
    		if (logger.isDebugEnabled()) {
    			logger.debug("Resolving to default view '" + this.defaultErrorView + "' for exception of type [" +
    					ex.getClass().getName() + "]");
    		}
    		viewName = this.defaultErrorView;
    	}
    	return viewName;
    }
    
    protected String findMatchingViewName(Properties exceptionMappings, Exception ex) {
    	String viewName = null;
    	String dominantMapping = null;
    	int deepest = Integer.MAX_VALUE;
    	for (Enumeration<?> names = exceptionMappings.propertyNames(); names.hasMoreElements();) {
    		String exceptionMapping = (String) names.nextElement();
    		int depth = getDepth(exceptionMapping, ex);
    		if (depth >= 0 && (depth < deepest || (depth == deepest &&
    				dominantMapping != null && exceptionMapping.length() > dominantMapping.length()))) {
    			deepest = depth;
    			dominantMapping = exceptionMapping;
    			viewName = exceptionMappings.getProperty(exceptionMapping);
    		}
    	}
    	if (viewName != null && logger.isDebugEnabled()) {
    		logger.debug("Resolving to view '" + viewName + "' for exception of type [" + ex.getClass().getName() +
    				"], based on exception mapping [" + dominantMapping + "]");
    	}
    	return viewName;
    }
    
    protected Integer determineStatusCode(HttpServletRequest request, String viewName) {
    	if (this.statusCodes.containsKey(viewName)) {
    		return this.statusCodes.get(viewName);
    	}
    	return this.defaultStatusCode;
    }
    

    由此可见:

    SimpleMappingExceptionResolver通过 exceptionMappings和statusCodes来确立Exception、http状态码以及view之间的映射关系。明白这个就很简单了,我们可以通过设置exceptionMappings、statusCodes的值来实现我们自定义的映射关系。

    实战

    页面准备

    1. 我们在WEB-INF/views/commons/error(目录自己定)新建我们自定义的错误页面,404.html, 500.html等等。

    2. SimpleMappingExceptionResolver只实现映射关系,我们还需要通过配置web.xml来实现。

       <error-page>
           <error-code>404</error-code>
           <location>/error/404.html</location>
       </error-page>
       
       <error-page>
           <error-code>500</error-code>
           <location>/error/500.html</location>
       </error-page>
      
    3. 在spring-mvc配置文件中将404.html、500.html等设置为资源文件,避免被springmvc再次拦截。

       <mvc:resources mapping="/error/**" location="/WEB-INF/views/commons/error/" />
      
    4. 配置SimpleMappingExceptionResolver。

       <bean class="org.springframework.web.servlet.handler. SimpleMappingExceptionResolver">
           <property name="exceptionMappings">
               <map>
                   <entry key="ResourceNotFoundException" value="common/error/resourceNotFoundError" />
                   <entry key=".DataAccessException" value="common/error/dataAccessError" />
               </map>
           </property>
           <property name="statusCodes">
               <map>
                   <entry key="common/error/resourceNotFoundError" value="404" />
                   <entry key="common/error/dataAccessError" value="500" />
               </map>
           </property>
       </bean>	
      

    到此,就实现我们需要的配置了。

    欢迎访问我的独立博客:

    www.javafan.cn

  • 相关阅读:
    Hibernate的查询方式汇总
    JdbcTemplate详解
    spring读取数据库的配置信息(url、username、password)时的<bean>PropertyPlaceholderConfigurer的用法
    spring aop方式配置事务中的三个概念 pointcut advice advisor
    spring mvc静态资源请求和<mvc:annotation-driven>
    spring aop实现原理
    Spring 配置 事务的几种方式
    转!!常用的4种动态网页技术—CGI、ASP、JSP、PHP
    转! java 中“==” 与“ .equals ”比较
    mysql 批处理文件--- 创建 用户 以及 导入数据
  • 原文地址:https://www.cnblogs.com/dongying/p/6129937.html
Copyright © 2011-2022 走看看