SpringMVC對異常進行全局處理,並區分對待ajax和普通請求


異常信息應統一進行處理. 程序員開發過程中,應盡量少用try..catch.避免因為catch造成的業務歧義.
而在web開發中,普通的頁面提交動作,和ajax提交動作,處理方式不一樣,因為跳轉后直接顯示響應數據,而ajax是通過error回調函數進行處理.

這里的處理思路,適用springmvc和struts2. 只是叫法不一樣,一個是HandlerExceptionResolver ,一個是exceptioninterceptor.

下面是部分摘要,體現一下思路

首先定義異常攔截器:

@Component
public class MyExceptionHandler implements HandlerExceptionResolver {
    protected Log log = LogFactory.getLog(this.getClass());

    @Override
    public ModelAndView resolveException(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex) {
        log.error("異常捕獲", ex);
        String requestURI = request.getRequestURI();
        String fileExtName = StringUtils.getFileExtName(requestURI);
        boolean isajax = "ajax".equals(fileExtName);
        Throwable parseException = parseException(ex);
        return ExceptionHandlerFactory.createExceptionhandler(parseException)
                .resolveException(request, response, handler, parseException, isajax);
    }
  //這里要獲取到最內層的異常
private static Throwable parseException(Throwable e){ Throwable tmp = e; int breakPoint = 0; while(tmp.getCause()!=null){ if(tmp.equals(tmp.getCause())){ break; } tmp=tmp.getCause(); breakPoint++; if(breakPoint>1000){ break; } } return tmp; } }

然后是 ExceptionHandlerFactory 主要是用於生成 異常處理的具體類型

public class ExceptionHandlerFactory {
    
    /**
     *  外掛的自定義處理器,用於外部擴展
     */
    private static  Map<String , ExceptionHandler> handlerList = null;
    
    public static ExceptionHandler createExceptionhandler(Throwable ex){
        //這個是自定義的接口
        ExceptionHandler exceptionHandler=null;
        String packageName=ExceptionHandlerFactory.class.getName().replace(ExceptionHandlerFactory.class.getSimpleName(), "");
        String className = ex.getClass().getSimpleName();
        String classFullName = ex.getClass().getName();
 
        if(handlerList==null){
            handlerList = new HashMap<String, ExceptionHandler>();
        }
        if(handlerList.containsKey(classFullName)){
            return handlerList.get(classFullName);
        }
        
        //能走到這邊,說明自定義的沒有生效,走過之后,下面會將他緩存,也就是說,自定義的優先級永遠大過系統自帶的
        try {
        //這里查找系統自帶的,按照捕獲的異常名稱+ Handler進行查找,算是簡單約定,因為框架開發中的內置我可以約定,擴展的使用配置文件進行 exceptionHandler
= (ExceptionHandler)Class.forName(packageName+ className+"Handler").newInstance(); } catch (Exception e) { e.printStackTrace(); } if(exceptionHandler==null){ exceptionHandler = new SimpleExceptionHandler(); } handlerList.put(classFullName, exceptionHandler); return exceptionHandler; } public Map<String, ExceptionHandler> getHandlerList() { return handlerList; }   //這里有getset,用於spring注入 public void setHandlerList(Map<String, ExceptionHandler> handlerList) { ExceptionHandlerFactory.handlerList = handlerList; } }

最后書寫一個用於驗證異常的處理類型

public class ConstraintViolationExceptionHandler  extends BaseExceptionHandler{

