巧用ThreadLocal实现责任链模式计数器(二)

上一篇:https://www.jianshu.com/writer#/notebooks/40052435/notes/54239948
请选择阅读上一篇了解事件的来龙去脉才好!!!

上一篇说到选择方案二

每次调用都初始化计数器,保证不遗漏策略;

如何做?

  1. 将计数器声明为类属性;
  2. 在每个策略中添加代码,如果当前策略可以处理,则将计数器重置为0;

能否解决问题?

答案:不完全可以(后面讲为什么不完全可以),而且太Low了,将不属于策略处理的逻辑硬生生的塞了进去,耦合性太强,也不利于维护(后续添加新策略时,忘记这茬了咋办???)。

如何做?忽然想起点什么……

对了,AOP啊!!!像处理日志、数据库事务那样,做成切面,用着清爽,维护方便!!!

于是乎,AOP出现了,那么,问题解决了么?

解决了一半,还有一半未解决!!!还有什么呢???对了,并发,没错,就是并发啊!使用类变量,遇到并发时怎么办???
仔细思考如下场景:
用户一:请求向北走,此时计数器为2;
用户二:请求向东走,此时计数器为2,继续走,发现仍然找不到相应策略……

使用类属性做为计数器,失败了!!!

那么,如何解决并发请求时计数器的准确性?对,线程隔离!!!

OK,ThreadLocal闪亮登场!!!

上面基本分析了整个设计流程,应该是比较清晰的了,如果有不清晰的可以留言讨论!!!

言归正传,开始上最终方案代码:

  1. ThreadLocal计数器
public class StrategyCounter {

    /**
     * 计数器
     */
    private final static ThreadLocal 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();
    }
}
  1. 基类
public interface IStrategy {

    /**
     * 处理方法
     *
     * @param payload
     */
    void handle(T payload, IStrategy strategy);

}
@Slf4j
public class BaseStrategy implements IStrategy {

    /**
     * 策略链
     */
    private final List 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);
    }

}
  1. 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();
    }

}
  1. 使用
@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

你可能感兴趣的:(巧用ThreadLocal实现责任链模式计数器(二))