1. 程式人生 > 其它 >基於SpringBoot註解實現策略模式

基於SpringBoot註解實現策略模式

原始碼meethigher/springboot-strategy-mode

參考文章

還是來自於工作上的一點心得。之前我做的資料庫的通用呼叫儲存過程的程式碼,是使用抽象工廠來實現的,裡面有if..else..的操作。如果要頻繁的新加資料庫實現邏輯,就要不斷的新增實現類和else if。這邊就想用註解的方式,來去除if..else..

一、簡單demo

案例:傳送不同型別的訊息

建立註解

@Target({ElementType.TYPE})//作用在類上
@Retention(RetentionPolicy.RUNTIME)//當前被描述的註解,會保留到class位元組碼檔案中,並被jvm讀取到。一般也只會用到這個
@Documented//註解被抽取到api文件中
@Inherited//註解被子類繼承
public @interface MsgType {
    MessageType value();
}

建立型別

public enum MessageType {
    /**
     * 微信·
     */
    WECHAT_MSG,
    /**
     * 簡訊
     */
    SMS_MSG
}

建立介面

public interface MessageHandler {

    /**
     * 傳送訊息
     * @param msg
     */
    String sendMessage(String msg);
}

建立SMS實現類

@Service
@MsgType(value = MessageType.SMS_MSG)
public class SmsMessageHandler implements MessageHandler {
    @Override
    public String sendMessage(String msg) {
        String message = "簡訊訊息:" + msg;
        System.out.println(message);
        return message;
    }
}

建立WECHAT實現類

@Service
@MsgType(value = MessageType.WECHAT_MSG)
public class WechatMessageHandler implements MessageHandler {
    @Override
    public String sendMessage(String msg) {
        String message = "微信訊息:" + msg;
        System.out.println(message);
        return message;
    }
}

建立配置類

  1. 通過註解拿到所有被標註的bean類
  2. 遍歷所有bean,拿到bean的型別、位元組碼
  3. 將型別、位元組碼存入全域性map
  4. 使用時,通過型別,將位元組碼取出,instance或者通過spring放入bean容器
@Component
public class MessageConfig implements ApplicationContextAware {
    private static Map<MessageType, Class<MessageHandler>> messageTypeClassMap = new HashMap<>();

    @Autowired
    private ApplicationContext applicationContext;


    /**
     * 1. 通過註解拿到所有被標註的bean類
     * 2. 遍歷所有bean,拿到bean的型別、位元組碼
     * 3. 將型別、位元組碼存入全域性map
     * 4. 使用時,通過型別,將位元組碼取出,instance或者通過spring放入bean容器
     *
     * @param applicationContext
     * @throws BeansException
     */
    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        //比較平易近人的寫法
        Map<String, Object> beans = applicationContext.getBeansWithAnnotation(MsgType.class);
        Iterator<String> iterator = beans.keySet().iterator();
        while (iterator.hasNext()) {
            String beanName = iterator.next();
            @SuppressWarnings("unchecked")
            Class<MessageHandler> messageHandlerClass = (Class<MessageHandler>) beans.get(beanName).getClass();
            MessageType messageType = messageHandlerClass.getAnnotation(MsgType.class).value();
            messageTypeClassMap.put(messageType, messageHandlerClass);
        }
        //比較裝逼的寫法
//        //獲取所有帶有指定註解的Bean物件
//        applicationContext.getBeansWithAnnotation(MsgType.class)
//                .entrySet()
//                .iterator()
//                .forEachRemaining(stringObjectEntry -> {
//                    Class<MessageHandler> messageHandlerClass = (Class<MessageHandler>) stringObjectEntry.getValue().getClass();
//                    MessageType messageType = messageHandlerClass.getAnnotation(MsgType.class).value();
//                    messageTypeClassMap.put(messageType, messageHandlerClass);
//                });
    }

    /**
     * 通過型別拿到例項化的物件
     * @param messageType
     * @return
     */
    public MessageHandler getMessageHandler(MessageType messageType) {
        Class<MessageHandler> messageHandlerClass = messageTypeClassMap.get(messageType);
        if (ObjectUtils.isEmpty(messageHandlerClass)) {
            throw new IllegalArgumentException("沒有指定型別");
        }
        return applicationContext.getBean(messageHandlerClass);
    }
}

