1: @ControllerAdvice理解
@ControllerAdvice是spring 3.2提供的新注解,他是一个controller增强器,加了@ControllerAdvice的类为那些声明了(@ExceptionHandler、@InitBinder 或 @ModelAttribute注解修饰的)方法的类而提供的专业化的@Component , 以供多个 Controller类所共享。说白了,就是aop思想的一种实现,你告诉我需要拦截规则,我帮你把他们拦下来,具体你想做更细致的拦截筛选和拦截之后的处理,你自己通过@ExceptionHandler、@InitBinder 或 @ModelAttribute这三个注解以及被其注解的方法来自定义。可以对controller中使用到@RequestMapping注解的方法做逻辑处理,用法可分为以下三种:
- 全局异常处理:@ExceptionHandler注解标注的方法:用于捕获Controller中抛出的不同类型的异常,从而达到异常全局处理的目的
- 全局数据绑定:@InitBinder注解标注的方法:用于请求中注册自定义参数的解析,从而达到自定义请求参数格式的目的;
- 全局数据预处理:@ModelAttribute注解标注的方法:表示此方法会在执行目标Controller方法之前执行
注意:这三个注解都可以在普通的Controller类上使用,ControllerAdvice只是作用范围可以自定义(默认全部)
@ControllerAdvice作用范围
ControllerAdvice 提供了多种指定Advice规则的定义方式,默认什么都不写,则是Advice所有Controller,当然你也可以通过下列的方式指定规则
1:指定包
匹配com.example.springbootshiro.controller包及其子包下的所有Controller
@ControllerAdvice(basePackages="com.example.springbootshiro.controller")
当然也可以用数组的形式指定,如:
@ControllerAdvice(basePackages={"com.example.springbootshiro.controller", "com.example.other.controller"}),
2:指点注解
//比如我自定了一个 @CustomAnnotation 注解,我想匹配所有被这个注解修饰的 Controller, 可以这么写:
@ControllerAdvice(annotations={CustomAnnotation.class})
2:@ExceptionHandler全局异常处理
@ControllerAdvice
public class MyGlobalExceptionHandler {
@ExceptionHandler(Exception.class)
public ModelAndView grobalExceptionHandler(Exception e) {
ModelAndView mv = new ModelAndView();
mv.addObject("message", e.getMessage());
mv.setViewName("myerror");
return mv;
}
@ExceptionHandler(CustomException.class)
public ModelAndView customException(Exception e) {
ModelAndView mv = new ModelAndView();
mv.addObject("message", e.getMessage());
mv.setViewName("myerror");
return mv;
}
@ExceptionHandler(MaxUploadSizeExceededException.class)
public void myexcept(MaxUploadSizeExceededException e, HttpServletResponse response){
response.setContentType("text/html;charset=utf-8");
PrintWriter writer = null;
try {
writer = response.getWriter();
} catch (IOException ex) {
ex.printStackTrace();
}
writer.write("文件太大,请重新选择");
writer.flush();
writer.close();
}
}
@ExceptionHandler(Exception.class)表示捕捉没有在代码中try catch的异常
@ExceptionHandler(CustomException.class)表示只捕捉自定义的异常,SurvetException是我自定义的继承Exception的类,如果执行了@ExceptionHandler(CustomException.class)中的代码就不再执行@ExceptionHandler(Exception.class)中的代码
3:预设全局数据 @ModelAttribute
有一些初始数据的操作,我们可以将一些公共的数据定义在添加了@ControllerAdvice类中,这样所有的Controller接口, 都能访问到这些数据,如下:
示例1:
//定义全局数据
@ControllerAdvice
public class MyGlobalExceptionHandler {
//标记该方法的返回数据是一个全局数据,默认key是返回的变量名(下面方法即为 map),value是方法返回值
//可以通过 @ModelAttribute 注解的 name 属性去重新指定 key(下面方法即为 place)。
@ModelAttribute(name = "place")
public Map<String,Object> mydata() {
HashMap<String, Object> map = new HashMap<>();
map.put("city", "广州");
map.put("province", "广东");
return map;
}
}
-----------------------------------------------------------------------------------
//在controller使用全局变量
@RestController
public class HelloController {
@GetMapping("/hello")
public String hello(Model model) {
// 获取数据
Map<String, Object> map = model.asMap();
System.out.println(map);
return "Welcome to here";
}
}
// {place={city=广州, province=广东}
示例2:
// 使用@ModelAttribute可以在controller请求前存入数据
// 1.无返回值方法,放入Model,自定义 key ,value
@ModelAttribute()
public void presetParam(Model model) {
model.addAttribute("globalAttr", "我是全局参数");
}
// 2.不有指定name,返回值方法,返回值是map,int等,key就是map,int等,,value是返回值
@ModelAttribute()
public Map<String, String> presetParam2() {
Map<String, String> map1 = new HashMap<String, String>();
map1.put("key1", "value1");
return map1;
}
// 3.指定name,返回值方法,key就是name,value是返回值
@ModelAttribute(name = "map2")
public Map<String, String> presetParam3() {
Map<String, String> map = new HashMap<String, String>();
map.put("key2", "value2");
return map;
}
// 4.可以接受请求参数
@ModelAttribute()
public void presetParam4(@RequestParam("name") String name,Model model) {
model.addAttribute("name", name);
}
-----------------------------------------------------------------------------------
//1.使用Model取出
@GetMapping("model")
public String methodOne(Model model) {
Map<String, Object> modelMap = model.asMap();
System.out.println(modelMap.get("name").toString()); // 传入name的值
return modelMap.get("globalAttr").toString();
}
//2.使用ModelMap取出
@GetMapping("modelMap")
public String methodThree(ModelMap modelMap) {
return modelMap.get("map").toString();
}
//3.@ModelAttribute()指定key,直接取出
@GetMapping("modelAttribute")
public String methodTwo(@ModelAttribute("map2") Map map2) {
return map2.toString();
}
4:请求参数预处理 @InitBinder
1:考虑我有两个实体类,Book 和 Author,分别定义如下:
@Data
public class Book {
private String name;
private Long price;
}
@Data
public class Author {
private String name;
private Integer age;
}
2:访问接口添加操作就会有问题,因为两个实体类都有一个 name 属性,从前端传递时 ,无法区分
@PostMapping("/book")
public void addBook(Book book, Author author) {
System.out.println(book);
System.out.println(author);
}
3:此时,通过 @ControllerAdvice 的全局数据预处理可以解决这个问题,@ModelAttribute给接口中的变量取别名
@PostMapping("/book")
public void addBook(@ModelAttribute("b") Book book, @ModelAttribute("a") Author author) {
System.out.println(book);
System.out.println(author);
}
4:进行请求数据预处理,在 @ControllerAdvice 标记的类中添加如下代码:
public class InitController {
@InitBinder("b")
public void b(WebDataBinder binder) {
binder.setFieldDefaultPrefix("b.");
}
@InitBinder("a")
public void a(WebDataBinder binder) {
binder.setFieldDefaultPrefix("a.");
}
}
@InitBinder(“b”) 注解表示该方法用来处理和Book和相关的参数,在方法中,给参数添加一个 b 前缀,即请求参数要有b前缀。请求发送时,通过给不同对象的参数添加不同的前缀,可以实现参数的区分.url=http://localhost:8080/book?a.name=张三&a.age=18&b.name=三国演义&b.price=88