关于java:SpringMVC-解析三-Controller-注解

4次阅读

共计 16040 个字符,预计需要花费 41 分钟才能阅读完成。

我在后面的文章中介绍了 Spring MVC 最外围的组件 DispatcherServlet,DispatcherServlet 把 Servlet 容器 (如 Tomcat) 中的申请和 Spring 中的组件分割到一起,是 SpringWeb 利用的枢纽。然而咱们在日常开发中往往不须要具体晓得枢纽的作用,咱们只须要解决枢纽分发给咱们的申请。Spring 中解决申请业务逻辑最常见的组件是 Controller,本文会对 Spring 的 Controller 及相干组件做具体介绍。

Controller 的定义

Controller 是 Spring 中的一个非凡组件,这个组件会被 Spring 辨认为能够承受并解决网页申请的组件。Spring 中提供了基于注解的 Controller 定义形式:@Controller 和 @RestController 注解。基于注解的 Controller 定义不须要继承或者实现接口,用户能够自在的定义接口签名。以下为 Spring Controller 定义的示例。

@Controller
public class HelloController {@GetMapping("/hello")
    public String handle(Model model) {model.addAttribute("message", "Hello World!");
        return "index";
    }
}

@Controller 注解继承了 Spring 的 @Component 注解,会把对应的类申明为 Spring 对应的 Bean,并且能够被 Web 组件治理。@RestController 注解是 @Controller 和 @ResponseBody 的组合,@ResponseBody 示意函数的返回不须要渲染为 View,应该间接作为 Response 的内容写回客户端。

映射关系 RequestMapping

门路的定义

定义好一个 Controller 之后,咱们须要将不同门路的申请映射到不同的 Controller 办法之上,Spring 同样提供了基于注解的映射形式:@RequestMapping。通常状况下,用户能够在 Controller 类和办法下面增加 @RequestMapping 注解,Spring 容器会辨认注解并将满足门路条件的申请调配到对应的办法进行解决。在上面的示例中,”GET /persons/xxx” 会调用 getPerson 办法解决。