二、實際案例

案例:根據頻次來進行工作,頻次有,一天一次,三天一次,七天一次,十天一次

建立列舉

public enum WorkFrequency {

    /**
     * 一天一次
     */
    ONE_DAY_PER_TIMES("1天/次", "0", "oneDayPerTimes"),
    /**
     * 三天一次
     */
    THREE_DAY_PER_TIMES("3天/次", "1", "threeDayPerTimes"),
    /**
     * 七天一次
     */
    SEVEN_DAY_PER_TIMES("7天/次", "2", "sevenDayPerTimes"),
    /**
     * 十天一次
     */
    TEN_DAY_PER_TIMES("10天/次", "3", "tenDayPerTimes"),
    ;
    public final String name;
    public final String value;
    public final String uniqueCode;

    WorkFrequency(String name, String value, String uniqueCode) {
        this.name = name;
        this.value = value;
        this.uniqueCode = uniqueCode;
    }

    public static WorkFrequency getByUniqueCode(String uniqueCode) {
        for (WorkFrequency frequency : WorkFrequency.values()) {
            if (frequency.uniqueCode.equals(uniqueCode)) {
                return frequency;
            }
        }
        return null;
    }
}

建立註解

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface FrequencyAnnotation {
    WorkFrequency value();
}

建立介面

public interface WorkFrequencyHandler {


    /**
     * 今天是否應該執行
     *
     * @param lastExecuteTime
     * @return
     */
    boolean isTodayShouldExecute(Long lastExecuteTime);
}

建立實現類

public class WorkTimeUtils {
    /**
     * 兩個時間相差天數
     *
     * @param startTime Date日期
     * @param endTime   Date日期
     * @return
     */
    public static int intervalDays(Date startTime, Date endTime) {
        Calendar cal1 = Calendar.getInstance();
        cal1.setTime(startTime);
        Calendar cal2 = Calendar.getInstance();
        cal2.setTime(endTime);
        int day1 = cal1.get(Calendar.DAY_OF_YEAR);
        int day2 = cal2.get(Calendar.DAY_OF_YEAR);
        int year1 = cal1.get(Calendar.YEAR);
        int year2 = cal2.get(Calendar.YEAR);
        /*同一年 */
        if (year1 != year2) {
            int timeDistance = 0;
            for (int i = year1; i < year2; i++) {
                if ((((i % 4) == 0) && ((i % 100) != 0)) || ((i % 400) == 0)) {
                    /* 閏年 */
                    timeDistance += 366;
                } else {
                    /*不是閏年 */
                    timeDistance += 365;
                }
            }
            return (timeDistance + (day2 - day1));
        } else {
            /*不同年 */
            return (day2 - day1);
        }
    }
}


@Service
@FrequencyAnnotation(value = WorkFrequency.ONE_DAY_PER_TIMES)
public class OneDayOnceHandler implements WorkFrequencyHandler {

    /**
     * 間隔的天數
     */
    private final Integer INTERVAL_DAY = 1;


    @Override
    public boolean isTodayShouldExecute(Long lastExecuteTime) {
        long currentTimeMillis = System.currentTimeMillis();
        int realIntervalDay = WorkTimeUtils.intervalDays(lastExecuteTime, currentTimeMillis);
        if (realIntervalDay >= INTERVAL_DAY) {
            return true;
        } else {
            return false;
        }
    }
}

@Service
@FrequencyAnnotation(value = WorkFrequency.THREE_DAY_PER_TIMES)
public class ThreeDayOnceHandler implements WorkFrequencyHandler {
    /**
     * 間隔的天數
     */
    private final Integer INTERVAL_DAY = 3;


    @Override
    public boolean isTodayShouldExecute(Long lastExecuteTime) {
        long currentTimeMillis = System.currentTimeMillis();
        int realIntervalDay = WorkTimeUtils.intervalDays(lastExecuteTime, currentTimeMillis);
        if (realIntervalDay >= INTERVAL_DAY) {
            return true;
        } else {
            return false;
        }
    }
}


