JavaWeb统一异常处理
[toc]
处理方案
1. xml文件配置方式
web.xml配置
<error-page>
<error-code>404</error-code>
<location>/404</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/500</location>
</error-page>
<!-- 未捕获的错误,同样可指定其它异常类,或自定义异常类 -->
<error-page>
<exception-type>java.lang.Exception</exception-type>
<location>/uncaughtException</location>
</error-page>
springmvc(applicationContext.xml)配置
<mvc:view-controller path="/400" view-name="400"/>
<mvc:view-controller path="/404" view-name="404"/>
<mvc:view-controller path="/500" view-name="500"/>
<mvc:view-controller path="/uncaughtException" view-name="uncaughtException"/>
SimpleMappingExceptionResolver
<bean class="org.springframework.web.servlet.handler.SimpleMappingExceptionResolver">
<property name="exceptionMappings">
<props>
<!-- 异常类名,可以是全路径,错误页面或Controller路径! -->
<prop key="NullPointerException">NullPointerException</prop>
<prop key="IOException">IOException</prop>
</props>
</property>
<!-- 表示当抛出异常但没有在exceptionMappings里面找到对应的异常时 返回名叫exception的视图-->
<property name="defaultErrorView" value="exception"/>
<!-- 定义在发生异常时视图跟返回码的对应关系 -->
<property name="statusCodes">
<props>
<prop key="number">500</prop>
<prop key="null">503</prop>
</props>
</property>
<property name="defaultStatusCode" value="404"/>
</bean>
2.代码拦截
自定义异常。
作统一异常处理。
自定义非检测异常,继承RuntimeException类,直接中断处理,不需要显示的捕捉和处理。例如参数错误等等无需进一步处理和恢复。
自定义检测异常, 继承Exception类,需要显示的捕捉或抛出。
public class BaseRuntimeException extends RuntimeException {
private String message; //异常信息
private Object[] args; //相关参数等
public BaseRuntimeException(String message) {
super(message);
this.message = message;
}
public BaseRuntimeException(String message, Throwable cause) {
super(message, cause);
this.message = message;
}
public BaseRuntimeException(String message, Object ... args) {
super(message);
this.message = message;
this.args = args;
}
public BaseRuntimeException(String message, Throwable cause, Object ... args) {
super(message, cause);
this.message = message;
this.args = args;
}
public String getMessage() {
return message;
}
public Object[] getArgs() {
return args;
}
}
自定义异常处理器。
1.基于spring mvc 实现HandlerExceptionResolver接口
public class GlobalHandlerExceptionResolver implements HandlerExceptionResolver {
private static final Logger LOG = LoggerFactory.getLogger(GlobalHandlerExceptionResolver.class);
@Override
public ModelAndView resolveException(HttpServletRequest req, HttpServletResponse resp, Object o, Exception ex) {
ex.printStackTrace();
if (ex instanceof BaseRuntimeException) {
printWrite(ex.getMessage, resp);
} else {
printWrite("ERROR!", resp);
}
return new ModelAndView();
}
/**
* 将错误信息写入到response中
* @param messsage
* @param response
* @throws IOException
*/
public static void printWrite(String messsage, HttpServletResponse response) {
try {
PrintWriter pw = response.getWriter();
pw.write(messsage);
pw.flush();
pw.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
2. 注册异常处理器
<bean class="com.iot.learnssm.firstssm.exception.CustomExceptionResolver"></bean>
自定义拦截器
由于HandlerExceptionResolver接口只实现返回值为ModelAndView,前后端分离的话,可以采用拦截器HandlerInterceptor或者Filter。(springmvc还可以采用@ExceptionHandler注解来处理局部异常(注解应用于方法上),此处不多谈)
1. 基于spring mvc 实现HandlerInterceptor接口
public interface HandlerInterceptor {
//处理前,返回ture 进入postHandle, 返回false 进入afterCompletion
boolean preHandle(HttpServletRequest var1, HttpServletResponse var2, Object var3) throws Exception;
//进入控制器
void postHandle(HttpServletRequest var1, HttpServletResponse var2, Object var3, ModelAndView var4) throws Exception;
//执行完控制器后调用
void afterCompletion(HttpServletRequest var1, HttpServletResponse var2, Object var3, Exception var4) throws Exception;
}
public class MVCRequestInterceptor implements HandlerInterceptor {
@Override
public boolean preHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o) throws Exception {
return true;
}
@Override
public void postHandle(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, ModelAndView modelAndView) throws Exception {
httpServletResponse.setHeader("Access-Control-Allow-Methods","GET,POST,PUT,DELETE");
httpServletResponse.setHeader("Access-Control-Allow-Credentials","true");
try {
chain.doFilter(request, httpServletResponse);
} catch (BaseRuntimeException e) {
e1.printStackTrace();
printMessage(httpServletResponse, e.getMessage);
} catch (Exception e){
e1.printStackTrace();
printMessage(httpServletResponse, ExceptionUtils.getStackTrace(e));
}
}
@Override
public void afterCompletion(HttpServletRequest httpServletRequest, HttpServletResponse httpServletResponse, Object o, Exception e) throws Exception {
}
private void printMessage(httpServletResponse response, String message){
PrintWriter out = httpServletResponse.getWriter();
Map<String,Object> map = new HashMap<String,Object>();
map.put("success", false);
map.put("msg", message);//获取详细错误信息
out.println(JSONObject.toJSONString(map));
}
}
2. 注册拦截器
<!-- 注册拦截器 -->
<mvc:interceptors>
<bean class="com.project.base.interceptor.ControlInterceptor" />
</mvc:interceptors>
自定义Filter拦截器
1. 选择缘由
- 框架由Struts2 改为 spring mvc,原代码并未改动,为了兼容两种控制器,实现javax.servlet的Filter接口,进行错误统一处理。
2.实现Filter 接口
@Component
public class GlobeExceptionFilter extends GenericFilterBean {
/*采用DelegatingFilterProxy代理管理自定义filter,可直接注入该service
@Autowired
private SysLoginLogService sysLoginLogService;
*/
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
HttpServletResponse httpServletResponse = (HttpServletResponse) servletResponse;
httpServletResponse.setHeader("Access-Control-Allow-Origin","*");
httpServletResponse.setHeader("Access-Control-Allow-Methods","GET,POST,PUT,DELETE");
httpServletResponse.setHeader("Access-Control-Allow-Credentials","true");
try {
filterChain.doFilter(servletRequest, httpServletResponse);
int httpStatus = httpServletResponse.getStatus();
if(httpStatus >=400){ //请求错误
HttpServletRequest request = (HttpServletRequest) servletRequest;
String requestURI = request.getRequestURI();
String queryString = request.getQueryString();
System.out.println("------>"+requestURI);
System.out.println("--------->"+queryString);
// throw new BaseRuntimeException("error", requestURI, queryString); //用于测试
}
} catch (BaseError e){ //内部自定义错误
System.out.println(e.getMessage());
System.out.println(Arrays.toString(e.getArgs()));
} catch (BaseRuntimeException e){ //内部自定义异常
System.out.println("messsage--->" +e.getMessage());
System.out.println("args--->" +Arrays.toString(e.getArgs()));
//追踪栈上信息
StackTraceElement[] st = e.getStackTrace();
if(st.length > 0){
String exclass = st[0].getClassName();
String method = st[0].getMethodName();
System.out.println("calss----->" + exclass); //发生异常类
System.out.println("method----->" + method); //方法
System.out.println("method----->" + lineNumber); //行数
}
printMessage(httpServletResponse, e.getMessage());
} catch (Exception e){ //其他异常
e.printStackTrace();
printMessage(httpServletResponse, e.getMessage());
}
}
private void printMessage(HttpServletResponse httpServletResponse, String message){
httpServletResponse.setContentType("application/json;charset=UTF-8");
PrintWriter out = null;
try {
out = httpServletResponse.getWriter();
Map<String,Object> map = new HashMap<String,Object>();
map.put("success", false);
map.put("msg", message);
out.println(JSONObject.toJSONString(map));
out.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if(out != null){
out.close();
}
}
}
}
3. xml配置
通过spring管理filter
采用DelegatingFilterProxy代理管理自定义filter ,filter内部可直接使用spring bean直接注入。
<filter>
<filter-name>globeExceptionFilter</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
<init-param>
<!-- 该值缺省为false,表示生命周期由SpringApplicationContext管理,设置为true则表示由servlet container管理 -->
<param-name>targetFilterLifecycle</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>globeExceptionFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Spring中注册filter
<!-- 全局异常处理 -->
<bean id="globeExceptionFilter" class="com.project.base.filter.GlobeExceptionFilter" />