background

It is inevitable that we will write repetitive code in our work, so we need to have some abstraction ability, for example, we can extract common logic into abstract classes, and we can use some utility classes to avoid redundant code

Today’s article is about extracting the retry function of a service invocation from a utility class for reuse. To simplify the service call to a method call, the foo method is called as follows:

public static List<String> foo(a) {// No exception is thrown
    System.out.println("Call method");
        // Simulate throwing an exception
    System.out.println(1/0);
    List<String> list = new ArrayList<>();
    list.add("1");
    return list;
}
Copy the code

The caller and retry logic are as follows:

List<String> result = null;
// Number of retries
int retryCount = 0;
// Switch of invoking services, enabled by default
boolean callSwitch = true;
The service is invoked as long as the call service switch is on and the number of retries is not greater than the maximum number of retries
while (callSwitch && retryCount <= 3) {
    try {
        // Invoke the service
        result = foo();
        // Turn off the call service switch
        callSwitch = false;
    } catch (Exception e) {
        // An exception occurred (such as a timeout, which requires a retry)
        // The service invocation switch is turned on
        callSwitch = true; retryCount++; }}// Process the result
Copy the code

In fact, the above code has already been solved, the logic of service retry, after the test has no problem, we can submit the code for colleagues to help carry out CR, but my classmate saw this code, and gave some suggestions:

Abstract a layer and propose a Retry utility class so that everyone can easily reuse your retry logic

Abstract thought process

White tooth thought, also rightness, that put forward a tool class, but sent a moment to stay, unexpectedly have no clue (reflected the abstract ability weak, usually should pay more attention to abstract ability cultivation). Seeing this, small friends gave a hint, white teeth immediately on the keyboard crackling percussion up, there is the following tool class

Mainly rely on the functional interfaces Supplier and BiFunction

public class RetryUtil {
    public static <T> T retry(int maxRetryCount, Supplier<T> supplier, BiFunction<T, Exception, Boolean> consumer) {
        T result = null;
        Exception exception = null;

        int retryCount = 0;
        boolean callMethod = true;
        while (callMethod && retryCount <= maxRetryCount) {
            try {
                // Get the result of calling the service
                result = supplier.get();
            } catch (Exception e) {
                If the number of retries is not less than the maximum number of retries, an exception is thrown and we hand the exception handling to the business side
                if (retryCount >= maxRetryCount) {
                    throw e;
                }
                exception = e;  
            }
            // Judge the result
            callMethod = consumer.apply(result, exception);
            if(callMethod) { retryCount++; }}returnresult; }}Copy the code

The code for the business caller is as follows:

List<String> result1 = retry(3.// Maximum number of retries
                ()-> foo(),// Invoke the service(list, e) -> e ! =null || list == null || list.isEmpty());// Process the result
Copy the code

After the self-test, I submitted the code and asked my friend to give it to CR. After staring at it for a while, I had the following dialogue

Friend: “Retry method does not throw an exception”

White teeth: “The called service does not show a throw exception, so there is no throw exception”

Small friend: “that person if the service side show throw exception?”

White teeth: “I’ll change it again”

The server displays an exception that was thrown, so the Retry method displays an exception that was thrown, but the caller displays an exception that was handled, as shown below:

public static List<String> foo(a) throws Exception{// display an exception thrown
    System.out.println("Call method");
        // Simulate throwing an exception
    System.out.println(1/0);
    List<String> list = new ArrayList<>();
    list.add("1");
    return list;
}
Copy the code
public class RetryUtil {
    public static <T> T retry(int maxRetryCount, Supplier<T> supplier, BiFunction<T, Exception, Boolean> consumer) throws Exception{
        / / to omit...
}
Copy the code

This happens because the Supplier’s get method does not throw an exception

@FunctionalInterface
public interface Supplier<T> {
    /**
     * Gets a result.
     *
     * @return a result
     */
    T get(a);
}
Copy the code

Since you don’t support it, write one yourself, and here is the DspSupplier. The main difference between DspSupplier and Supplier is that the exception is thrown in the get method

@FunctionalInterface
interface DspSupplier<T> {
    /**
     * Gets a result.
     *
     * @return a result
     */
    T get(a) throws Exception;
}
Copy the code

The Retry method then looks like this

public class RetryUtil {
    public static <T> T retry(int maxRetryCount, DspSupplier<T> supplier, BiFunction<T, Exception, Boolean> consumer) throws Exception{
        / / to omit...
}
Copy the code

By using the custom Supplier, the caller is no longer “Unhandled Exception”

conclusion

In our usual redevelopment process, we can try to use functional interface to achieve some logical extraction, make a tool class for everyone to use, simplify manpower, but also to improve our coding ability.

The above case is relatively simple, but the sparrow is small, all five organs, but also a good experience welcome to pay attention to the public number [every day sun white teeth], get the latest article, we communicate together, common progress!