@ControllerAdvice

Spring’s source code notes @controllerAdvice as follows:

Specialization of {@link Component @Component} for classes that declare {@link ExceptionHandler @ExceptionHandler}, {@link InitBinder @InitBinder}, or {@link ModelAttribute @ModelAttribute} methods to be shared across multiple {@code @Controller} classes.

To understand:

@ControllerAdvice is a special @Component that identifies a class with methods identified by one of three annotations: @ExceptionHandler, @initBinder, and @ModelAttribute will apply to all @Controller interfaces.

So what do these three annotations mean and what do they do?


@InitBinder

Annotation that identifies methods which initialize the {@link org.springframework.web.bind.WebDataBinder} which will be used for populating command and form object arguments of annotated handler methods. Such init-binder methods support all arguments that {@link RequestMapping} supports, except for command/form objects and corresponding validation result objects. Init-binder methods must not have a return value; they are usually declared as {@code void}.

Function: register the property editor, process THE HTTP request parameters, and bind them to the corresponding interface, such as formatting time conversion, etc. When applied to a method of a single @Controller class, it only applies to interfaces in that class. This parameter takes effect globally when combined with @controllerAdvice.

Example:

@ControllerAdvice
public class ActionAdvice {
    
    @InitBinder
    public void handleException(WebDataBinder binder) {
        binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd HH:mm:ss")); }}Copy the code

@ExceptionHandler

Purpose: Unified exception handling, can also specify the type of exception to handle

Example:

@ControllerAdvice
public class ActionAdvice {
    
    @ExceptionHandler(Exception.class)
    @ResponseBody
    @ResponseStatus(HttpStatus.OK)
    public Map handleException(Exception ex) {
        Map<String, Object> map = new HashMap<>();
        map.put("code".400);
        map.put("msg", ex.toString());
        returnmap; }}Copy the code

@ModelAttribute

Function: Bind data

Example:

@ControllerAdvice
public class ActionAdvice {
    
    @ModelAttribute
    public void handleException(Model model) {
        model.addAttribute("user"."zfh"); }}Copy the code

Get the previously bound parameter in the interface:

@RestController
public class BasicController {
    
    @GetMapping(value = "index")
    public Map index(@ModelAttribute("user") String user) {
        / /...}}Copy the code

Complete sample code:

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.format.datetime.DateFormatter;
import org.springframework.http.HttpStatus;
import org.springframework.ui.Model;
import org.springframework.validation.Validator;
import org.springframework.web.bind.WebDataBinder;
import org.springframework.web.bind.annotation.*;

import java.util.HashMap;
import java.util.Map;

/** * unified exception handling *@author zfh
 * @version 1.0
 * @since2019/1/4 mingled * /
@ControllerAdvice
public class ControllerExceptionHandler {

    private Logger logger = LoggerFactory.getLogger(ControllerExceptionHandler.class);

    @InitBinder
    public void initMyBinder(WebDataBinder binder) {
        // Add unified handling of dates
        //binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd"));
        binder.addCustomFormatter(new DateFormatter("yyyy-MM-dd HH:mm:ss"));

        // Add form validation
        //binder.addValidators();
    }

    @ModelAttribute
    public void addMyAttribute(Model model) {
        model.addAttribute("user"."zfh"); // Use @modelAttribute ("name") Object name in the @requestMapping interface
    }

    @ExceptionHandler(value = Exception.class)
    @ResponseStatus(HttpStatus.OK)
    @ResponseBody // If @restControllerAdvice is used, @responseBody is not needed here
    public Map handler(Exception ex) {
        logger.error("Unified Exception Handling", ex);
        Map<String, Object> map = new HashMap<>();
        map.put("code".400);
        map.put("msg", ex);
        returnmap; }}Copy the code

Test interface:

@RestController
public class TestAction {

    @GetMapping(value = "testAdvice")
    public JsonResult testAdvice(@ModelAttribute("user") String user, Date date) throws Exception {
        System.out.println("user: " + user);
        System.out.println("date: " + date);
        throw new Exception("Throw an exception directly"); }}Copy the code

Advanced application – Format the time to Date

Use @controllerAdvice + @initBinder to automatically convert the time in HTTP request parameters to the Date type.

    @InitBinder
    public void initBinder(WebDataBinder binder) {
        GenericConversionService genericConversionService = (GenericConversionService) binder.getConversionService();
        if(genericConversionService ! =null) {
            genericConversionService.addConverter(newDateConverter()); }}Copy the code

Custom time type converter:

import org.springframework.core.convert.converter.Converter;
import org.springframework.util.StringUtils;

import java.text.SimpleDateFormat;
import java.util.Date;

/** * Date conversion class * convert standard Date, standard Date time, timestamp to Date type */
public class DateConverter implements Converter<String.Date> {
    private static final String dateFormat = "yyyy-MM-dd HH:mm:ss";
    private static final String shortDateFormat = "yyyy-MM-dd";
    private static final String timeStampFormat = "^\\d+$";

    @Override
    public Date convert(String value) {

        if(StringUtils.isEmpty(value)) {
            return null;
        }

        value = value.trim();

        try {
            if (value.contains("-")) {
                SimpleDateFormat formatter;
                if (value.contains(":")) {
                    formatter = new SimpleDateFormat(dateFormat);
                } else {
                    formatter = new SimpleDateFormat(shortDateFormat);
                }
                return formatter.parse(value);
            } else if (value.matches(timeStampFormat)) {
                Long lDate = new Long(value);
                return newDate(lDate); }}catch (Exception e) {
            throw new RuntimeException(String.format("parser %s to Date fail", value));
        }
        throw new RuntimeException(String.format("parser %s to Date fail", value)); }}Copy the code

Extension:

@RestControllerAdvice = @ControllerAdvice + @ResponseBody


Reference:

  1. Intercepts exceptions and handles them uniformly
  2. SpringMVC Important Note (2) @ControllerAdvice
  3. Mastering Spring Boot — Chapter 15: Handling Exceptions using @ControllerAdvice
  4. @InitBinder
  5. SpringMVC uses @initBinder to parse the binding of page data