@RestController
@RequestMapping("/persons")
class PersonController {@GetMapping("/{id}")
    public Person getPerson(@PathVariable Long id) {// ...}

    @PostMapping
    @ResponseStatus(HttpStatus.CREATED)
    public void add(@RequestBody Person person) {// ...}
}

门路的匹配

Spring 反对两种门路匹配形式,二者之间能够很好的兼容,Spring 默认应用 PathPattern 进行门路的匹配。

  1. PathPattern:应用预解析的办法匹配门路。专门为 Web 门路匹配而设计,能够反对简单的表达式,执行效率很高。
  2. AntPathMatcher:Spring 中用于类门路、文件系统和其它资源的解决方案,效率比拟低。

PathPattern 根本能够向下兼容 AntPathMatcher 的逻辑,并且反对门路变量和 ”**” 多段门路匹配,以下列出几种 PathPattern 的示例:

门路示例 阐明
/resources/ima?e.png 门路中有一个字符是可变的,如 /resources/image.png
/resources/*.png 门路中多个字符是可变的,如 /resources/test.png
/resources/** 门路中多段可变,如 /resources/test/path/xxx
/projects/{project}/versions 匹配一段门路,并且把门路中的值提取进去,如 /projects/MyApp/versions
/projects/{project:[a-z]+}/versions 匹配一段合乎正则表达式门路,并且把门路中的值提取进去,如 /projects/myapp/versions

门路中匹配到的变量能够应用 @PathVariable 获取,Path 变量能够是办法或者类级别的,匹配到的变量会主动进行类型转换,如果转换失败则会抛出异样。

@GetMapping("/owners/{ownerId}/pets/{petId}")
public Pet findPet(@PathVariable Long ownerId, @PathVariable Long petId) {// ...}

@Controller
@RequestMapping("/owners/{ownerId}")
public class OwnerController {@GetMapping("/pets/{petId}")
    public Pet findPet(@PathVariable Long ownerId, @PathVariable Long petId) {// ...}
}

门路抵触

当一次申请匹配到多个 Pattern,那么就须要选出最靠近的 Pattern 门路。Spring 为 Pattern 和 AntPathMatcher 提供了抉择最靠近的门路策略,二者之间逻辑相近,此处只介绍 PathPattern。对于 PathPattern,Spring 提供了 PathPattern.SPECIFICITY_COMPARATOR 用于比照门路之间的优先级,比照的规定如下:

  1. null 的 pattern 具备最低优先级。
  2. 蕴含通配符的 pattern 的具备最低优先级(如 /**)。
  3. 如果两个 pattern 都蕴含通配符,长度比拟长的有更高的优先级。
  4. 蕴含越少匹配符号和越少门路变量的 pattern 有越高的优先级。
  5. 门路越长的优先级越高。

Spring 5.3 之后不再反对.* 后缀匹配,默认状况下“/person”就会匹配到所有的“/person.*”

承受和返回参数的类型

RequestMapping 还能够指定接口承受什么类型的参数以及返回什么类型的参数,这通常会在申请头的 Content-Type 中指定:

@PostMapping(path = "/pets", consumes = "application/json") 
public void addPet(@RequestBody Pet pet) {// ...}

@GetMapping(path = "/pets/{petId}", produces = "application/json") 
@ResponseBody
public Pet getPet(@PathVariable String petId) {// ...}

依据参数或 Header 抉择

RequestMapping 还反对依照申请的参数或者 Header 判断是否解决申请。

  • 如只承受参数 myParam 的值为 myValue 的状况,能够通过如下形式指定:

    @GetMapping(path = "/pets/{petId}", params = "myParam=myValue") 
    public void findPet(@PathVariable String petId) {// ...}
  • 如只承受申请头中 myParam 的值为 myValue 的状况,能够通过如下形式指定:

    @GetMapping(path = "/pets", headers = "myHeader=myValue") 
    public void findPet(@PathVariable String petId) {// ...}

编程式注册 RequestMapping

咱们后面的教程中讲的都是怎么通过 @RequestMapping 进行门路的映射,应用这种形式会主动把门路映射为增加了注解的办法。这种形式尽管应用很不便,然而灵活性方面有一些欠缺,如果我想要依据 Bean 的配置信息动静映射门路之间的关系时,注解的形式就无奈做到这种需要。Spring 提供了一种动静注册 RequestMapping 的办法,注册示例如下所示:

@Configuration
public class MyConfig {

    // 从容器中获取保护映射关系的 RequestMappingHandlerMapping 和自定义组件 UserHandler
    @Autowired
    public void setHandlerMapping(RequestMappingHandlerMapping mapping, UserHandler handler) 
            throws NoSuchMethodException {

        // 生成门路匹配信息
        RequestMappingInfo info = RequestMappingInfo
                .paths("/user/{id}").methods(RequestMethod.GET).build(); 

        // 获取须要映射的办法
        Method method = UserHandler.class.getMethod("getUser", Long.class); 

        // 注册门路和办法之间的映射信息
        mapping.registerMapping(info, handler, method); 
    }
}

解决办法

通过 RequestMapping 映射通常能够把顺次申请映射到某个办法,这个办法就是解决办法(Handler Methods)。解决办法的参数和返回值能够应用很多申请中的信息 (如 @RequestParam, @RequestHeader) 等,这些参数反对应用 Optional 进行封装。

办法参数 阐明
WebRequest, NativeWebRequest 蕴含了申请参数、申请和 Session 信息,次要用于 Spring 框架外部解析参数等操作
javax.servlet.ServletRequest, javax.servlet.ServletResponse Servlet 的申请和参数信息
javax.servlet.http.HttpSession 申请的 Session 信息
javax.servlet.http.PushBuilder 服务器推送是 HTTP/ 2 协定中的新个性之一,旨在通过将服务器端的资源推送到浏览器的缓存中来预测客户端的资源需要,以便当客户端发送网页申请并接管来自服务器的响应时,它须要的资源曾经在缓存中。这是一项进步网页加载速度的性能加强的性能。在 Servlet 4.0 中,服务器推送性能是通过 PushBuilder 实例公开的,此实例是从 HttpServletRequest 实例中获取的。
java.security.Principal 以后用户的登录信息
HttpMethod 申请的形式,如 GET,POST 等
java.util.Locale 申请中的国际化信息
java.util.TimeZone + java.time.ZoneId 申请的时区信息
java.io.InputStream, java.io.Reader 用于获取申请原始 Body 的输出流
java.io.OutputStream, java.io.Writer 用于写回响应的输入流
@PathVariable 门路变量,如 ”/pets/{petId}” 中的 petId
@MatrixVariable 用分号宰割的参数,如 GET /pets/42;q=11;r=22
@RequestParam 获取申请中的参数,蕴含 multipart 类型的文件
@RequestHeader 申请头信息
@CookieValue 申请中的 Cookie 信息
@RequestBody 把申请的 Body,会应用 HttpMessageConverter 转为指定的类型的数据。
HttpEntity\<B> 相似于 @RequestBody
@RequestPart 用于获取 multipart/form-data 中的数据
java.util.Map, org.springframework.ui.Model, org.springframework.ui.ModelMap 获取用于渲染 HTML 视图的参数
@ModelAttribute 用于获取模型中的属性
Errors, BindingResult 获取参数校验后果信息
SessionStatus + class-level @SessionAttributes Session 信息
UriComponentsBuilder 获取匹配过程中的参数信息
@SessionAttribute 获取一个 Session 属性
@RequestAttribute 获取申请中的属性

解决办法也能够反对很多类型的返回值,不同类型的返回有不同的意义。

返回参数 阐明
@ResponseBody @RestController 就蕴含了这个注解,这个注解示意应用 HttpMessageConverter 把返回值写入 Response,不会进行视图解析
HttpEntity\<B>, ResponseEntity\<B> 和 @ResponseBody 相似,返回值间接写入 Response
HttpHeaders 只返回 Header 不返回 body
String 依照返回值去查找 View,并解析为模型
View 返回一个视图
java.util.Map, org.springframework.ui.Model 用于渲染视图的模型,View 由 RequestToViewNameTranslator 决定
@ModelAttribute 用于渲染视图的模型,View 由 RequestToViewNameTranslator 决定
ModelAndView 返回一个可用的模型视图
void 通常示意没有返回 Body
DeferredResult\<V> 异步返回后果,后文具体介绍
Callable\<V> 异步返回后果,后文具体介绍
ListenableFuture\<V>, java.util.concurrent.CompletionStage\<V>, java.util.concurrent.CompletableFuture\<V> 相似于 DeferredResult,异步返回调用后果
ResponseBodyEmitter, SseEmitter 异步的把 HttpMessageConverter 转换后的 Body 写入 Response
StreamingResponseBody 把返回异步写入 Response
Reactive types — Reactor, RxJava, or others through ReactiveAdapterRegistry Flux 场景下的异步返回

类型转换

网络申请的参数往往是 String 类型的,而映射到后端时须要转为解决办法须要的数据类型(如 @RequestParam, @RequestHeader,@PathVariable,@MatrixVariable 和 @CookieValue)。这种状况下 Spring 会获取容器内的类型转换服务和属性编辑器进行转换,用户也能够向 WebDataBinder 中注入本人须要的转换服务。

Matrix 参数

Matrix 参数其实时 RFC3986 中对于 Url 编码的一些标准,Matrix 参数之间用分号宰割,Matrix 参数的多个值之间用逗号宰割,例如/cars;color=red,green;year=2012,多个值之间也容许用分号宰割,如color=red;color=green;color=blue

如果一个 URL 须要蕴含 Matrix 参数,那么蕴含 Matrix 参数应该是一个门路变量,否则 Matrix 参数会对门路匹配造成影响:

// GET /pets/42;q=11;r=22

// 最初一段门路必须为门路变量{petId},否则会造成门路匹配失败
@GetMapping("/pets/{petId}")
public void findPet(@PathVariable String petId, @MatrixVariable int q) {

    // petId == 42
    // q == 11
}

不仅仅 URL 最初一段能够加 Matrix 参数,URL 的任意一段都能够家 Matrix 参数,如下所示:

// GET /owners/42;q=11/pets/21;q=22

@GetMapping("/owners/{ownerId}/pets/{petId}")
public void findPet(@MatrixVariable(name="q", pathVar="ownerId") int q1,
        @MatrixVariable(name="q", pathVar="petId") int q2) {

    // q1 == 11
    // q2 == 22
}

Matrix 参数容许设置默认值,用户没有传该参数的时候应用这个默认值:

// GET /pets/42

@GetMapping("/pets/{petId}")
public void findPet(@MatrixVariable(required=false, defaultValue="1") int q) {// q == 1}

如果门路中蕴含很多 Matrix 参数,一个一个接管可能比拟麻烦,咱们能够通过 MultiValueMap 用汇合的模式去接管:

// GET /owners/42;q=11;r=12/pets/21;q=22;s=23

@GetMapping("/owners/{ownerId}/pets/{petId}")
public void findPet(
        @MatrixVariable MultiValueMap<String, String> matrixVars,
        @MatrixVariable(pathVar="petId") MultiValueMap<String, String> petMatrixVars) {// matrixVars: ["q" : [11,22], "r" : 12, "s" : 23]
    // petMatrixVars: ["q" : 22, "s" : 23]
}

如果你须要在程序中应用 Matrix 参数,须要的配置 UrlPathHelperremoveSemicolonContent=false

@RequestParam

@RequestParam 用于把申请中的参数 (查问参数或者表单参数) 绑定到对应的办法参数上,默认状况下不容许申请参数中不蕴含指定的参数,不过用户能够指定 required=false 去容许设置申请参数到对应的办法参数。如果办法的参数类型不为 String 类型,Spring 会主动进行类型转换。当 @RequestParam 注解的参数类型为 Map\<String, String> 并且 @RequestParam 没有指定参数名称的时候,Spring 会把所有的参数注入到 Map 中。

@Controller
@RequestMapping("/pets")
public class EditPetForm {

    // ...

    @GetMapping
    public String setupForm(@RequestParam("petId") int petId, Model model) {Pet pet = this.clinic.loadPet(petId);
        model.addAttribute("pet", pet);
        return "petForm";
    }

    // ...

}

@RequestHeader

一次 Http 申请往往会蕴含申请头和 Body 两局部,咱们能够通过 @RequestHeader 把申请头和解决办法的参数进行绑定,@RequestHeader 同样反对 Map,假如一次申请有如下的头:

Host                    localhost:8080
Accept                  text/html,application/xhtml+xml,application/xml;q=0.9
Accept-Language         fr,en-gb;q=0.7,en;q=0.3
Accept-Encoding         gzip,deflate
Accept-Charset          ISO-8859-1,utf-8;q=0.7,*;q=0.7
Keep-Alive              300

如果咱们须要在办法中获取 Accept-Encoding 和 Keep-Alive 标签,咱们能够通过如下代码获取:

@GetMapping("/demo")
public void handle(@RequestHeader("Accept-Encoding") String encoding, 
        @RequestHeader("Keep-Alive") long keepAlive) {//...}

@CookieValue

如果咱们须要获取一次申请中的 cookie 信息,咱们能够通过 @CookieValue 获取,获取办法如下所示:

@GetMapping("/demo")
public void handle(@CookieValue("JSESSIONID") String cookie) {//...}

@ModelAttribute

@ModelAttribute 能够把申请中的参数映射为对象,而后传递给对应的办法。

@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@ModelAttribute Pet pet) {// method logic...}

下面的例子中,申请参数能够来自 pet 能够来自以下几种路径:

  1. 在申请预处理的过程中增加的 @ModelAttribute 属性中的 pet;
  2. 从 HttpSession 中的 @SessionAttributes 属性中查找 pet;
  3. 从申请参数或者 pathVariable 中查找 pet 属性;
  4. 应用默认的构造函数初始化数据。
@PutMapping("/accounts/{account}")
public String save(@ModelAttribute("account") Account account) {// ...}

@PostMapping("/owners/{ownerId}/pets/{petId}/edit")
public String processSubmit(@ModelAttribute("pet") Pet pet, BindingResult result) {if (result.hasErrors()) {return "petForm";}
    // ...
}
@ModelAttribute
public AccountForm setUpForm() {return new AccountForm();
}

@ModelAttribute
public Account findAccount(@PathVariable String accountId) {return accountRepository.findOne(accountId);
}

@PostMapping("update")
public String update(@Valid AccountForm form, BindingResult result,
        @ModelAttribute(binding=false) Account account) {// ...}

@SessionAttributes 和 @SessionAttribute

@SessionAttributes 用于在多个申请之间共享 Session 数据,该注解只能加载类之上。在第一次申请的时候,会把 Session 数据放入 SessionAttributes 中,Session 完结的时候革除数据。

@Controller
@SessionAttributes("pet") // 把数据放入 Session 中
public class EditPetForm {

    // 从 Session 中查问数据
    @PostMapping("/pets/{id}")
    public String handle(Pet pet, BindingResult errors, SessionStatus status) {if (errors.hasErrors) {// ...}
            // 清空 Session 中的数据.
            status.setComplete(); 
            // ...
        }
    }
}

如果 Session 的属性不禁 Controller 治理,而是其它组件治理(如 Filter 治理),咱们就能够应用 @SessionAttribute 去把 Session 中的数据和解决办法中的参数进行绑定。

@RequestMapping("/")
public String handle(@SessionAttribute User user) {// ...}

@RequestAttribute

@RequestAttribute 和 @SessionAttributes 相似,一个是申请级别的,一个是 Session 级别的,此处不做具体介绍。

@GetMapping("/")
public String handle(@RequestAttribute Client client) {// ...}

Multipart 参数

咱们在后面的文章中说过,DispatcherServlet 中会蕴含 MultipartResolver 组件,如果一次申请的数据为 multipart/form-data 类型,DispatcherServlet 会把上传的文件解析为 MultipartFile 格局的文件。Servlet3 中也反对应用 javax.servlet.http.Part 代替 MultipartFile 接管文件,上传多个文件的时候能够应用列表或者 Map 获取参数。

@Controller
public class FileUploadController {@PostMapping("/form")
    public String handleFormUpload(@RequestParam("name") String name,
            @RequestParam("file") MultipartFile file) {if (!file.isEmpty()) {byte[] bytes = file.getBytes();
            // store the bytes somewhere
            return "redirect:uploadSuccess";
        }
        return "redirect:uploadFailure";
    }
}

Multipart 也能够把须要接管的文件封装为对象进行接管。

class MyForm {

    private String name;

    private MultipartFile file;

    // ...
}

@Controller
public class FileUploadController {@PostMapping("/form")
    public String handleFormUpload(MyForm form, BindingResult errors) {if (!form.getFile().isEmpty()) {byte[] bytes = form.getFile().getBytes();
            // store the bytes somewhere
            return "redirect:uploadSuccess";
        }
        return "redirect:uploadFailure";
    }
}

除了通过浏览器上传文件,咱们还能够通过 RestFul 形式以 Json 的格局上传文件:

POST /someUrl
Content-Type: multipart/mixed

--edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp
Content-Disposition: form-data; name="meta-data"
Content-Type: application/json; charset=UTF-8
Content-Transfer-Encoding: 8bit

{"name": "value"}
--edt7Tfrdusa7r3lNQc79vXuhIIMlatb7PQg7Vp
Content-Disposition: form-data; name="file-data"; filename="file.properties"
Content-Type: text/xml
Content-Transfer-Encoding: 8bit
... File Data ...
@PostMapping("/")
public String handle(@RequestPart("meta-data") MetaData metadata,
        @RequestPart("file-data") MultipartFile file) {// ...}

@RequestBody 和 HttpEntity

@RequestBody 应该是日常开发中应用最多的参数之一了,咱们能够通过 @RequestBody 把申请中的 Body 和解决办法中的参数对象进行绑定,Spring 会调用 HttpMessageConverter 服务把申请中的数据反序列化为解决办法中的参数对象。@RequestBody 还能够和 @Validated 注解组合进行应用,如果校验失败会抛出异样或者交给用户解决校验异样信息。

@PostMapping("/accounts")
public void handle(@Valid @RequestBody Account account, BindingResult result) {// ...}

HttpEntity 和 @RequestBody 的原理相似,不过会把申请体封装到 HttpEntity 中。

@PostMapping("/accounts")
public void handle(HttpEntity<Account> entity) {// ...}

@ResponseBody 和 ResponseEntity

@ResponseBody 示意会把返回值通过 HttpMessageConverter 间接序列化为 String 写入 Response,咱们平时应用比拟多的 @RestController 就是由 @ResponseBody 和 @Controller 组成。

@GetMapping("/accounts/{id}")
@ResponseBody
public Account handle() {// ...}

ResponseEntity 和 @ResponseBody,不过返回的根底上会蕴含状态码和返回头等信息。

@GetMapping("/something")
public ResponseEntity<String> handle() {
    String body = ... ;
    String etag = ... ;
    return ResponseEntity.ok().eTag(etag).build(body);
}

JSON Views

Spring 内置了对 JacksonJSON 的反对,并且反对 Jackson 的 Json 序列化视图,在应用 @ResponseBody 和 ResponseEntity 返会数据时,能够依照 @JsonView 来指定 Json 序列化时须要显示的字段。

@RestController
public class UserController {@GetMapping("/user")
    @JsonView(User.WithoutPasswordView.class)
    public User getUser() {return new User("eric", "7!jd#h23");
    }
}

public class User {public interface WithoutPasswordView {};
    public interface WithPasswordView extends WithoutPasswordView {};

    private String username;
    private String password;

    public User() {}

    public User(String username, String password) {
        this.username = username;
        this.password = password;
    }

    @JsonView(WithoutPasswordView.class)
    public String getUsername() {return this.username;}

    @JsonView(WithPasswordView.class)
    public String getPassword() {return this.password;}
}

咱们也能够通过编程的形式实现对象的不同视图的序列化,应用办法如下所示:

@RestController
public class UserController {@GetMapping("/user")
    public MappingJacksonValue getUser() {User user = new User("eric", "7!jd#h23");
        MappingJacksonValue value = new MappingJacksonValue(user);
        value.setSerializationView(User.WithoutPasswordView.class);
        return value;
    }
}

对于基于 View 的解决方案,咱们能够在 Model 中增加对应的对象以及 Json 序列化视图,应用的示例如下所示:

@Controller
public class UserController extends AbstractController {@GetMapping("/user")
    public String getUser(Model model) {model.addAttribute("user", new User("eric", "7!jd#h23"));
        model.addAttribute(JsonView.class.getName(), User.WithoutPasswordView.class);
        return "userView";
    }
}

Model 对象

Spring 中的 model 对象负责在控制器和展示数据的视图之间传递数据。Spring 提供了 @ModelAttribute 去获取和写入 Model 对象的属性,@ModelAttribute 有多种应用形式:

  1. 在解决办法的入参上增加 @ModelAttribute,能够获取 WebDataBinder 中曾经有的 Model 中的属性值。
  2. 在类上(如 Controller)增加 @ModelAttribute 注解,则会为所有的申请初始化模型。
  3. 在解决办法的返回值上增加 @ModelAttribute,示意返回值会作为模型的属性。
@ModelAttribute
public void populateModel(@RequestParam String number, Model model) {model.addAttribute(accountRepository.findAccount(number));
    // add more ...
}

@ModelAttribute
public Account addAccount(@RequestParam String number) {return accountRepository.findAccount(number);
}

DataBinder

后面咱们讲了很多如何把申请参数和解决办法入参进行绑定的注解或者类型,并且晓得申请参数须要通过类型转换能力转为对应类型的数据。然而注解只是一个标记,并不会理论执行参数绑定和类型转换操作,Spring 中必然有一个组件进行参数绑定和类型转换,这个组件就是 WebDataBinder。WebDataBinder 有一下作用:

  1. 将申请中的参数和解决办法参数进行绑定;
  2. 把申请中 Spring 类型的数据转为解决办法的参数类型;
  3. 对渲染表单的数据进行格式化。

Spring 给用户提供了批改 WebDataBinder 的接口,用户能够在 Controller 中定义被 @InitBinder 注解的办法,在办法中批改 WebDataBinder 的定义:

@Controller
public class FormController {

    @InitBinder 
    public void initBinder(WebDataBinder binder) {SimpleDateFormat dateFormat = new SimpleDateFormat("yyyy-MM-dd");
        dateFormat.setLenient(false);
        binder.registerCustomEditor(Date.class, new CustomDateEditor(dateFormat, false));
    }

    // ...
}

异样解决

在对于 DispatcherServlet 相干的章节中,咱们晓得了 DispatcherServlet 蕴含了异样解析组件,当异样产生的时候会对异样进行解析。日常开发中应用比拟多的异样解决组件是 ExceptionHandlerExceptionResolver,用于在遇到异样时,应用带有 @ExceptionHandler 注解的办法解决对应的异样,该办法能够定义中 Controller 或者 ControllerAdvice 中。

@Controller
public class SimpleController {

    // ...

    @ExceptionHandler
    public ResponseEntity<String> handle(IOException ex) {// ...}
    
    @ExceptionHandler({FileSystemException.class, RemoteException.class})
    public ResponseEntity<String> handle(Exception ex) {// ...}
}

如果咱们须要定义很多 @ExceptionHandler,咱们能够抉择在 @ControllerAdvice 中定义,而不是在每个 Controller 中定义。

如果一个异样匹配到多个 @ExceptionHandler,Spring 会尝试应用间隔异样继承体系最近的 @ExceptionHandler 去解决这个异样。

Controller Advice

如果咱们须要定义全局的 @InitBinder 或者 @ExceptionHandler,那咱们就不应该在 Controller 中定义这些办法。Spring 提供了 @ControllerAdvice 用于增加全局配置:

// Target all Controllers annotated with @RestController
@ControllerAdvice(annotations = RestController.class)
public class ExampleAdvice1 {}

// Target all Controllers within specific packages
@ControllerAdvice("org.example.controllers")
public class ExampleAdvice2 {}

// Target all Controllers assignable to specific classes
@ControllerAdvice(assignableTypes = {ControllerInterface.class, AbstractController.class})
public class ExampleAdvice3 {}

我是御狐神,欢送大家关注我的微信公众号:wzm2zsd

本文最先公布至微信公众号,版权所有,禁止转载!

正文完
 0