基于SpringBoot注解实现策略模式

Posted 言成言成啊

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了基于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;
        
    

以上是关于基于SpringBoot注解实现策略模式的主要内容,如果未能解决你的问题,请参考以下文章

基于SpringBoot实现一个简单的权限控制注解

SpringBoot自定义注解+异步+观察者模式实现业务日志保存

SpringBoot2.0 基础案例(13):基于Cache注解模式,管理Redis缓存

SpringBoot EnableAsync无效 Async注解不异步

Spring 实现策略模式--自定义注解方式解耦if...else

springboot 基于@Scheduled注解 实现定时任务