012医疗项目-模块一:统一异常处理器的设计思路及其实现(涉及到了Springmvc的异常处理流程)...

我们上一篇文章是建立了一个自定义的异常类,来代替了原始的Exception类。在Serice层抛出异常,然后要在Action层捕获这个异常,这样的话在每个Action中都要有try{}catch{}代码,这样很麻烦啊,那怎么办?定义一个统一的异常处理器类。所有的异常都不用自己去捕获了,统一进行处理就好。

编写流程:

 

 

我们这里用的是SpringMvc的框架,所以要先知道SpingMvc的执行流程:

Springmvc提供统一处理器机制,springmvc的前端控制器在调用适配器,去调用action,过程中如果发生异常,前端控制器交给异常处理器进行异常处理。

前端控制器源代码:

 

我们来讲一下Springmvc中的异常处理机制:

进入到:

我们看一下DispatcherServlet的源码:

这个类的核心函数就是:    protected void doDispatch(HttpServletRequest request, HttpServletResponse response) throws Exception {

我们在这个函数里面看到这个:

 

processHandlerException就是处理异常的。

进入到这个函数看一下:

    protected ModelAndView processHandlerException(HttpServletRequest request, HttpServletResponse response,
            Object handler, Exception ex) throws Exception {

        // Check registered HandlerExceptionResolvers...
        ModelAndView exMv = null;
//handlerExceptionResolvers是容器加载的时候把继承了HandlerExceptionResolver接口的类全部放入了handlerExceptionResolvers这个List里面。,具体是
//private void initHandlerExceptionResolvers(ApplicationContext context)这个函数完成.initHandlerExceptionResolvers函数里面有一句话
//    Map<String, HandlerExceptionResolver> matchingBeans =
// BeanFactoryUtils.beansOfTypeIncludingAncestors(context, HandlerExceptionResolver.class, true, false);

for (HandlerExceptionResolver handlerExceptionResolver : this.handlerExceptionResolvers) { exMv = handlerExceptionResolver.resolveException(request, response, handler, ex); // 遍历每一个实现了handlerExceptionResolver 接口的类,
然后调用它的 resolveException 方法进行异常处理
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; }

观察代码,发现在这里会遍历 Ioc 容器中的所有实现 handlerExceptionResolver 接口的类,然后调用它的 resolveException 方法进行异常处理,并返回具体的error page(错误页面)。

这个博客讲得很好:http://blog.csdn.net/u012420654/article/details/52141807

总结:也就是说我们要写自定义的异常处理器,就需要继承handlerExceptionResolver 接口。然后实现里面的resolveException方法,这样出现了错误之后就会去

执行我们自己写的resolveException()方法。

 

 

接下里我们写自己的异常处理器。

 

package yycg.base.process.exception;

import java.io.IOException;
import java.lang.reflect.Method;

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

import org.springframework.core.annotation.AnnotationUtils;
import org.springframework.http.HttpOutputMessage;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.converter.HttpMessageNotWritableException;
import org.springframework.http.server.ServletServerHttpResponse;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.method.HandlerMethod;
import org.springframework.web.servlet.HandlerExceptionResolver;
import org.springframework.web.servlet.ModelAndView;

import yycg.base.process.result.ExceptionResultInfo;
import yycg.base.process.result.ResultInfo;

//全局异常处理器
public class ExceptionResolverCustom implements HandlerExceptionResolver{
 //json转换器
    private HttpMessageConverter<ExceptionResultInfo> jsonMessageConverter;
    public HttpMessageConverter<ExceptionResultInfo> getJsonMessageConverter() {
        return jsonMessageConverter;
    }
    public void setJsonMessageConverter(
            HttpMessageConverter<ExceptionResultInfo> jsonMessageConverter) {
        this.jsonMessageConverter = jsonMessageConverter;
    }
    //前端控制器
    //handler就是最后要执行的Action类,Springmvc是面向方法开发的。handler就包装了一个方法
    //这个方法就对应于URL的那个方法,
    @Override
    public ModelAndView resolveException(HttpServletRequest request,
            HttpServletResponse response, Object handler, Exception ex) {
        
        
        //输出异常信息
        ex.printStackTrace();
        //在springmvc中,action是被Handle封装起来的,action的方法也被封装到Handle里面了。
        //转成springmvc底层对象,这个底层对象就是对action方法进行封装的那个对象
        HandlerMethod handlerMethod=(HandlerMethod)handler;//这个对象里面只有一个方法。
        Method method=handlerMethod.getMethod();//把这个方法取出来。
        //查找method这个方法上是否有ResponBody这个注解
        ResponseBody responseBody=AnnotationUtils.findAnnotation(method, ResponseBody.class);
        
        if(responseBody!=null)
        {
            //这里说明发生异常的Action是我们本来正常情况下要返回JSON数据的,我们在这里做的处理时是把这个错误拿出来我们自己转成json
          //再输出到页面上

return this.resolveJsonException(request, response, handlerMethod, ex);
}
//这里说明发生异常的那个Action方法没有用@Responbody定义。正常情况也不是返回json格式的数据的,那我们就没有必要转换成json了。
//解析异常
ExceptionResultInfo exceptionResultInfo=resolveExceptioncustom(ex);
//将异常信息在异常页面输出。
request.setAttribute("exceptionResultInfo", exceptionResultInfo.getResultInfo());
//转向错误页面:用Springmvc的方式去转
ModelAndView modelAndView=new ModelAndView();
modelAndView.addObject(
"exceptionResultInfo", exceptionResultInfo.getResultInfo()); modelAndView.setViewName("/base/error");
return modelAndView; }



private ModelAndView resolveJsonException(HttpServletRequest request,HttpServletResponse response, Object handler, Exception ex) { //解析异常 ExceptionResultInfo exceptionResultInfo=resolveExceptioncustom(ex); HttpOutputMessage outputMessage=new ServletServerHttpResponse(response); try { //将exceptionResultInfo转成json然后输出,这里直接通过HttpMessageConverter将exceptionResultInfo转化为 //JSON,然后直接在HttpOutputMessage这个对象中把转化而成的JSON信息写入。输出到浏览器客户端。 jsonMessageConverter.write(exceptionResultInfo,MediaType.APPLICATION_JSON,outputMessage ); } catch (HttpMessageNotWritableException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } return new ModelAndView(); } //解析异常,看是不是我们自己定义的异常 private ExceptionResultInfo resolveExceptioncustom( Exception ex) { ResultInfo resultInfo=null; //这样的话就是系统的自定义异常 if(ex instanceof ExceptionResultInfo) { //解析系统的自定义的异常, resultInfo=((ExceptionResultInfo) ex).getResultInfo(); }else { //不属于系统自定义异常的话就是未知错误的异常,就是不可知的异常 resultInfo=new ResultInfo(); resultInfo.setType(ResultInfo.TYPE_RESULT_FAIL); resultInfo.setMessage("未知错误异常"); } return new ExceptionResultInfo(resultInfo); } }

 

总结:就是说浏览器进入到了Action中的方法中,在执行过程中,发生了异常。然后触发了我们的自定义异常处理器。进入到了resolveException()这个方法。

在这个方法里面首先要做的就是把封装到Handle中的那个Action取出来,然后把这个Action对象中执行的那个方法取出来。看这个方法有没有ResponBody这个注解这个注解,有的话就是说使我们自己定义的异常信息,比如账号重复什么的,我们是要求把这个“账号重复”这类异常信息变成JSON信息放到客户端的,所以就把这个异常信息转化为json格式输出到页面。

然后如果发现这个方法没有ResponBody这个注解,那么我们这个就不是我们自定义的异常了,那么我们把这个异常信息解析好之后,通过自己定义的error.jsp页面把错误信息输出。

 

 

 

上面已经写好了异常处理类,接下来我们就把这个异常处理类配置好。

来到Springmvc.xml中:

 <bean id="handlerExceptionResolver" 这个id要固定,一个字都不能改。
<beans xmlns="http://www.springframework.org/schema/beans"
    xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:mvc="http://www.springframework.org/schema/mvc"
    xmlns:context="http://www.springframework.org/schema/context"
    xmlns:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx"
    xsi:schemaLocation="http://www.springframework.org/schema/beans 
        http://www.springframework.org/schema/beans/spring-beans-3.1.xsd 
        http://www.springframework.org/schema/mvc 
        http://www.springframework.org/schema/mvc/spring-mvc-3.1.xsd 
        http://www.springframework.org/schema/context 
        http://www.springframework.org/schema/context/spring-context-3.1.xsd 
        http://www.springframework.org/schema/aop 
        http://www.springframework.org/schema/aop/spring-aop-3.1.xsd 
        http://www.springframework.org/schema/tx 
        http://www.springframework.org/schema/tx/spring-tx-3.1.xsd ">

    <!-- 组件扫描 扫描所有标记@Controller类,由于使用自动扫描所以action类不用在spring配置文件中配置 -->
    <!-- 为什么这里就要用包扫面的方式了而不是用bean 的方式了因为这里的action太多了,配置不过来,只好用扫描了 -->
    <context:component-scan base-package="yycg.**.action" />

    <!-- 处理器映射器和适配器,可以使用mvc注解驱动 -->
    <mvc:annotation-driven/>


    <!-- 视图解析器 -->
    <bean
        class="org.springframework.web.servlet.view.InternalResourceViewResolver">
        <!-- 将jstl的jar包加入工程,默认支持jstl -->
        <!-- 前缀和后缀可以去掉的,为了方便开发才加的 -->
        <property name="prefix" value="/WEB-INF/jsp" />
        <property name="suffix" value=".jsp" />
    </bean>




<!-- json提供的json转换器 --> <bean id="jsonmessageConverter" class="org.springframework.http.converter.json.MappingJacksonHttpMessageConverter"> </bean> <!-- 配置统一异常处理器类 --> <bean id="handlerExceptionResolver" class="yycg.base.process.exception.ExceptionResolverCustom" > <!-- 注入一个json转换器--> <property name="jsonMessageConverter" ref="jsonmessageConverter"/> </bean>

</beans>

 

然后我们还要在web.xml中进行配置。

 

在web.xml中添加如下:

 

<servlet>
        <servlet-name>springmvc</servlet-name>
        <servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
        <!-- contextConfigLocation不是必须的, 如果不配置contextConfigLocation, springmvc的配置文件默认在:WEB-INF/servlet的name+"-servlet.xml" -->
        <init-param>
            <param-name>contextConfigLocation</param-name>
            <param-value>classpath:spring/springmvc.xml</param-value>
        </init-param>
    
     
        
        <!-- 屏蔽springmvc自动注册的异常处理器 -->
         <init-param>
      <param-name>detectAllHandlerExceptionResolvers</param-name>
      <param-value>false</param-value>
    </init-param>
        
    </servlet>

detectAllHandlerExceptionResolvers的作用:屏蔽自动注册异常处理器,固定使用bean的id为handlerExceptionResolver的异常处理器。(所以自定义的异常处理器的id是要要固定的)

 

 

 接下来我们可以把之前的在Action中try{}catch的做法给去掉了。达到我们最先的想法。

修改后的代码如下:

 

    @RequestMapping("/addsysusersubmit")
    public @ResponseBody SubmitResultInfo addsysusersubmit(SysuserQueryVo sysuserQueryVo) throws Exception
    {
        ResultInfo resultInfo=new ResultInfo();
        resultInfo.setType(ResultInfo.TYPE_RESULT_SUCCESS);//默认是成功的
        resultInfo.setMessage("成功了");
        SubmitResultInfo submitResultInfo=new SubmitResultInfo(resultInfo);
           userService.insertSysuser(sysuserQueryVo.getSysuserCustom());
        //使用全局异常处理器后不用捕获异常了。
        submitResultInfo.setResultInfo(resultInfo);
        
        return submitResultInfo;
        
    }

 

到这里,我们的配置就好了接下里就是测试:现在只要在执行Acition里面的任何方法时遇到任何问题都会由这个异常处理器来处理.

测试如下:

1、  在action的提交方法(addsysusersubmit(SysuserQueryVo sysuserQueryVo) )中发生异常,由全局异常处理器进行处理。

 

 

 

 

 

 

2、在action返回jsp方法中发生异常,由全局异常处理器进行处理。

测试方法:我们在随便一个Action的方法中写一句:int i=1/0;

然后就出现了:

 

 

测试成功

 

 

 

 

------------------------------------------------------------------------------------------------------------------------------------------

第二次看自己的博客时有点绕晕。我写一个具体的案例,结合代码来讲解一下:

我们看userAction:里面有这么一段代码:

    //添加用户提交
    //提交的结果要转json到页面
    //提交表单的数据一律使用包装类,这里我们用的是SysuserQueryVo
    //在SysuserQueryVo中包装了SysuserCustom
    @RequestMapping("/addsysusersubmit")
    public @ResponseBody SubmitResultInfo addsysusersubmit(SysuserQueryVo sysuserQueryVo) throws Exception
    {
        //将执行结果返回页面
        
        userService.insertSysuser(sysuserQueryVo.getSysuserCustom());
        //使用全局异常处理器后不用捕获异常了。
        //submitResultInfo.setResultInfo(resultInfo);
        return ResultUtil.createSubmitResult(ResultUtil.createSuccess(Config.MESSAGE,906 , null));
        
    }

 

我们看一下userService.insertSysuser(sysuserQueryVo.getSysuserCustom());这个函数:

@Override
    public void insertSysuser(SysuserCustom sysuserCustom) throws Exception {
        //参数的校验
        //通用的参数合法性校验
       Sysuser sysuser=findSysuerByUserid(sysuserCustom.getUserid());
    //如果不等于空,就是账号重复了。给出一个警告。
        if(sysuser!=null)
        {
            //包警告告诉到Action,可以再Aciton中捕获这个异常。
            //throw new Exception("单位名称输入错误");
            //使用系统自定义的异常类
            /*ResultInfo resultInfo=new ResultInfo();
            resultInfo.setType(ResultInfo.TYPE_RESULT_FAIL);
            String message=ResourcesUtil.getValue("resources.messages", "213");
            resultInfo.setMessage(message);
            throw new ExceptionResultInfo(resultInfo);*/
            //使用工具类对ResultInfo做一个封装。
            ResultInfo resultInfo=ResultUtil.createFail(Config.MESSAGE, 213, null);
            new ResultUtil().throwExcepion(resultInfo);
        }
        //根据用户类型,输入单位名称必须存在对应的单位表中
        String groupid=sysuserCustom.getGroupid();//用户类型
        String sysmc=sysuserCustom.getSysmc();//单位名称
        String userid=null;
        if(groupid.equals("1")||groupid.equals("2"))
        {//监督单位
            //根据单位名称查询单位的信息。
            Userjd userjd=this.findUserJdByMc(sysmc);
            if(userjd==null)
            {
                //throw new Exception("单位名称输入错误");
                //使用系统自定义的异常类
                ResultInfo resultInfo=new ResultInfo();
                resultInfo.setType(ResultInfo.TYPE_RESULT_FAIL);
                resultInfo.setMessage("单位名称输入错误");
                throw new ExceptionResultInfo(resultInfo);
            }
            
            userid=userjd.getId();
            
            
        }
        //卫生室
        else if(groupid.equals("3"))
        {
            
            //根据单位名称查询单位的信息,查不到就报错
            Useryy useryy=this.findUseryyByMc(sysmc);
            if(useryy==null)
            {
                //throw new Exception("单位名称输入错误");
                //使用系统自定义的异常类
                ResultInfo resultInfo=new ResultInfo();
                resultInfo.setType(ResultInfo.TYPE_RESULT_FAIL);
                resultInfo.setMessage("单位名称输入错误");
                throw new ExceptionResultInfo(resultInfo);
            }
            userid=useryy.getId();
        }
        else if(groupid.equals("4")){
            Usergys usergys=this.findUsergesByMc(sysmc);
            if(usergys==null)
            {
                //throw new Exception("单位名称输入错误");
                //使用系统自定义的异常类
                ResultInfo resultInfo=new ResultInfo();
                resultInfo.setType(ResultInfo.TYPE_RESULT_FAIL);
                resultInfo.setMessage("单位名称输入错误");
                throw new ExceptionResultInfo(resultInfo);
            }
            userid=usergys.getId();
        }
        sysuserCustom.setId(UUIDBuild.getUUID());
        sysuserCustom.setSysid(userid);
        //把数据插入到数据库
        //刚开始的时候感到很奇怪,就是在SysuserMapper.xml中写着输入的参数是yycg.base.pojo.po.Sysuser
        //但是我这里输入的是sysuserCustom,明显不是Sysuser类型啊,其实是没有关系的,因为,这里的sysuserCustom
        //继承了Sysuser.所以Sysuser里面有的,在SysuserCustom里面都是有的的。
        sysuserMapper.insert(sysuserCustom);
        
    }

如果账号重复了:执行了

 

 ResultInfo resultInfo=ResultUtil.createFail(Config.MESSAGE, 213, null);
            new ResultUtil().throwExcepion(resultInfo);这样就触发了异常。
我们这个Action是用@ResponseBody注释的。那么正常情况下我们应该是要返回json数据的。
但是这个抛异常了,不是正常情况了,但是我们也要返回json数据。所以有了
        if(responseBody!=null)
        {
            //这里说明发生异常的Action是我们本来正常情况下要返回JSON数据的,我们在这里做的处理时是把这个错误拿出来我们自己转成json
          //再输出到页面上

return this.resolveJsonException(request, response, handlerMethod, ex);
}

那些没有用@ResponseBody注释的Action方法,他们的异常处理就按照正规思路处理了。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值