Recently I designed an external interface call failure retry retry function, because for many interface calls I am going to adopt policy mode to implement. After thinking that if you use the normal policy pattern, one interface pair should have one implementation class, and if you have one more type of interface, then there will be one more implementation class, you decide to use the enumeration policy pattern.

Failed interface entity

If the interface fails to be called, the url, PARam, method and other information of the failed call will be saved in the database as a record

public class HttpFailEntity {...private Integer apiType;

    private String url;

    privateString param; ...}Copy the code

Timer push

Retrieves data from the library about failed calls to the external interface through timers

List<HttpFailEntity> httpFailEntities;
/ / pushhttpFailEntities.forEach(httpFailEntity -> { PushHttpFailEnum.valueOf(httpFailEntity.getApiType()).push(httpFailEntity);  });Copy the code

Policy pattern processing

Use enumeration strategy to avoid an extra interface and add an implementation class

public enum PushHttpFailEnum {

    /** * id -> service interface */
    ONE(1."A interface") {

        private XXXXService xxxxService = SpringContextHolder.getBean(XXXXService.class);

        @Override
        public Map<String, Object> push(HttpFailEntity httpFailEntity) {
            // do not push
            xxxxService.XXXX();
        }
    },
    TWO(2."B interface") {

        @Override
        public Map<String, Object> push(HttpFailEntity httpFailEntity) {
            // do not push
        }
    },
    .
    .
    .
    OTHER(9999."") {
        @Override
        public Map<String, Object> push(HttpFailEntity httpFailEntity) {
            return null; }};/** ** *@paramHttpFailEntity HTTP push failure class */
    public abstract Map<String, Object> push(HttpFailEntity httpFailEntity);

    /** * Status value */
    @Getter
    private int value;

    /** * state statement */
    @Getter
    private String desc;

    PushHttpFailEnum(int value, String desc) {
        this.value = value;
        this.desc = desc;
    }

    public static PushHttpFailEnum valueOf(int value) {
        returnArrays.stream(PushHttpFailEnum.values()) .filter(a -> a.getValue() == value) .findFirst().orElse(PushHttpFailEnum.ERROR); }}Copy the code

pit

The Service class in the Spring container is used in the implementation abstraction method of interface A, which starts as an annotation and runs null

@Resource
private XXXXService xxxxService;
Copy the code

It turns out that the abstract methods implemented in enumerated classes are essentially anonymous inner classes that the Spring container cannot scan and therefore cannot inject. Finally, the SpringContextHolder static method is used to retrieve the corresponding object from the Spring container.

@Component
public class SpringContextHolder implements ApplicationContextAware {

    private static ApplicationContext applicationContext;

    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        SpringContextHolder.applicationContext = applicationContext;
    }

    public static ApplicationContext getApplicationContext(a) {
        assertApplicationContext();
        return applicationContext;
    }

    public static <T> T getBean(String beanName) {
        assertApplicationContext();
        return (T) applicationContext.getBean(beanName);
    }

    public static <T> T getBean(Class<T> requiredType) {
        assertApplicationContext();
        return applicationContext.getBean(requiredType);
    }

    private static void assertApplicationContext(a) {
        if (SpringContextHolder.applicationContext == null) {
            throw new RuntimeException("ApplicationContext property is null, please check whether SpringContextHolder is injected!"); }}}Copy the code