Background introduction
We inevitably write repetitive code in our work, so we need to be abstract, such as extracting common logic into abstract classes, or avoiding redundant code through some tool classes.
This article today extracts a tool class from a retry function that invokes a service for reuse.For the sake of introduction, the calling service is simplified as a method call, and the foo method invoked is as follows:
public static List<String> foo() {// No throw exception shown System.out.println("Call Method"); // Analog throws an exception System.out.println(1/0); List<String> list = new ArrayList<>(); list.add("1"); return list; }
The caller and retry logic is as follows:
List<String> result = null; // retry count int retryCount = 0; // Switch calling service, turned on by default boolean callSwitch = true; // The service is invoked as long as the invocation service switch is on and the number of retries is not greater than the maximum number of retries while (callSwitch && retryCount <= 3) { try { // Call Service result = foo(); // Omitting the validation of the results, assuming that there is no problem here, turn off the call service switch callSwitch = false; } catch (Exception e) { // An exception has occurred (such as a timeout, which requires retrying) // Service call switch on callSwitch = true; retryCount++; } } // Processing result later
In fact, the above code has been solved, the logic of service retry, after the test has no problems, you can submit the code for colleagues to help CR, but after the little friend saw the code, gave suggestions:
You can abstract a layer and suggest a retry tool class so that everyone can easily reuse your retry logic
Abstract thinking process
White teeth think, yeah, then put forward a tool class, but after a while, I didn't have any idea (reflecting the weakness of abstraction ability, we should pay more attention to the cultivation of abstraction ability in peacetime).My little friend saw me and gave me a hint. White teeth immediately cracked on the keyboard and there were the following tools
Mainly dependent on 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 it over to the business party if (retryCount >= maxRetryCount) { throw e; } exception = e; } // Judge the results callMethod = consumer.apply(result, exception); if (callMethod) { retryCount++; } } return result; } }
The code for the business caller is as follows:
List<String> result1 = retry(3,// max retries ()-> foo(),// Call Service (list, e) -> e != null || list == null || list.isEmpty());// Processing results
After the self-test is OK, submit the code to let the little friend give CR a look. After the little friend stares for a while, he has the following conversation
Little friend: "The retry method did not throw an exception"
White teeth:'The invoked service does not show a throw exception, and there is no throw here'
Xiao Peng: "What if the person shows an exception thrown by the server?"
White teeth: "I'll change it again"
The service side shows that an exception was thrown, so the retry method also shows that an exception was thrown, but the caller shows the exception that will be handled, as follows:
public static List<String> foo() throws Exception{// Show Thrown Exceptions System.out.println("Call Method"); // Analog throws an exception System.out.println(1/0); List<String> list = new ArrayList<>(); list.add("1"); return list; }
public class RetryUtil { public static <T> T retry(int maxRetryCount, Supplier<T> supplier, BiFunction<T, Exception, Boolean> consumer) throws Exception{ // Omit... }
This happens because Supplier's get method did not throw an exception
@FunctionalInterface public interface Supplier<T> { /** * Gets a result. * * [@return](https://my.oschina.net/u/556800) a result */ T get(); }
Since you don't support it, write your own chop, and you have the following DspSupplier, the main difference between it and Supplier is that it shows that an exception was thrown in the get method
@FunctionalInterface interface DspSupplier<T> { /** * Gets a result. * * [@return](https://my.oschina.net/u/556800) a result */ T get() throws Exception; }
So the retry method becomes
public class RetryUtil { public static <T> T retry(int maxRetryCount, DspSupplier<T> supplier, BiFunction<T, Exception, Boolean> consumer) throws Exception{ // Omit... }
When a custom Supplier is used, there is no "Unhandled exception" for the caller
summary
In the process of our ordinary redevelopment, we can try to use the functional interface to achieve some logical extraction and make it into a tool class for everyone to use, simplify human resources, and also improve their coding ability.
The above case is relatively simple, but sparrows are small and full of viscera, which is also a good experience Welcome to the Public Number, Get the latest articles, and we can communicate and make progress together!