注解介绍
以下是
@ControllerAdvice
的源码
package org.springframework.web.bind.annotation;
import java.lang.annotation.Annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;
import org.springframework.stereotype.Component;
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Component
public @interface ControllerAdvice {
//指定一个或多个包的基本路径。@ControllerAdvice将应用于这些基本路径下的所有控制器类。
//另外注意参数名为basePackages,因为别名注解@AliasFor。
@AliasFor("basePackages")
String[] value() default {};
//指定一个或多个包的基本路径。@ControllerAdvice将应用于这些基本路径下的所有控制器类。
//另外注意参数名为value,因为别名注解@AliasFor。
@AliasFor("value")
String[] basePackages() default {};
//value与basePackages作用一致,都是指定包,以下几种写法作用一致,都是指定处理com.example.controller包下的控制器
//@ControllerAdvice(value = {"com.example.controller"}) //可以指定多个包
//@ControllerAdvice(value = "com.example.controller")
//@ControllerAdvice("com.example.controller")
//@ControllerAdvice(basePackages = {"com.example.controller"}) //可以指定多个包
/******************************************************************************************/
//指定一个或多个类,参数是*.class。 @ControllerAdvice将应用于这些类所在的包的所有控制器。
//比如 @ControllerAdvice(basePackageClasses = DemoController.class)
Class<?>[] basePackageClasses() default {};
//指定一个或多个控制器接口或父类,参数是*.class。 @ControllerAdvice将应用于这些接口或父类的所有实现类或子类。
Class<?>[] assignableTypes() default {};
//指定一个或多个注解类,参数是*.class。 @ControllerAdvice将应用于带有这些注解的控制器类。
Class<? extends Annotation>[] annotations() default {};
}
全局异常处理
Controller
在
Controller
中抛一个异常,异常亦可以是在Controller
调用的Service
或其他地方抛出,只要被Controller
调用即可,但是不能被内部捕获处理,否则ControllerAdvice
就不会处理了
import com.example.exception.CustomException;
import org.springframework.web.bind.annotation.*;
@RestController//如果没有@ResponseBody,方法返回值会被视图解析器解析,无法解析就会404
@RequestMapping("/two")
public class DemoController {
@RequestMapping("/d")
public String test3(@RequestParam String d) throws CustomException {
//这里如果是try-catch,ControllerAdvice就不会生效了
if("0".equals(d)){
throw new CustomException("自定义异常");
}
System.out.println(d);
return d;
}
}
自定义异常
public class CustomException extends Exception {
public CustomException(String message) {
super(message);
}
}
ControllerAdvice
@ControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(CustomException.class)
@ResponseBody//不加此注解返回值会被视图解析器解析,无法正常返回字符串
public String toCustomException(CustomException e) {
//如果出现异常,就会被此类处理,然后将下面的返回值响应出去
return "Controller报错,异常信息为: "+e.getMessage();
}
}
全局模型属性
用于向
Model
添加属性,Model
的属性可以通过视图获取并展示到前端
ControllerAdvice
import com.example.controller.DemoController;
import org.springframework.ui.Model;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ModelAttribute;
@ControllerAdvice(basePackageClasses = DemoController.class)
public class GlobalExceptionHandler {
/**
* Model参数是用于在控制器方法中添加模型属性并将它们传递给视图的对象。
* Model参数不是从HTTP请求中获取的,而是在控制器方法被调用时由Spring MVC框架自动创建和传递的。
* Model设置的参数,可以在视图中通过${***}获取,然后展示在thymeleaf上
* */
@ModelAttribute
public void addGlobalAttributes(Model model) {
// 定义全局的数据预处理规则
model.addAttribute("myModel","自定义模型属性值");
//下面是对应一个key多个value
/*
Map<String, String> map = new HashMap<String, String>();
map.put("key1", "value1");
map.put("key2", "value2");
map.put("key3", "value3");
model.addAttribute("myModel",map);
*/
}
/**
对应上面一个key多个value的另一种写法
*/
// @ModelAttribute()//model的属性key为map,就是下面方法里的map名
// //@ModelAttribute("myMap") //model的属性key为自定义的myMap
// public Map<String, String> presetParam(){
// Map<String, String> map = new HashMap<String, String>();
// map.put("key1", "value1");
// map.put("key2", "value2");
// map.put("key3", "value3");
// return map;
// }
}
Controller
@Controller
@RequestMapping("/two")
public class DemoController {
@RequestMapping("/view")
public ModelAndView test4(@RequestParam String d){
System.out.println(d);
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("resultOne");
return modelAndView;
}
//可以通过以下方式获取自定义模型属性值
/*
@RequestMapping("/view")
public ModelAndView test4(@RequestParam String d,Model model){
String e = model.getAttribute("myModel").toString();
System.out.println("输出自定义模型属性值:"+e);
ModelAndView modelAndView = new ModelAndView();
modelAndView.setViewName("resultOne");
return modelAndView;
}
*/
}
视图
记得添加thymeleaf依赖
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-thymeleaf</artifactId>
<version>2.7.2</version> <!-- 与springboot保持一致即可 -->
</dependency>
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta charset="UTF-8">
<title>我的视图</title>
</head>
<body>
<h1>Hello,View</h1>
<p th:text="自定义模型属性值为+${myModel}"></p>
</body>
</html>
效果
访问
http://localhost:8081/two/view?d=123
属性值为map时
全局数据绑定与预处理
在请求参数进入controller前做处理,也可以用自定义参数解析器实现,这里主要是用到了
WebDataBinder
org.springframework.web.bind.WebDataBinder
是Spring MVC框架中的一个类,用于绑定请求参数到Java对象上。
在Web开发中,当客户端发送一个HTTP请求时,请求中通常会包含一些参数。这些参数需要绑定到Java对象中,以便在控制器方法中进行处理。
WebDataBinder
的作用就是将请求参数绑定到Java对象上。它提供了一系列方法,用于配置和自定义数据绑定的规则。
下面是一些WebDataBinder
的常用方法:
registerCustomEditor(Class<?> requiredType, PropertyEditor propertyEditor)
:注册一个自定义的属性编辑器,用于将请求参数的字符串值转换为Java对象的属性类型。registerCustomEditor(Class<?> requiredType, String field, PropertyEditor propertyEditor)
:注册一个自定义的属性编辑器,用于将请求参数的字符串值转换为Java对象的指定字段的属性类型。setValidator(Validator validator)
:设置一个验证器,用于验证绑定到Java对象上的数据。initBeanPropertyAccess()
:初始化绑定的Java对象,以便在后续的数据绑定过程中使用。
下面展示,实现将请求参数
ip:port/test?d=1
换成ip:port/test?d=一
(如果是请求体传json转对象的需求,就用自定义参数解析器吧。。。)
Controller
@Controller
@RequestMapping("/two")
public class DemoController {
@RequestMapping("/s")
@ResponseBody
public String test4(@RequestParam String d){
System.out.println(d);
return d;
}
}
配置
import com.example.view.DemoPropertyEditor;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;
@ControllerAdvice
public class GlobalExceptionHandler {
@InitBinder
public void initBinder(WebDataBinder binder) {
//添加一个将请求参数数字转中文的编辑器
//比如把 ip:port/test?d=1 换成 ip:port/test?d=一
binder.registerCustomEditor(String.class, new DemoPropertyEditor());
}
}
自定义编辑器
import java.beans.PropertyEditorSupport;
public class DemoPropertyEditor extends PropertyEditorSupport {
@Override
public void setAsText(String text) throws IllegalArgumentException {
int value = Integer.parseInt(text);
String chineseValue = convertToChinese(value);
setValue(chineseValue);
}
private String convertToChinese(int value) {
switch (value) {
case 1:
return "一";
case 2:
return "二";
case 3:
return "三";
case 4:
return "四";
default:
throw new IllegalArgumentException("Invalid value: " + value);
}
}
}
验证
访问
http://localhost:8081/two/s?d=1
效果