@Service
@FrequencyAnnotation(value = WorkFrequency.SEVEN_DAY_PER_TIMES)
public class SevenDayOnceHandler implements WorkFrequencyHandler {
    /**
     * 間隔的天數
     */
    private final Integer INTERVAL_DAY = 7;


    @Override
    public boolean isTodayShouldExecute(Long lastExecuteTime) {
        long currentTimeMillis = System.currentTimeMillis();
        int realIntervalDay = WorkTimeUtils.intervalDays(lastExecuteTime, currentTimeMillis);
        if (realIntervalDay >= INTERVAL_DAY) {
            return true;
        } else {
            return false;
        }
    }
}

@Service
@FrequencyAnnotation(value = WorkFrequency.TEN_DAY_PER_TIMES)
public class TenDayOnceHandler implements WorkFrequencyHandler {
    /**
     * 間隔的天數
     */
    private final Integer INTERVAL_DAY = 10;


    @Override
    public boolean isTodayShouldExecute(Long lastExecuteTime) {
        long currentTimeMillis = System.currentTimeMillis();
        int realIntervalDay = WorkTimeUtils.intervalDays(lastExecuteTime, currentTimeMillis);
        if (realIntervalDay >= INTERVAL_DAY) {
            return true;
        } else {
            return false;
        }
    }
}

新增配置類,通過配置類,直接獲取Service

@Configuration
public class WorkFrequencyConfig implements ApplicationContextAware {
    /**
     * 儲存對應關係
     */
    private static Map<WorkFrequency, Class<WorkFrequencyHandler>> workFrequencyClassMap = new HashMap<>();


    @Autowired
    private ApplicationContext applicationContext;


    @Override
    public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
        applicationContext.getBeansWithAnnotation(FrequencyAnnotation.class)
                .entrySet()
                .iterator()
                .forEachRemaining(stringObjectEntry -> {
                    Class<WorkFrequencyHandler> aClass = (Class<WorkFrequencyHandler>) stringObjectEntry.getValue().getClass();
                    WorkFrequency messageType = aClass.getAnnotation(FrequencyAnnotation.class).value();
                    workFrequencyClassMap.put(messageType, aClass);
                });
    }

    /**
     * 通過型別拿到例項化的物件
     *
     * @param messageType
     * @return
     */
    public WorkFrequencyHandler getFrequencyHandler(WorkFrequency messageType) {
        Class<WorkFrequencyHandler> workFrequencyHandlerClass = workFrequencyClassMap.get(messageType);
        if (ObjectUtils.isEmpty(workFrequencyHandlerClass)) {
            throw new IllegalArgumentException("沒有指定型別");
        }
        return applicationContext.getBean(workFrequencyHandlerClass);
    }
}

所有的配置好了,開始使用了。

@SpringBootTest
public class WorkTest {

    @Autowired
    private WorkFrequencyConfig workFrequencyConfig;

    @Test
    public void test() {
        //模擬
        People people = new People();


        WorkFrequency workFrequency = WorkFrequency.getByUniqueCode(people.getFrequency());
        WorkFrequencyHandler handler = workFrequencyConfig.getFrequencyHandler(workFrequency);
        
        boolean todayShouldExecute = handler.isTodayShouldExecute(people.getLastWorkTime());
        if(todayShouldExecute) {
            System.out.println("今天應該工作");
        }else {
            System.out.println("今天不應該工作");
        }
    }

    static class People {
        private String frequency;

        private Long lastWorkTime;

        public People() {
            //模擬
            this.frequency = "oneDayPerTimes";
            this.lastWorkTime = System.currentTimeMillis();
        }

        public String getFrequency() {
            return frequency;
        }

        public void setFrequency(String frequency) {
            this.frequency = frequency;
        }

        public Long getLastWorkTime() {
            return lastWorkTime;
        }

        public void setLastWorkTime(Long lastWorkTime) {
            this.lastWorkTime = lastWorkTime;
        }
    }
}