天天看點

Spring Retry 重試機制 10 分鐘立馬掌握特别鳴謝:https://blog.csdn.net/Revivedsun/article/details/53401335參考方式:以下内容都是demo,直接複制粘貼到項目中即可。1.自定義手寫重試機制2.Spring Retry 入門

特别鳴謝:https://blog.csdn.net/Revivedsun/article/details/53401335

參考方式:以下内容都是demo,直接複制粘貼到項目中即可。

1.自定義手寫重試機制

參考位址:https://blog.csdn.net/u010081710/article/details/77879129

參考位址:https://blog.csdn.net/xiaolyuh123/article/details/80209815

/**
 * 手寫重試機制
 */
public class MyJavaRetry {

    public static void main(String[] args) throws InterruptedException {
        // 重試次數 3 次
        int maxRryTimes = 3;
        // 時間間隔 3 秒
        int intervalTime = 3;
        // 重試次數
        int redo = 1;
        
        while (redo <= maxRryTimes) {
            
            try {
                
                doBuinessLogic(redo); // 業務處理,可能需要重試的業務
                break;                //執行成功後直接退出此循環
            } catch (Exception e) {   //異常時,重試次數增加
                
                redo++;
                // 優先使用TimeUnit類中的sleep() 而不是Thread.sleep(4*60*1000);
                TimeUnit.SECONDS.sleep(intervalTime);
                
                continue;
            }
        }

    }

    private static void doBuinessLogic(int redo) {
        SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        if (redo < 3) {
            System.err.println("第" + redo + "次執行doBuinessLogic()" + "開始執行時間:" + s.format(new Date()) + "執行失敗");
            throw new RuntimeException();
        }
        
        System.err.println("第" + redo + "次執行doBuinessLogic()" + "開始執行時間:" + s.format(new Date()) + "執行成功");
    }
}
           

2.Spring Retry 入門

2.1 maven項目pom.xml

<dependency>
            <groupId>org.springframework.retry</groupId>
            <artifactId>spring-retry</artifactId>
            <version>1.2.2.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.9.2</version>
        </dependency>
           

2.2 SpringRetryDemo

package com.retry;

import java.text.SimpleDateFormat;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;
import java.util.Random;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.TimeoutException;

import org.junit.Test;
import org.springframework.classify.Classifier;
import org.springframework.retry.RecoveryCallback;
import org.springframework.retry.RetryCallback;
import org.springframework.retry.RetryContext;
import org.springframework.retry.RetryListener;
import org.springframework.retry.RetryPolicy;
import org.springframework.retry.backoff.ExponentialBackOffPolicy;
import org.springframework.retry.backoff.ExponentialRandomBackOffPolicy;
import org.springframework.retry.backoff.FixedBackOffPolicy;
import org.springframework.retry.backoff.NoBackOffPolicy;
import org.springframework.retry.backoff.Sleeper;
import org.springframework.retry.backoff.UniformRandomBackOffPolicy;
import org.springframework.retry.policy.AlwaysRetryPolicy;
import org.springframework.retry.policy.CompositeRetryPolicy;
import org.springframework.retry.policy.ExceptionClassifierRetryPolicy;
import org.springframework.retry.policy.NeverRetryPolicy;
import org.springframework.retry.policy.SimpleRetryPolicy;
import org.springframework.retry.policy.TimeoutRetryPolicy;
import org.springframework.retry.support.RetryTemplate;


/**
 * spring-retry 版本 1.2.2
 * 
 * Spring-retry提供了RetryOperations接口的實作類RetryTemplate。
 * 通過RetryTemplate來完成重試,下面是使用RetryTemplate重試的一些簡單例子。
 * 
 */
public class SpringRetryDemo {
    
    private static final int NOTIFY_RETRY_TIMES = 3;

    /**
     * 簡介:
     * 
     * Spring-retry提供的RetryOperations接口,該接口提供了若幹方法來執行重試操作
     * 
     * public interface RetryOperations {
     *       <T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback) throws E;
     *       
     *       <T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback, RecoveryCallback<T> recoveryCallback) throws E;
     *       
     *       <T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback, RetryState retryState) throws E, ExhaustedRetryException;
     *   
     *       <T, E extends Throwable> T execute(RetryCallback<T, E> retryCallback, RecoveryCallback<T> recoveryCallback, RetryState retryState) throws E;
     * }
     * 
     * 調用者通過傳入RetryCallback來完成調用者的重試操作。如果callback執行失敗(抛出某些異常),那麼會按照調用者設定的政策進行重試。
     * 重試操作直到成功,或根據使用者設定的條件而退出。
     * 
     * RetryCallback的接口定義如下:
     * 
     * public interface RetryCallback<T, E extends Throwable> {
     * 
     *      T doWithRetry(RetryContext context) throws E;
     * }
     * 
     */

    
    /**
     * TimeoutRetryPolicy政策(注:不是間隔多長時間進行重試的那個時間)
     * 
     * 代碼定義了TimeoutRetryPolicy政策,TimeoutRetryPolicy逾時時間預設是1秒。
     * TimeoutRetryPolicy逾時是指在execute方法内部,從open操作開始到調用
     * TimeoutRetryPolicy的canRetry方法這之間所經過的時間。
     * 這段時間未超過TimeoutRetryPolicy定義的逾時時間,那麼執行操作,否則抛出異常。
     * 
     * protected <T, E extends Throwable> T doExecute(RetryCallback<T, E> retryCallback,RecoveryCallback<T> recoveryCallback, RetryState state) throws E,ExhaustedRetryException {
     *      ……略
     *      RetryContext context = open(retryPolicy, state);                 
     *      ……略
     *      while (canRetry(retryPolicy, context) &&   !context.isExhaustedOnly())   
     *      // 調用canRetry檢查是否可以重試
     *      ……略
     *   }
     *
     */
    @Test
    public void timeoutRetryPolicyTest() throws Exception{
        
        // Spring-retry提供了RetryOperations接口的實作類RetryTemplate
        RetryTemplate template = new RetryTemplate();

        // 逾時重試政策
        TimeoutRetryPolicy policy = new TimeoutRetryPolicy();
        // 逾時時間 預設 1000 毫秒
        policy.setTimeout(2000);
        
        // 設定重試政策
        template.setRetryPolicy(policy);

        // 執行
        String result = template.execute(
                new RetryCallback<String, Exception>() {

                    public String doWithRetry(RetryContext context) throws InterruptedException {
                   
                        System.out.println("執行Buiness logic");
                        // TODO 對這個政策模式的了解還是模糊 還為了解逾時到底是哪個地方逾時
                        // 等待3秒
                        TimeUnit.SECONDS.sleep(3);
                        
                        return "SUCCESS";
                    }
                },
                // 當重試執行完閉,操作還未成為,那麼可以通過RecoveryCallback完成一些失敗事後處理。
                new RecoveryCallback<String>() {
                    public String recover(RetryContext context) throws Exception {
                        
                        return "failed callback";
                    }
                }
        );
        
        System.out.println(result);
    }
    
    /**
     * SimpleRetryPolicy 政策
     * 
     * will execute the callback at least once, and as many as 3 times.
     * 該政策定義了對指定的異常進行若幹次重試。預設情況下,對Exception異常及其子類重試3次.
     * 如果建立SimpleRetryPolicy并指定重試異常map,可以選擇性重試或不進行重試.
     * 
     */
    @SuppressWarnings("unused")
    @Test
    public void simpleRetryPolicyTest() throws Exception{
        
        // Spring-retry提供了RetryOperations接口的實作類RetryTemplate
        RetryTemplate template = new RetryTemplate();
        
        
        // 簡單重試政策 example one
        SimpleRetryPolicy policy1 = new SimpleRetryPolicy();
        // 最大重試次數  預設3次  這裡設定為5次
        policy1.setMaxAttempts(5);
        
        // 簡單重試政策 example two  1 重試次數5次  2Exception及其子類都進行異重試
        // Set the max attempts including the initial attempt before retrying
        // and retry on all exceptions (this is the default):
        SimpleRetryPolicy policy2 = new SimpleRetryPolicy(5, Collections.singletonMap(Exception.class, true));

        // 簡單重試政策 example three
        Map<Class<? extends Throwable>, Boolean> retryableExceptionMaps = new HashMap<Class<? extends Throwable>, Boolean>();;
        // 空指針異常進行重試  true 進行重試(當Map中的的value為false,那麼執行方法,随後抛出異常不進行重試。)
        retryableExceptionMaps.put(NullPointerException.class, true);
        SimpleRetryPolicy policy3 = new SimpleRetryPolicy(5, retryableExceptionMaps);
        
        // 設定重試政策
        template.setRetryPolicy(policy1);
//        template.setRetryPolicy(policy2);
//        template.setRetryPolicy(policy3);

        // 執行
        String result = template.execute(
                new RetryCallback<String, Exception>() {

                    public String doWithRetry(RetryContext context) throws InterruptedException {
                   
                        if (true) {
                            System.out.println("執行Buiness logic. NPE");
                            // 抛出異常
                            throw new NullPointerException("NullPointerException");
                        }
                        
                        return "SUCCESS";
                    }
                },
                // 當重試執行完閉,操作還未成為,那麼可以通過RecoveryCallback完成一些失敗事後處理。
                new RecoveryCallback<String>() {
                    public String recover(RetryContext context) throws Exception {
                        
                        return "failed callback";
                    }
                }
        );
        
        System.out.println(result);
    }
    
    /**
     * NeverRetryPolicy 政策
     * 
     * 執行一次待執行操作,若出現異常後不進行重試。
     * 
     */
    @Test
    public void neverRetryPolicyTest() throws Exception{
        
        // Spring-retry提供了RetryOperations接口的實作類RetryTemplate
        RetryTemplate template = new RetryTemplate();

        // 從不重試政策
        NeverRetryPolicy  policy = new NeverRetryPolicy();
        
        // 設定重試政策
        template.setRetryPolicy(policy);

        // 執行
        String result = template.execute(
                new RetryCallback<String, Exception>() {

                    public String doWithRetry(RetryContext context) throws InterruptedException {
                   
                        System.out.println("執行Buiness logic. NPE");
                        // 抛出異常
                        throw new NullPointerException("NullPointerException");
                        
                    }
                },
                // 當重試執行完閉,操作還未成為,那麼可以通過RecoveryCallback完成一些失敗事後處理。
                new RecoveryCallback<String>() {
                    public String recover(RetryContext context) throws Exception {
                        
                        return "failed callback";
                    }
                }
        );
        
        System.out.println(result);
    }
    
    
    /**
     * AlwaysRetryPolicy 政策
     * 
     * 異常後一直重試直到成功。
     * 
     */
    @Test
    public void alwaysRetryPolicyTest() throws Exception{
        
        // Spring-retry提供了RetryOperations接口的實作類RetryTemplate
        RetryTemplate template = new RetryTemplate();
        
        // 一直重試政策
        AlwaysRetryPolicy  policy = new AlwaysRetryPolicy();
        
        // 設定重試政策
        template.setRetryPolicy(policy);
        
        // 執行
        String result = template.execute(
                new RetryCallback<String, Exception>() {
                    
                    public String doWithRetry(RetryContext context) throws InterruptedException {
                        
                        System.out.println("執行Buiness logic. NPE");
                        // 抛出異常
                        throw new NullPointerException("NullPointerException");
                        
                    }
                },
                // 當重試執行完閉,操作還未成為,那麼可以通過RecoveryCallback完成一些失敗事後處理。
                new RecoveryCallback<String>() {
                    public String recover(RetryContext context) throws Exception {
                        
                        return "failed callback";
                    }
                }
        );
        
        System.out.println(result);
    }
    
    
    /**
     * ExceptionClassifierRetryPolicy 政策
     * 
     * 根據産生的異常選擇重試政策。
     * 
     */
    @SuppressWarnings("serial")
    @Test
    public void exceptionClassifierRetryPolicyTest() throws Exception{
        
        // Spring-retry提供了RetryOperations接口的實作類RetryTemplate
        RetryTemplate template = new RetryTemplate();
        
        
        /* setPolicyMap 與 setExceptionClassifier 使用一個即可。*/
        
        // 方式 1 setPolicyMap
        // 根據異常設定重試政策
        ExceptionClassifierRetryPolicy policy1 = new ExceptionClassifierRetryPolicy();
        
        Map<Class<? extends Throwable>, RetryPolicy> policyMap = new HashMap<Class<? extends Throwable>, RetryPolicy>();
        // 抛出TimeoutException采用AlwaysRetryPolicy政策
        policyMap.put(TimeoutException.class, new AlwaysRetryPolicy());
        // 抛出NullPointerException采用NeverRetryPolicy政策
        policyMap.put(NullPointerException.class, new NeverRetryPolicy());
        // 異常重試政策map
        policy1.setPolicyMap(policyMap);
        
        // 方式 2 setExceptionClassifier
        // 根據異常設定重試政策
        ExceptionClassifierRetryPolicy policy2 = new ExceptionClassifierRetryPolicy();
        // 此外可以通過setExceptionClassifier來為異常指定重試政策。
        Classifier<Throwable, RetryPolicy> exceptionClassifier = new Classifier<Throwable, RetryPolicy>(){
            public RetryPolicy classify(Throwable classifiable) {
                if(classifiable instanceof TimeoutException)
                    return new SimpleRetryPolicy();
                return new NeverRetryPolicy();
            }

        };
        policy2.setExceptionClassifier(exceptionClassifier);
        
        // 設定重試政策
        template.setRetryPolicy(policy1);
//        template.setRetryPolicy(policy2);
        
        // 執行
        String result = template.execute(
                new RetryCallback<String, Exception>() {
                    
                    public String doWithRetry(RetryContext context) throws Exception {
                        
                        int number = new Random().nextInt(10) + 1;
                        if (number > 5) {
                            
                            System.out.println("執行Buiness logic. NPE");
                            // 抛出異常
                            throw new NullPointerException("NullPointerException");
                        } else {
                            
                            System.out.println("執行Buiness logic. TE");
                            // 抛出異常
                            throw new TimeoutException("TimeoutException");
                        }
                    }
                },
                // 當重試執行完閉,操作還未成為,那麼可以通過RecoveryCallback完成一些失敗事後處理。
                new RecoveryCallback<String>() {
                    public String recover(RetryContext context) throws Exception {
                        
                        return "failed callback";
                    }
                }
        );
        
        System.out.println(result);
    }
    
    /**
     * CompositeRetryPolicy 政策
     * 
     * 使用者指定一組政策,随後根據optimistic選項來确認如何重試。
     * 
     * 下面的代碼中建立CompositeRetryPolicy政策,并建立了RetryPolicy數組,
     * 數組有兩個具體政策SimpleRetryPolicy與AlwaysRetryPolicy。
     * 
     * optimistic = true
     * 當CompositeRetryPolicy設定optimistic為true時,Spring-retry會順序周遊RetryPolicy[]數組,如果有一個重試政策可重試,
     * 例如SimpleRetryPolicy沒有達到重試次數,那麼就會進行重試
     * 
     * optimistic = false
     * 如果optimistic選項設定為false。那麼有一個重試政策無法重試,那麼就不進行重試。
     * 例如SimpleRetryPolicy達到重試次數不能再重試,而AlwaysRetryPolicy可以重試,那麼最終是無法重試的。
     *  
     * 以下代碼,設定setOptimistic(true),而AlwaysRetryPolicy一直可重試,那麼最終可以不斷進行重試。
     *  
     */
    @Test
    public void compositeRetryPolicyTest() throws Exception{
        
        // Spring-retry提供了RetryOperations接口的實作類RetryTemplate
        RetryTemplate template = new RetryTemplate();
        
        // 混合政策
        CompositeRetryPolicy policy = new CompositeRetryPolicy();
        // 政策組
        RetryPolicy[] polices = {new SimpleRetryPolicy(), new AlwaysRetryPolicy()};
        policy.setPolicies(polices);
        policy.setOptimistic(true);
        
        // 設定重試政策
        template.setRetryPolicy(policy);
        
        try {
            

        // 執行
        String result = template.execute(
                new RetryCallback<String, Exception>() {
                    
                    public String doWithRetry(RetryContext context) throws Exception {
                        
                        int number = new Random().nextInt(10) + 1;
                        if (number > 5) {
                            
                            System.out.println("執行Buiness logic. NPE");
                            // 抛出異常
                            throw new NullPointerException("NullPointerException");
                        } else {
                            
                            System.out.println("執行Buiness logic. TE");
                            // 抛出異常
                            throw new TimeoutException("TimeoutException");
                        }
                    }
                },
                // 當重試執行完閉,操作還未成為,那麼可以通過RecoveryCallback完成一些失敗事後處理。
                new RecoveryCallback<String>() {
                    public String recover(RetryContext context) throws Exception {
                        
                        return "failed callback";
                    }
                }
                );
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
    
    /**
     * 退避(BackOff)政策
     * 
     * 當操作執行失敗時,根據設定的重試政策進行重試。通過BackoffPolicy可以設定再次重試的時間間隔。
     * 
     * 接口:
     * public interface BackOffPolicy {
     *       BackOffContext start(RetryContext context);
     *       void backOff(BackOffContext backOffContext) throws BackOffInterruptedException;
     * }
     * 
     * BackOff政策接口的具體實作:
     * 
     * interface BackOffPolicy
     *      // 實作BackOff接口的抽象類
     *      abstract StatelessBackOffPolicy
     *          class FixedBackOffPolicy // 在等待一段固定的時間後,再進行重試。預設為1秒。
     *          class NoBackOffPolicy    // 實作了空方法,是以采用次政策,重試不會等待。這也是RetryTemplate采用的預設退避(backOff)政策
     *          class UniformRandomBackOffPolicy // 均勻随機退避政策,等待時間為 最小退避時間 + [0,最大退避時間 - 最小退避時間)間的一個随機數,如果最大退避時間等于最小退避時間那麼等待時間為0。
     *      // 繼承BackOff接口的接口
     *      interface SleepingBackOffPolicy
     *          class ExponentialBackOffPolicy // 指數退避政策 ,每次等待時間為 等待時間 = 等待時間 * N ,即每次等待時間為上一次的N倍。如果等待時間超過最大等待時間,那麼以後的等待時間為最大等待時間。
     *              // 該類是ExponentialBackOffPolicy的子類
     *              class ExponentialRandomBackOffPolicy // 指數随機政策
     *          class FixedBackOffPolicy    // 與StatelessBackoffPolicy的同名實作類傳回等待時間的方法是一緻的。而兩者的主要差別是,SleepingbackOffPolicy可以設定使用者定義的Sleeper。
     *          class UniformRandomBackOffPolicy // 與StatelessBackoffPolicy的同名實作類傳回等待時間的方法是一緻的。而兩者的主要差別是,SleepingbackOffPolicy可以設定使用者定義的Sleeper。
     *
     */
    
    /**
     * 實作BackOff接口的抽象類
     * StatelessBackoffPolicy 抽象類下的退避政策
     * 
     */
    @SuppressWarnings("unused")
    @Test
    public void statelessBackoffPolicyTest() throws Exception{
        
        // Spring-retry提供了RetryOperations接口的實作類RetryTemplate
        RetryTemplate template = new RetryTemplate();
        
        // 簡單重試政策
        SimpleRetryPolicy policy = new SimpleRetryPolicy(); // 預設重試3次
        // 設定重試政策
        template.setRetryPolicy(policy);
        
        // 1. 固定退避政策
        FixedBackOffPolicy backOffPolicy1 = new FixedBackOffPolicy();
        backOffPolicy1.setBackOffPeriod(2000); // Default back off period - 1000ms.

        // 2. 預設退避政策 不等待,直接重試
        NoBackOffPolicy backOffPolicy2 = new NoBackOffPolicy();
        
        // 3. 均勻随機退避政策 
        UniformRandomBackOffPolicy backOffPolicy3 = new UniformRandomBackOffPolicy();
        // 等待時間為 最小退避時間 + [0, 最大退避時間 - 最小退避時間)間的一個随機數 (如果最大退避時間等于最小退避時間那麼等待時間為0)
        backOffPolicy3.setMinBackOffPeriod(2000); // Default min back off period - 500ms.
        backOffPolicy3.setMaxBackOffPeriod(5000); // Default max back off period - 1500ms.
        
        // 設定退避政策
        template.setBackOffPolicy(backOffPolicy1);
//        template.setBackOffPolicy(backOffPolicy2);
//        template.setBackOffPolicy(backOffPolicy3);
        
        // 執行
        String result = template.execute(
                new RetryCallback<String, Exception>() {
                    
                    public String doWithRetry(RetryContext context) throws Exception {
                        SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
                        int number = new Random().nextInt(10) + 1;
                        if (number > 5) {
                            
                            System.out.println("執行Buiness logic. NPE" + "開始執行時間:" + s.format(new Date()));
                            // 抛出異常
                            throw new NullPointerException("NullPointerException");
                        } else {
                            
                            System.out.println("執行Buiness logic. TE" + "開始執行時間:" + s.format(new Date()));
                            // 抛出異常
                            throw new TimeoutException("TimeoutException");
                        }
                    }
                },
                // 當重試執行完閉,操作還未成為,那麼可以通過RecoveryCallback完成一些失敗事後處理。
                new RecoveryCallback<String>() {
                    public String recover(RetryContext context) throws Exception {
                        
                        return "failed callback";
                    }
                }
                );
        
        System.out.println(result);
    }
    
    
    /**
     * 繼承BackOff接口的接口
     * SleepingbackOffPolicy 接口下的退避政策
     * 
     */
    @SuppressWarnings("serial")
    @Test
    public void sleepingbackOffPolicyTest() throws Exception{
        
        // Spring-retry提供了RetryOperations接口的實作類RetryTemplate
        RetryTemplate template = new RetryTemplate();
        
        // 簡單重試政策
        SimpleRetryPolicy policy = new SimpleRetryPolicy();
        // 重試5次
        policy.setMaxAttempts(5);
        // 設定重試政策
        template.setRetryPolicy(policy);
        
        
        // 1. 指數退避政策
        ExponentialBackOffPolicy backOffPolicy1 = new ExponentialBackOffPolicy();
        // 每次等待時間為 等待時間 = 等待時間 * N ,即每次等待時間為上一次的N倍。 (如果等待時間超過最大等待時間,那麼以後的等待時間為最大等待時間。)
        // 以下設定 初始時間間隔為2000毫秒,N = 3,¸最大間隔為6000毫秒,那麼從第3次重試開始,以後每次等待時間都為6000毫秒。
        backOffPolicy1.setInitialInterval(2000);// 等待時間 The default 'initialInterval' value - 100 millisecs.
        backOffPolicy1.setMultiplier(3);// 等待倍數 The default 'multiplier' value - value 2 (100% increase per backoff).
        backOffPolicy1.setMaxInterval(6000);//最大等待時間  The default maximum backoff time (30 seconds).
        
        // 2. 指數随機退避政策
        ExponentialRandomBackOffPolicy backOffPolicy2 = new ExponentialRandomBackOffPolicy();
        // 算法是 等待時間 = 等待時間 * (1 + Random(随機數) * (N - 1))
        backOffPolicy2.setInitialInterval(2000);// 等待時間 The default 'initialInterval' value - 100 millisecs.
        backOffPolicy2.setMultiplier(3);// 等待倍數 The default 'multiplier' value - value 2 (100% increase per backoff).
        
        // 3. 固定退避政策
        FixedBackOffPolicy backOffPolicy3 = new FixedBackOffPolicy();
        backOffPolicy3.setBackOffPeriod(2000); // Default back off period - 1000ms.
        // 使用者自定義 sleeper 不知道能幹啥 不懂
        Sleeper sleeper3 = new Sleeper() {
            
            @Override
            public void sleep(long backOffPeriod) throws InterruptedException {
                // 不了解這個的作用
                TimeUnit.SECONDS.sleep(10);
                System.out.println("sleeper");
            }
        };
        backOffPolicy3.setSleeper(sleeper3);
        
        // 4. 均勻随機退避政策 
        UniformRandomBackOffPolicy backOffPolicy4 = new UniformRandomBackOffPolicy();
        // 等待時間為 最小退避時間 + [0, 最大退避時間 - 最小退避時間)間的一個随機數 (如果最大退避時間等于最小退避時間那麼等待時間為0)
        backOffPolicy4.setMinBackOffPeriod(2000); // Default min back off period - 500ms.
        backOffPolicy4.setMaxBackOffPeriod(5000); // Default max back off period - 1500ms.
        // 使用者自定義 sleeper 不知道能幹啥 不懂
        Sleeper sleeper4 = new Sleeper() {
            
            @Override
            public void sleep(long backOffPeriod) throws InterruptedException {
             // 不了解這個的作用
                TimeUnit.SECONDS.sleep(10);
                System.out.println("sleeper");
            }
        };
        backOffPolicy4.setSleeper(sleeper4);
        
        
        // 設定退避政策
        template.setBackOffPolicy(backOffPolicy1);
//        template.setBackOffPolicy(backOffPolicy2);
//        template.setBackOffPolicy(backOffPolicy3);
//        template.setBackOffPolicy(backOffPolicy4);
        
        // 執行
        String result = template.execute(
                new RetryCallback<String, Exception>() {
                    
                    public String doWithRetry(RetryContext context) throws Exception {
                        SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
                        int number = new Random().nextInt(10) + 1;
                        if (number > 5) {
                            
                            System.out.println("執行Buiness logic. NPE" + "開始執行時間:" + s.format(new Date()));
                            // 抛出異常
                            throw new NullPointerException("NullPointerException");
                        } else {
                            
                            System.out.println("執行Buiness logic. TE" + "開始執行時間:" + s.format(new Date()));
                            // 抛出異常
                            throw new TimeoutException("TimeoutException");
                        }
                    }
                },
                // 當重試執行完閉,操作還未成為,那麼可以通過RecoveryCallback完成一些失敗事後處理。
                new RecoveryCallback<String>() {
                    public String recover(RetryContext context) throws Exception {
                        
                        return "failed callback";
                    }
                }
                );
        
        System.out.println(result);
    }
    
    /**
     * 監聽器
     * 
     */
    @Test
    public void retryListenerTest() throws Exception{
        
        // Spring-retry提供了RetryOperations接口的實作類RetryTemplate
        RetryTemplate template = new RetryTemplate();
        
        // 簡單重試政策
        SimpleRetryPolicy policy = new SimpleRetryPolicy(); // 預設重試3次
        // 設定重試政策
        template.setRetryPolicy(policy);
        
        // 固定時間退避政策
        FixedBackOffPolicy backOffPolicy1 = new FixedBackOffPolicy();
        backOffPolicy1.setBackOffPeriod(2000); // Default back off period - 1000ms.
        // 設定退避政策
        template.setBackOffPolicy(backOffPolicy1);
        
        /**
         * 
         * 上述代碼注冊了兩個Listener,Listener中的三個實作方法,onError, open, close會在執行重試操作時被調用,
         * 在RetryTemplate中doOpenInterceptors, doCloseInterceptors, doOnErrorInterceptors
         * 會調用監聽器對應的open, close, onError 方法。
         *
         * doOpenInterceptors方法在第一次重試之前會被調用,如果該方法傳回true,則會繼續向下直接,如果傳回false,則抛出異常,停止重試。
         * 
         * doOnErrorInterceptors 在抛出異常後執行,
         *   
         * doCloseInterceptors 會在重試操作執行完畢後調用。
         * 
         */
        
        // 監聽器1
        RetryListener retryListener1 = new RetryListener() {
            
            @Override
            public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
                System.out.println("1-open");
                return true;
            }
            
            @Override
            public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback,
                    Throwable throwable) {
                System.out.println("1-onError");
            }
            
            @Override
            public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback,
                    Throwable throwable) {
                System.out.println("1-close");
            }
        };
        
        // 監聽器1
        RetryListener retryListener2 = new RetryListener() {
            
            @Override
            public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
                System.out.println("2-open");
                return true;
            }
            
            @Override
            public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback,
                    Throwable throwable) {
                System.out.println("2-onError");
            }
            
            @Override
            public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback,
                    Throwable throwable) {
                System.out.println("2-close");
            }
        };
        // 監聽組
        RetryListener[] listeners = {retryListener1, retryListener2};
        /*
         *  設定監聽器  當注冊多個Listener時,open方法按會按Listener的注冊順序調用,
         *  而onError和close則按Listener注冊的順序逆序調用。
         *  
         *  open    初始重試調用
         *  onError 發生異常就調用
         *  close   重試次數全部結束調用
         */
        template.setListeners(listeners);
        
        // 執行
        String result = template.execute(
                new RetryCallback<String, Exception>() {
                    
                    public String doWithRetry(RetryContext context) throws Exception {
                        SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
                        int number = new Random().nextInt(10) + 1;
                        if (number > 5) {
                            
                            System.out.println("執行Buiness logic. NPE" + "開始執行時間:" + s.format(new Date()));
                            // 抛出異常
                            throw new NullPointerException("NullPointerException");
                        } else {
                            
                            System.out.println("執行Buiness logic. TE" + "開始執行時間:" + s.format(new Date()));
                            // 抛出異常
                            throw new TimeoutException("TimeoutException");
                        }
                    }
                },
                // 當重試執行完閉,操作還未成為,那麼可以通過RecoveryCallback完成一些失敗事後處理。
                new RecoveryCallback<String>() {
                    public String recover(RetryContext context) throws Exception {
                        
                     // TODO 推送次數,推送完畢後,還沒有成功,發送預警短信,通知人工處理
                        
                        return "FAIL";
                    }
                }
                );
        
        System.out.println(result);
    }
    
    
    
    @Test
    public void realRetryListenerTest() throws Exception{
        
        // Spring-retry提供了RetryOperations接口的實作類RetryTemplate
        RetryTemplate template = new RetryTemplate();
        
        // 簡單重試政策
        SimpleRetryPolicy policy = new SimpleRetryPolicy(); // 預設重試3次
        // 設定重試政策
        template.setRetryPolicy(policy);
        
        // 固定時間退避政策
        FixedBackOffPolicy backOffPolicy1 = new FixedBackOffPolicy();
        backOffPolicy1.setBackOffPeriod(2000); // Default back off period - 1000ms.
        // 設定退避政策
        template.setBackOffPolicy(backOffPolicy1);
        // 監聽器1
        RetryListener retryListener1 = new RetryListener() {
            
            @Override
            public <T, E extends Throwable> boolean open(RetryContext context, RetryCallback<T, E> callback) {
                System.out.println("1-open");
                return true;
            }
            
            @Override
            public <T, E extends Throwable> void onError(RetryContext context, RetryCallback<T, E> callback,
                    Throwable throwable) {
                System.out.println("1-onError");
                System.out.println("save FAIL notify info");
            }
            
            @Override
            public <T, E extends Throwable> void close(RetryContext context, RetryCallback<T, E> callback,
                    Throwable throwable) {
                if (context.getRetryCount() < NOTIFY_RETRY_TIMES) {
                    // TODO 表示沒有到達重試次數 就成功了。在這兒入庫,儲存通知成功資訊。
                    System.out.println("save SUCCESS notify info");
                }
                System.out.println("1-close" + "重試了" + context.getRetryCount() +"次");
            }
        };
        // 監聽組
        RetryListener[] listeners = {retryListener1};
        template.setListeners(listeners);
        
        // 執行
        String result = template.execute(
                new RetryCallback<String, Exception>() {
                    
                    public String doWithRetry(RetryContext context) throws Exception {
                        SimpleDateFormat s = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss:SSS");
                        int number = new Random().nextInt(10) + 1;
                        if (number > 5) {
                            
                            System.out.println("執行Buiness logic. NPE" + "開始執行時間:" + s.format(new Date()));
                            // 抛出異常
                            throw new NullPointerException("NullPointerException");
                        }
                        
                        return "SUCCESS";
                    }
                },
                // 當重試執行完閉,操作還未成為,那麼可以通過RecoveryCallback完成一些失敗事後處理。
                new RecoveryCallback<String>() {
                    public String recover(RetryContext context) throws Exception {
                        
                       
                        
                        return "FAIL";
                    }
                }
            );
        
        System.out.println(result);
    }
}
           

tips:如有疑問,歡迎評論交流。