    @Override
    public Object processException(HttpServletRequest request,
            HttpServletResponse response, Object handler, Throwable ex,
            boolean isajax) {
        ConstraintViolationException e=(ConstraintViolationException)ex;
        Set<ConstraintViolation<?>> constraintViolations = e.getConstraintViolations();
       //ValidateInfo包含當前出錯的字段,錯誤信息,出錯的字段所在的類型,等必要的信息
       // 在ajax特別是ajaxForm的提交中,只要前端約定好了命名規則,就可以根據返回的信息,進行界面提示,比如可渲染成和validate一樣的風格
      // 也可以給出一個dialog提示,或者......
List
<ValidateInfo> validateInfos =new ArrayList<ValidateInfo>(); if(constraintViolations!=null && !constraintViolations.isEmpty()){ for(ConstraintViolation<?> violation : constraintViolations){ ValidateInfo info = new ValidateInfo(); info.setField(violation.getPropertyPath().toString().replaceAll("\\.","_")); info.setMessage(violation.getMessage()); Class<? extends Object> class1 = violation.getRootBean().getClass(); String simpleName =StringUtils.getSpringName(class1); if(simpleName.indexOf("$pcsubclass")>-1){ //這個判斷是openjpa的代理類型,帶$的不光是代理類型,內部類的名稱同樣有,所以編碼上要約束 String[] ss = simpleName.split("\\$"); if(ss.length>1){ simpleName = ss[ss.length-2]; simpleName = simpleName.substring(0,1).toLowerCase()+simpleName.substring(1); } } info.setClassName(simpleName); Object ov =violation.getInvalidValue(); if(ov==null){ info.setCurrentValue(""); }else{ info.setCurrentValue(ov.toString()); } validateInfos.add(info); } } return validateInfos; //返回經過封裝的驗證信息,用於jquery ajax error回調方法進行統一處理 } }

BaseExceptionHandler 只是對最后響應代碼做一個判斷

public abstract class BaseExceptionHandler implements ExceptionHandler{

    /**
     * 用於傳遞到頁面的值
     */
    protected Map<String, Object> data = new HashMap<String, Object>();
    
    /**
     * 寫到輸出流
     */
    protected ModelAndView write(HttpServletRequest request,
            HttpServletResponse response, Object handler, Throwable ex,
            boolean isajax,Object dt){
        int responseCode=500;
        if(ex instanceof BaseRuntimeException){
            responseCode=((BaseRuntimeException)ex).getResponseCode();
        }
        if(ex instanceof ConstraintViolationException){
            responseCode=5001;
        }
        response.setStatus(responseCode);
        if(!isajax){//非ajax,直接跳轉的,這里的是否ajax很簡單,我們約定,ajax請求全部使用.ajax擴展.當然通過httpheader也能,jquery還支持preFilter,可以在這里統一加參數
            ModelAndView modelAndView = new ModelAndView("/error/error");
            modelAndView.addObject("__exception__", ex);
            modelAndView.addAllObjects(data);
            if(dt!=null){
            modelAndView.addObject(dt);
            }
            return modelAndView;
        }
        //這個是封裝的標准返回值模版,包含相應code,錯誤信息和響應數據等字段
        ResultTemplate createFailResult = ResultTemplate.createFailResult(ex.getMessage());
        createFailResult.setData(dt);
        WebUtils.renderJson(createFailResult); 
        return null;
    }

    public abstract Object processException(HttpServletRequest request,
            HttpServletResponse response, Object handler, Throwable ex,
            boolean isajax);
    
    
    @Override
    public ModelAndView resolveException(HttpServletRequest request,
            HttpServletResponse response, Object handler, Throwable ex,
            boolean isajax) { 
        Object d = processException(request,  response,   handler,   ex,  isajax); 
        return write(request,
                  response,   handler,   ex,
                  isajax,d);
    }
    
    
    
}

xml 配置

<bean class="com.core.web.interceptor.exceptionhandler.ExceptionHandlerFactory">
        <property name="handlerList">
                     <map>
                         <entry key="javax.validation.ConstraintViolationException" > <!-- jpa驗證異常,這里可以不配置,系統內置,也可以配置自己的,替換系統自帶的 -->
                          <bean class="com.core.web.interceptor.exceptionhandler.ConstraintViolationExceptionHandler"/> 
                         </entry>
                          
                     </map>
             </property> 
    </bean>

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM