在编写代码时,需要对异常进行处理。进行异常处理的普通的代码是try...catch结构。但在开发业务时,只想关注业务正常的代码,对于catch语句中的捕获异常,希望交给异常捕获来处理,不单独在每个方法中编写,这样不仅可以减少冗余代码,还可以减少因忘记写catch而出现错误的概率。
Spring正好提供了一个非常方便的异常处理方案——控制器通知 (@ControllerAdvice 或 @RestControllerAdvice),它将所有控制器作为一 个切面,利用切面技术来实现。
通过基于@ControllerAdvice或@RestContollerAdvice的注解可以对异常进行全局统一处理,默认对所有的Controller有效。如果要限定生效范围,则可以使用@ControllerAdvice支持的限定范围方式。
可以利用这一特性在一个 系统实现多个异常处理器,然后Controller可以有选择地决定使用哪个,使得异常处理更加灵活、降低侵入性。
@ControllerAdvice注解是Spring3.2提供的新注解,从名字上可以看出大体意思是控制器通知。@ControllerAdvice注解主要功能有:
@ModelAttribute注解:在控制器方法被执行前,对所有Controller的Model添加属性进行操作。
【示例】使用@ControllerAdvice注解和@ModelAttribute注解实现全局数据绑定。
(1)创建GlobalController类(全局控制器)。
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ModelAttribute;
/**
* 全局控制器
* @author pan_junbiao
**/
@ControllerAdvice
public class GlobalController
{
@ModelAttribute
public void addBolgInfo(Model model)
{
model.addAttribute("blogName","pan_junbiao的博客");
model.addAttribute("blogUrl","https://blog.csdn.net/pan_junbiao");
model.addAttribute("blogInfo","您好,欢迎访问 pan_junbiao的博客");
}
}
(2)创建BlogController类(博客信息控制器)。
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
/**
* 博客信息控制器
* @author pan_junbiao
**/
@Controller
public class BlogController
{
/**
* 获取博客信息
*/
@RequestMapping("/blogInfo")
public String blogInfo()
{
return "blog-info.html";
}
}
(3)创建blog-info.html页面,并使用Thymeleaf绑定全局数据。
博客信息
执行结果:
@InitBinder注解:对表单数据进行绑定,用于定义控制器参数绑定规则。如转换规则、格式化等。可以通过这个注解的方法得到WebDataBinder对象,它在参数转换之前被执行。
【示例】使用@ControllerAdvice注解和@InitBinder注解现全局数据预处理。
(1)修改GlobalController类(全局控制器)。
import org.springframework.beans.propertyeditors.CustomDateEditor;
import org.springframework.ui.Model;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.InitBinder;
import org.springframework.web.bind.annotation.ModelAttribute;
import java.util.Date;
import java.text.SimpleDateFormat;
/**
* 全局控制器
* @author pan_junbiao
**/
@ControllerAdvice
public class GlobalController
{
@InitBinder
public void globalInitBinder(WebDataBinder binder)
{
SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
dateFormat.setLenient(false);
binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
}
@ModelAttribute
public void addBolgInfo(Model model)
{
model.addAttribute("blogName","pan_junbiao的博客");
model.addAttribute("blogUrl","https://blog.csdn.net/pan_junbiao");
model.addAttribute("blogInfo","您好,欢迎访问 pan_junbiao的博客");
}
}
(2)修改控制器中的获取博客信息方法,添加日期参数。
/**
* 获取博客信息
* @author pan_junbiao
*/
@RequestMapping("/blogInfo")
public String blogInfo(Date date,Model model)
{
model.addAttribute("date",date);
return "blog-info.html";
}
(3)修改blog-info.html页面,显示转换后的日期信息。
博客信息
执行结果:
@ExceptionHandler注解:定义控制器发生异常后的操作,可以拦截所有控制器发生的异常。统一异常处理 ,通过@ExceptionHandler(value = Exception.class) 来指定捕获的异常。“@ControllerAdvice + @ExceptionHandle" 可以处理除“404”
以外的运行异常。
【示例】使用@ControllerAdvice注解和@ExceptionHandler注解现全局异常处理。
(1)创建BusinessException类(自定义业务异常),继承RuntimeException类。
/**
* 自定义业务异常
* 注意:如果继承的是Exception类,那么Spring的事务管理将会失效,
* 只有继承RuntimeException类才使Spring的事务管理不会失效
* @author pan_junbiao
**/
public class BusinessException extends RuntimeException
{
private String errorMessage; //异常信息
public BusinessException(String errorMessage)
{
super(errorMessage);
this.errorMessage = errorMessage;
}
public String getErrorMessage()
{
return errorMessage;
}
public void setErrorMessage(String errorMessage)
{
this.errorMessage = errorMessage;
}
}
(2)创建BusinessExceptionHandler类(全局异常处理器)。
import com.pjb.pms.entity.DBEntity.StaffInfo;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authz.UnauthorizedException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.servlet.ModelAndView;
/**
* 全局异常处理器
* @author pan_junbiao
**/
@ControllerAdvice
public class BusinessExceptionHandler
{
//日志对象
private Logger logger = LogManager.getLogger(BusinessExceptionHandler.class);
/**
* 自定义业务异常处理
*/
@ExceptionHandler(BusinessException.class)
public ModelAndView businessExceptionHandler(BusinessException ex)
{
//错误信息
String errorMessage = ex.getErrorMessage();
//记录异常日志
logger.error("[业务异常]" + errorMessage);
//返回错误页面
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("errorMessage",errorMessage);
modelAndView.setViewName("error.html");
return modelAndView;
}
/**
* Shiro无权限异常
*/
@ExceptionHandler(UnauthorizedException.class)
public ModelAndView unauthorizedExceptionHandler(UnauthorizedException ex)
{
//获取当前登录人
StaffInfo currentSaff = (StaffInfo) SecurityUtils.getSubject().getPrincipal();
//记录异常日志
String logMeg = String.format("[403无权限] 操作人:%s;异常信息:%s;",currentSaff.getStaffName(),ex.toString());
logger.error(logMeg);
//返回403无权限页面
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("403.html");
return modelAndView;
}
/**
* 系统异常
*/
@ExceptionHandler(Exception.class)
public ModelAndView exceptionHandler(Exception ex)
{
//记录异常日志
logger.error("[系统异常]" + ex.toString());
//返回错误页面
ModelAndView modelAndView = new ModelAndView();
modelAndView.addObject("errorMessage","系统错误,请联系管理员");
modelAndView.setViewName("error.html");
return modelAndView;
}
}
(3)修改控制器中的获取博客信息方法,让其抛出BusinessException自定义业务异常。
/**
* 获取博客信息
* @author pan_junbiao
*/
@RequestMapping("/blogInfo")
public String blogInfo(Date date,Model model)
{
boolean isException = true;
if(isException)
{
throw new BusinessException("这是自定义异常信息");
}
model.addAttribute("date",date);
return "blog-info.html";
}
(4)创建error.html页面,显示异常信息。
异常页面
异常页面
执行结果: