后端项目分层一般如下:
控制层:controller
业务层:service
数据访问层:dao
下面演示在实际项目中,移除统一处理流程:
这篇文章的处理原则:controller负责异常统一处理,业务层负责把异常抛至控制层
dao层:
该层直接与数据库交互,而dao层由service调用,因此该层异常在service中处理即可
service层
业务层数据牵扯到业务处理流程,所有很容易出现异常,在方法后统一抛出一个Exception的方式。(对于某些代码明确其异常(或需自定义异常)时,可以使用try...catch来处理)
controller层
控制层来接收业务层处理的结果,当业务层出现异常且通过抛出异常的方式,那么异常会交由控制层处理,在控制层中,一旦出现异常,需要在该层处理,不能再往外抛,否则能影响服务器性能。
案例:
模拟一个service----controller流程,来处理异常,如下:
1、service层:
接口层:模拟一个testException方法,并在接口后抛出统一的异常Exception
package com.shuizhu.service;
import java.util.Map;
/**
* @author 睡竹
* @date 2023/3/20
*/
public interface TestService {
Map<String,Object> testException() throws Exception;
}
接口实现层:
package com.shuizhu.service.impl;
import com.shuizhu.service.TestService;
import org.springframework.stereotype.Service;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @author 睡竹
* @date 2023/3/20
*/
@Service
public class TestExceptionImpl implements TestService {
@Override
public Map<String, Object> testException() throws Exception{
//1/0 会出现异常,当出现异常时,会抛至controller层进行统一处理
int i = 1/0;
Map<String,Object> map = new LinkedHashMap();
map.put("result",i);
return map;
}
}
2、前后端统一响应的工具类
package com.shuizhu.utils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import java.util.HashMap;
import java.util.Map;
/**
* 返回数据
*
* @author 睡竹
*/
public class R extends HashMap<String, Object> {
private static final long serialVersionUID = 1L;
private static final Integer METHOD_INDEX = 1;
private static final Logger logger= LogManager.getLogger();
public R() {
put("code", 0);
put("msg", "success");
}
//测试获取的code
public Integer getCode() {
return (Integer) this.get("code");
}
public static R error() {
return error(500, "未知异常,请联系管理员");
}
public static R error(String msg) {
return error(500, msg);
}
public static R error(int code, String msg) {
R r = new R();
r.put("code", code);
r.put("msg", msg);
return r;
}
public static R errorApi(Exception e) {
/** 通过getStackTrace获取controller中的方法名称 */
StackTraceElement[] stackTrace = new Throwable().getStackTrace();
String method = "";
if (stackTrace.length > 0) {
/** 获取方法名称 */
method = stackTrace[METHOD_INDEX].getMethodName();
}
/** 把错误日志打印至控制台,按文本形式可以提高系统性能 */
logger.error(method + "接口异常->",e);
/**
* 日志输出格式:"方法名称 接口异常-> 异常重要的信息"
* 前提:接口名称需要与controller方法名称一致
*/
return error(method + "接口异常->" + e.getMessage());
}
public static R ok(String msg) {
R r = new R();
r.put("msg", msg);
return r;
}
public static R ok(Map<String, Object> map) {
R r = new R();
r.putAll(map);
return r;
}
public static R ok() {
return new R();
}
@Override
public R put(String key, Object value) {
super.put(key, value);
return this;
}
}
3、controller层
package com.shuizhu.controller;
import com.shuizhu.service.TestService;
import com.shuizhu.utils.R;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import javax.annotation.Resource;
import java.util.Map;
/**
* @author 睡竹
* @date 2023/3/20
*/
@RestController
public class TestController {
@Resource
TestService testService;
@RequestMapping("/testException")
public R testException(){
try {
Map<String, Object> map = testService.testException();
return R.ok(map);
} catch (Exception e) {
return R.errorApi(e);
}
}
}
4、测试
接口为:http://127.0.0.1:8080/testException

接口返回了异常中的getMessage()信息,再来看控制台打印日志:

上述日志是通过logger进行日志打印输出的
下面改下代码,直接打印堆栈信息至控制台(这种方式不能出现在生产环境中):
try {
Map<String, Object> map = testService.testException();
return R.ok(map);
} catch (Exception e) {
e.printStackTrace();//直接打印堆栈信息,会影响性能
return R.error(e.getMessage());
}
可以再控制台中看到明显的区别:
