上一篇:https://www.jianshu.com/writer#/notebooks/40052435/notes/54239948
请选择阅读上一篇了解事件的来龙去脉才好!!!
上一篇说到选择方案二:
每次调用都初始化计数器,保证不遗漏策略;
如何做?
- 将计数器声明为类属性;
- 在每个策略中添加代码,如果当前策略可以处理,则将计数器重置为0;
能否解决问题?
答案:不完全可以(后面讲为什么不完全可以),而且太Low了,将不属于策略处理的逻辑硬生生的塞了进去,耦合性太强,也不利于维护(后续添加新策略时,忘记这茬了咋办???)。
如何做?忽然想起点什么……
对了,AOP啊!!!像处理日志、数据库事务那样,做成切面,用着清爽,维护方便!!!
于是乎,AOP出现了,那么,问题解决了么?
解决了一半,还有一半未解决!!!还有什么呢???对了,并发,没错,就是并发啊!使用类变量,遇到并发时怎么办???
仔细思考如下场景:
用户一:请求向北走,此时计数器为2;
用户二:请求向东走,此时计数器为2,继续走,发现仍然找不到相应策略……
使用类属性做为计数器,失败了!!!
那么,如何解决并发请求时计数器的准确性?对,线程隔离!!!
OK,ThreadLocal闪亮登场!!!
上面基本分析了整个设计流程,应该是比较清晰的了,如果有不清晰的可以留言讨论!!!
言归正传,开始上最终方案代码:
- ThreadLocal计数器
public class StrategyCounter {
/**
* 计数器
*/
private final static ThreadLocal<AtomicInteger> STRATEGY_COUNTER = new ThreadLocal<>();
/**
* 设置计数器
*/
public static void set() {
STRATEGY_COUNTER.set(new AtomicInteger(0));
}
/**
* 获取当前计数器值
*
* @return
*/
public static int getValue() {
return STRATEGY_COUNTER.get().intValue();
}
/**
* 获取当前计数器值并++
*
* @return
*/
public static int getAndIncrement() {
return STRATEGY_COUNTER.get().getAndIncrement();
}
/**
* 清除计数器
*/
public static void clear() {
STRATEGY_COUNTER.remove();
}
}
- 基类
public interface IStrategy<T> {
/**
* 处理方法
*
* @param payload
*/
void handle(T payload, IStrategy strategy);
}
@Slf4j
public class BaseStrategy<T> implements IStrategy<T> {
/**
* 策略链
*/
private final List<IStrategy> strategies = Lists.newArrayList();
/**
* 注册策略
*
* @param strategy
*/
public BaseStrategy register(IStrategy strategy) {
strategies.add(strategy);
return this;
}
/**
* 策略链调用
*
* @param payload
* @param strategy
*/
@Override
public void handle(T payload, IStrategy strategy) {
if (StrategyCounter.getValue() >= strategies.size()) {
log.warn("无匹配的处理策略,请检查!请求:{}", payload);
return;
}
// 获取策略后索引++
IStrategy currStrategy = strategies.get(StrategyCounter.getAndIncrement());
// 策略处理
currStrategy.handle(payload, this);
}
}
- AOP处理器
@Target({ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface Strategy {
}
@Order(2)
@Component
@Aspect
public class StrategyAdvisor {
@Before(value = "@annotation(strategy)")
public void before(Strategy strategy) {
StrategyCounter.set();
}
@After(value = "@annotation(strategy)")
public void after(Strategy strategy) {
StrategyCounter.clear();
}
}
- 使用
@Service
public class StrategyService {
private BaseStrategy strategy;
@PostConstruct
public void init() {
strategy = new BaseStrategy();
// 注册策略链
strategy.register(new EastStrategy())
.register(new SouthStrategy())
.register(new WestStrategy())
.register(new NorthStrategy());
}
/**
* 处理类
*
* @param o
*/
@Strategy
public void handle(BasePayload o) {
// 由策略链处理
strategy.handle(o, strategy);
}
}
最后
现在Java方向AOP最火的莫过于Spring了,本文也是结合Spring一起使用;
需要注意:调用策略时,一定要经过AOP处理(最好将策略调用定义为单独Service,从根本上避免不走AOP错误),否则可能导致调用失败!!!
--end
网友评论