言成言成啊 | Kit Chen's Blog

基于SpringBoot注解实现策略模式

发布于2021-11-22 22:41:41,更新于2022-03-02 22:04:09,标签:java open  转载随意,文章会持续修订,请注明来源地址:https://meethigher.top/blog

源码meethigher/springboot-strategy-mode

参考文章

还是来自于工作上的一点心得。之前我做的数据库的通用调用存储过程的代码,是使用抽象工厂来实现的,里面有if..else..的操作。如果要频繁的新加数据库实现逻辑,就要不断的添加实现类和else if。这边就想用注解的方式,来去除if..else..

一、简单demo

案例:发送不同类型的消息

创建注解

1
2
3
4
5
6
7
@Target({ElementType.TYPE})//作用在类上
@Retention(RetentionPolicy.RUNTIME)//当前被描述的注解,会保留到class字节码文件中,并被jvm读取到。一般也只会用到这个
@Documented//注解被抽取到api文档中
@Inherited//注解被子类继承
public @interface MsgType {
MessageType value();
}

创建类型

1
2
3
4
5
6
7
8
9
10
public enum MessageType {
/**
* 微信·
*/
WECHAT_MSG,
/**
* 短信
*/
SMS_MSG
}

创建接口

1
2
3
4
5
6
7
8
public interface MessageHandler {

/**
* 发送消息
* @param msg
*/
String sendMessage(String msg);
}

创建SMS实现类

1
2
3
4
5
6
7
8
9
10
@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实现类

1
2
3
4
5
6
7
8
9
10
@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容器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
@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);
}
}

二、实际案例

案例:根据频次来进行工作,频次有,一天一次,三天一次,七天一次,十天一次

创建枚举

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
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;
}
}

创建注解

1
2
3
4
5
6
7
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface FrequencyAnnotation {
WorkFrequency value();
}

创建接口

1
2
3
4
5
6
7
8
9
10
11
public interface WorkFrequencyHandler {


/**
* 今天是否应该执行
*
* @param lastExecuteTime
* @return
*/
boolean isTodayShouldExecute(Long lastExecuteTime);
}

创建实现类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
@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);
}
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
@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;
}
}
}
发布:2021-11-22 22:41:41
修改:2022-03-02 22:04:09
链接:https://meethigher.top/blog/2021/springboot-strategy-mode/
标签:java open 
付款码 打赏 分享
shift+ctrl+1可控制目录显示