(十二)Spring Boot 全局异常处理和 Retry 重试 —— 《一步一步学 Spring Boot 2》读书笔记

本文纯个人读书笔记,书籍《一步一步学 Spring Boot 2》
如果喜欢,可直接购买书籍。如有侵权,请联系删除

一、全局异常

在 Web 应用中,我们经常需要处理错误情况。Spring Boot 默认提供了一个映射 /error,在处理中抛出异常之后,就跳转到这个映射中,并且最终会跳转到一个全局的错误界面。

启动 Spring Boot 应用,随便输入一个地址,就会触发异常,跳转到错误界面。

错误界面:
(十二)Spring Boot 全局异常处理和 Retry 重试 —— 《一步一步学 Spring Boot 2》读书笔记_第1张图片
虽然 Spring Boot 提供了一个默认的错误界面,但是这个界面没有很友好,我们经常会重新开发一个错误界面。

二、自定义错误界面

1.错误界面

新建错误界面 /resources/static/404.html。
(十二)Spring Boot 全局异常处理和 Retry 重试 —— 《一步一步学 Spring Boot 2》读书笔记_第2张图片

404.html:




    
    错误


    
一不小心,走丢了哦 !!!

这边就简单的实现一个界面,实际项目中可以根据具体需求进行 404 界面开发。

2.配置类

新建 com.xiaoyue.demo.error.ErrorPageConfig 配置类,配置上面的界面未错误处理界面。

ErrorPageConfig:

@Configuration
public class ErrorPageConfig {

    @Bean
    public WebServerFactoryCustomizer webServerFactoryCustomizer(){
        return new WebServerFactoryCustomizer() {
            @Override
            public void customize(ConfigurableWebServerFactory factory) {

                ErrorPage error404Page = new ErrorPage(HttpStatus.NOT_FOUND, "/404.html");

                factory.addErrorPages(error404Page);
            }
        };
    }
}

这边用的是 Spring Boot 2.0 以上的,所以使用 WebServerFactoryCustomizer,原书使用 EmbeddedServletContainerCustomizer,这个已经被替代了。

也可以继续在 WebServerFactoryCustomizer 中添加 401,,500 等处理界面。

3.测试

再次启动 Spring Boot 应用,随便输入一个地址,查看错误界面。

错误界面:
(十二)Spring Boot 全局异常处理和 Retry 重试 —— 《一步一步学 Spring Boot 2》读书笔记_第3张图片
可以发现,错误界面已经被替换。

三、自定义错误界面

实际项目中,处理业务功能时候,由于一些业务的特殊要求,导致处理不能继续而抛出异常。我们希望这些业务异常能够统一处理,因此使用 Spring Boot 进行全局异常处理就变得很方便。

1.自定义异常

首先,我们需要封装自定义业务异常 com.xiaoyue.demo.exception.BusinessException , 该类继承自 RuntimeException 异常类。

BusinessException:

public class BusinessException extends RuntimeException {

    public BusinessException(){
        super();
    }

    public BusinessException(String message){
        super(message);
    }
}

2.错误信息封装类

创建错误信息封装类 com.xiaoyue.demo.error.Errorlnfo。

ErrorInfo:

public class ErrorInfo {

    public static final Integer SUCCESS = 200;
    public static final Integer ERROR = 100;
    //错误信息
    private Integer code;
    //错误码
    private String message;
    private String url;
    private T data;

}

3.异常处理类

创建异常处理类 com.xiaoyue.demo.error.GlobalDefaultExceptionHandler 对异常进行处理。

GlobalDefaultExceptionHandler:

@ControllerAdvice(basePackages = {"com.xiaoyue.demo",})
public class GlobalDefaultExceptionHandler {

    @ExceptionHandler({BusinessException.class})
    //如果返回的为 json 数据或其他对象,就添加该注解
    @ResponseBody
    public ErrorInfo defaultErrorHandler(HttpServletRequest req ,
                                         Exception e)  throws Exception {
        ErrorInfo errorinfo = new ErrorInfo();
        errorinfo.setMessage(e.getMessage());
        errorinfo.setUrl(req.getRequestURI());
        errorinfo.setCode(ErrorInfo.SUCCESS);
        return errorinfo;
    }
}

@ControllerAdvice: 定义统一的异常处理类, basePackages 属性用 于定义扫描哪些包,默认可不设置。

@ExceptionHandler: 用来定义函数针对的异常类型,可以传入多个需要捕获的异常类。

