springboot基于注解方式实现策略模式
在项目开发的过程中,我们经常会遇到很多的业务分支,一般我们都采用switch或者if else简单粗暴来处理,当每次我们需要新增一个分支业务逻辑时,就需要修改整体业务逻辑。
因此在项目中,可以采用策略模式进行优化
例如:
使用策略模式发送不同的信息
1、定义策略分类
/**
*@Description 定义策略分类
*@Author aogula
*@Date 2021/1/7
*@Time 13:57
*/
public enum MsgType {
/**
* 微信消息
*/
WECHAT_MSG,
/**
* 短信消息
*/
SMS_MSG;
}
2、定义策略注解
/**
*@Description 策略注解
*@Author aogula
*@Date 2021/1/7
*@Time 14:00
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
public @interface Type {
MsgType value();
}
3、定义策略接口
/**
*@Description 策略接口
*@Author aogula
*@Date 2021/1/7
*@Time 13:58
*/
public interface MsgHandler {
/**
* 发送信息
* @param msg
*/
void handleMsg(String msg);
}
4、定义实现类
4.1短信消息实现类
/**
*@Description 短信消息策略实现类
*@Author aogula
*@Date 2021/1/7
*@Time 13:59
*/
@Component
@Type(value = MsgType.SMS_MSG)
public class SmsMsgHandler implements MsgHandler {
@Override
public void handleMsg(String msg) {
System.out.println("发送短信信息:" + msg);
}
}
4.2微信消息实现类
/**
*@Description 微信消息策略实现类
*@Author aogula
*@Date 2021/1/7
*@Time 14:00
*/
@Component
@Type(value = MsgType.WECHAT_MSG)
public class WechatMsgHandler implements MsgHandler{
@Override
public void handleMsg(String msg) {
System.out.println("发送微信信息: " + msg);
}
}
5、策略初始化
5.1获取策略
/**
* Description:
*
* @author: aogula
* @date Created on 2021/1/7 13:40
*/
@Component
public class MsgHandlerContext {
/**
* 注入bean上下文
*/
@Autowired
private ApplicationContext applicationContext;
/**
* 存放所有策略类Bean的map
*/
public static Map<MsgType, Class<MsgHandler>> msgTypeClassMap= new HashMap<>();
public MsgHandler getMsgHandler(MsgType type) {
Class<MsgHandler> baseMsgHandlerClass = msgTypeClassMap.get(type);
// 可以定义全局异常进行处理
if(baseMsgHandlerClass==null) throw new IllegalArgumentException("没有类型");
// 从容器中获取对应的策略Bean
return applicationContext.getBean(baseMsgHandlerClass);
}
}
5.2策略注册
/**
*@Description 将策略注册到map容器中
*@Author aogula
*@Date 2021/1/7
*@Time 14:08
*/
@Component
public class MsgConfig implements ApplicationContextAware {
@Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
//获取所有策略注解的Bean
applicationContext.getBeansWithAnnotation(Type.class).
entrySet().iterator().forEachRemaining(entrySet -> {
Class<MsgHandler> baseMsgHandler = (Class<MsgHandler>) entrySet.getValue().getClass();
MsgType value = baseMsgHandler.getAnnotation(Type.class).value();
// 将class加入MsgHandlerContext的map中,注解的value作为key
MsgHandlerContext.msgTypeClassMap.put(value, baseMsgHandler);
});
}
}
6、测试接口
@RestController
@RequestMapping("/task")
public class TaskController {
@Autowired
private MsgHandlerContext msgHandlerContext;
@GetMapping("msg")
public void handleMsg(MsgType msg) {
MsgHandler handler = msgHandlerContext.getMsgHandler(msg);
handler.handleMsg("我是具体的一条信息,hello world!");
}
}
7、结果
8、问题
如果下面这句代码出现空指针异,说明实现类走了JDK代理模式,导致无法获取到注解
MsgType value = baseMsgHandler.getAnnotation(Type.class).value();
解决:
方法一:可以将策略接口类修改为抽象类,让实现类走Cglib代理
方法二:合理规划包路径,因为扫描包路径过大,导致所使用的的bean被spring代理
解决方法可以参考其他文章:https://www.jianshu.com/p/b69e64121b97