美文网首页
巧用ThreadLocal实现责任链模式计数器(二)

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

作者: 像鸣人 | 来源:发表于2019-10-20 19:46 被阅读0次

    上一篇: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<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();
        }
    }
    
    1. 基类
    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);
        }
    
    }
    
    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实现责任链模式计数器(二)

          本文链接:https://www.haomeiwen.com/subject/leqbuctx.html