@ResponseBody: 如果返回的为 json 数据或其他对象,就添加该注解。

4.测试

在 UserController 中新增方法 testError,抛出 BusinessException 异常。

BusinessException:

@Controller
@RequestMapping(value = "/user")
public class UserController {

    @RequestMapping("/testError")
    public String testError(Model model) {

        if (true){
            throw new BusinessException("业务处理异常");
        }

        return "user";
    }
}

运行项目,访问 http://localhost:8080/demo/user/testError,查看结果。
在这里插入图片描述

四、Retry 重试

实际项目中,处理业务功能时候,由于一些业务的特殊要求,导致处理不能继续而抛出异常。我们希望这些业务异常能够统一处理,因此使用 Spring Boot 进行全局异常处理就变得很方便。

1.Retry 重试

我们调用一个接口的时候,可能由于网络等原因导致失败,这时候需要需要进行重试。简单点就是 try-catch-redo 简单重试模式 ,判断返回结果或监昕异常来判断是否重试。

** try-catch-redo:**

    public void testRetry() throws Exception {
        boolean result = false;
        try {
            result = load();
            if (!result) {
                load();     //一次重试
            }
        }catch(Exception e){
            load();         //一次重试
        }
    }

try-catch-redo 在失败之后马上发起重试,这种情况下,间隔时间太短,再次重试有人容易失败。这时候,需要考虑增加重试次数或者间隔时间。

try-catch-redo-retry:

    public void testRetry() throws Exception {
        boolean result = false;
        try {
            result = load();
            if (!result) {
               reLload(3000L, 3);     //延迟 3 秒,重试 3 次
            }
        }catch(Exception e){
            reLload(3000L, 3);     //延迟 3 秒,重试 3 次
        }
    }

我们自己进行编写重试逻辑,会导致正常逻辑和重试逻辑强耦合,属于入侵式的。Spring-Retry 规范正常逻辑和重试逻辑,将正常逻辑和重试逻辑解藕。

2.Spring-Retry

Spring-Retry 是一个开源工具包 , 该工具把重试操作模板定制化,可以设置重试策略和回退策略。同时,重试执行实例保证线程安全。 Spring-Retry 重试可以用 Java 代码方式实现,也可以用注解@Retryable 方式实现,这里 Spring-Retry 提倡以注解的方式。

1.引入依赖
在 pom.xml 文件中添加依赖。

pom.xml:

        
            org.springframework.retry
            spring-retry
        
        
            org.aspectj
            aspectjweaver
        

2.开启重试
在入口类 DemoApplication 中 添加注解 @EnableRetry 开启 Retry 重试。

DemoApplication:

@SpringBootApplication
@ServletComponentScan
@EnableAsync
@ImportResource(locations={"classpath:spring-mvc.xml"})
@EnableRetry
public class DemoApplication {

    public static void main(String[] args) {
        SpringApplication.run(DemoApplication.class, args);
    }

}

3.测试

在 UserService 中新增测试接口 findByNameAndPasswordRetry 。

UserService:

public interface UserService {
    User findByNameAndPasswordRetry(String name, String password);
}

在 UserServiceImpl 中添加实现方法。

UserServiceImpl:

@Service
@Transactional
public class UserServiceImpl implements UserService {

    @Resource
    private UserRepository userRepository;

    @Override
    @Retryable(value= {BusinessException.class}, maxAttempts = 5,
            backoff = @Backoff(delay = 5000 , multiplier = 2))
    public User findByNameAndPasswordRetry(String name, String password) {
        System.out.println("findByNameAndPasswordRetry 调用失败,进行重试");
        throw new BusinessException() ;
    }
}

@Retryable:
value 属性表示当出现哪些异常的时候触发重试。
maxAttempts 表示最大重试次数,默认为 3。
delay 表示重试的延迟时间。
multiplier 表示下一次延时时间是这一次的倍数。

在测试类 DemoApplicationTests 中添加测试代码进行测试。

DemoApplicationTests:

    @Test
    public void testRetry(){
        User user = userService.findByNameAndPasswordRetry("aa", "123456");
        logger.info(user.getId() + "   " + user.getName());
    }

运行结果:
(十二)Spring Boot 全局异常处理和 Retry 重试 —— 《一步一步学 Spring Boot 2》读书笔记_第4张图片
可以发现,findByNameAndPasswordRetry 方法进行了重试,且重试的间隔时间越来越长。

你可能感兴趣的:(《一步一步学Spring,Boot,2》学习笔记)