基于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;
        }
    }
}
上一篇:超大文件中在有限的内存里找到单词频率 top 100


下一篇:springboot总结(一)