A Java annotation @Recover takes care of ugly loop retry code

# This article has participated in the "Newcomer Creation Ceremony" event to start the road of creation of Nuggets.

use background

In the actual project, some of the logic may have unexpected exceptions due to calling external services or waiting for locks. At this time, we may need to retry the calling of this part of the logic. The code mainly uses for loops to write a Big chunks of retry logic, all kinds of hard coding, all kinds of hot-eyed patches.

In particular, the logic for retrying is everywhere. spring-retrySo I decided to optimize a wave with a retry component . It appears, get rid of this part of the ugly code!

The source address of this component is as follows: github.com/spring-proj…

picture

Without further ado, let's go to the code!

2 Start the code

First introduce dependencies:

<dependency>
    <groupId>org.springframework.retry</groupId>
    <artifactId>spring-retry</artifactId>
    <version>1.3.2</version>
</dependency>
复制代码

Since this component depends on AOP for you, you also need to introduce this dependency (if you have referenced it in other jar packages, of course you don't need to reference it again):

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-aop</artifactId>
    <version>2.6.1</version>
</dependency>
复制代码

Enable retry:

@SpringBootApplication
@EnableRetry
public class ApplicationStarter {
  public static void main(String[] args) {
  SpringApplication.run(ApplicationStarter.class);
  }
}
复制代码

Controller layer

@RestController
public class TestController {
@Autowired
private IRecursiveCallService recursiveCallService;

    @GetMapping("test2")
    public Object test2() {
        return recursiveCallService.testService();
    }
}
复制代码

Service layer

public interface IRecursiveCallService {

    /**
     * 测试service
     *
     * @return
     */
    List<Integer> testService();
}
复制代码

The specific implementation of the Service layer

@Service
public class RecursiveCallServiceImpl implements IRecursiveCallService {

    @Override
    @Retryable(recover = "testService3")
    public List<Integer> testService() {
        System.out.println("到此一游!");
        System.out.println(1 / 0);
        return null;
    }

    @Recover
    public List<String> testService1() {
        System.out.println("错误的返回");
        return Collections.singletonList("S");
    }

    @Recover
    public List<Integer> testService2(String i) {
        System.out.println("正确的返回");
        return Collections.singletonList(1);
    }

    @Recover
    public List<Integer> testService3() {
        System.out.println("正确的返回2");
        return Collections.singletonList(2);
    }
}
```## 3@Retryable注解重要属性解析

-   `recover`: 此类中用于恢复的方法的名称。方法必须用 {@link Recover} 注释标记。
-   `value`: 可重试的异常类型。包括()的同义词。默认为空(如果 excludes 也为空,则重试所有异常)。
-   `exclude`: 不可重试的异常类型。默认为空(如果包含也为空,则重试所有异常)。如果 include 为空但 excludes 不是,则重试所有未排除的异常
-   `maxAttempts`: 方法重试调用次数,默认3次
-   `backoff`: 指定用于重试此操作的其他属性

## 4@backoff注解

-   `value`:重试之间间隔时间
-   `delay`:重试之间的等待时间(以毫秒为单位)
-   `maxDelay`:重试之间的最大等待时间(以毫秒为单位)
-   `multiplier`:指定延迟的倍数
-   `delayExpression`:重试之间的等待时间表达式
-   `maxDelayExpression`:重试之间的最大等待时间表达式
-   `multiplierExpression`:指定延迟的倍数表达式
-   `random`:随机指定延迟时间

## 5@Recover注解

主要作用是标记方法为一个重试方法的补偿方法!!!

## 6注意事项

-   方法重试依赖于 spring 注入,所以调用的方法的类必须是被spring管理的,然后通过 @Autowired 或 @Resource 引入使用,不然不会生效
-   方法重试的前提是方法抛出了异常,在方法执行出现了异常且没有被捕获的情况下重试
-   方法重试需要在方法上面加上注解 @Retryable
-   方法重试的补偿方法上面必须携带@Recover注解
-   @Recover方法需要和@Retryable方法在同一个类中才能生效@Recover方法(@Recover方法在父类中也可以生效)
-   使用@Retryable注解,如果类中没有被@Recover标示的方法,无论是否使用 recover 属性都抛出原有异常
-   使用@Retryable注解同时 recover 属性不是空,如果类中有@Recover标示的方法,但是标示的方法不是 recover 指定的方法,抛出ExhaustedRetryException异常
-   使用@Retryable注解同时 recover 属性不是空,同时方法有注解@Recover,但是补偿方法的参数不是当前异常或者异常的父类,抛出ExhaustedRetryException 异常
-   使用@Retryable注解不使用 recover 属性,如果类中被@Recover标示的方法有和原方法返回值一样的,使用当前被@Recover标示的方法(此时方法参数可随意,但是不能是除开当前异常的类及父类的异常类)
复制代码

Guess you like

Origin juejin.im/post/7086641658002931743