玄星幻月004-spring使用策略模式及nacos动态配置策略

前言

前几天有个需求是根据不同的用户,会走不同的业务,由于业务之间的耦合性比较复杂,如果使用 if-else 语法的话,改动的地方比较多,代码更加丑陋,产生代码负债。于是想到了策略模式来实现这个功能,根据用户来确定具体的策略,并将策略交给 spring 来管理,只要将原来的一部分代码抽离出来,和新的需求构成两个策略来实现,既可以对原来的代码将修改降到最低,也提高了代码的扩展性,更加符合开闭原则。
在此写了一个 demo,可以在以后在遇到就可以套用了。

一、环境准备

为了模拟根据不同用户走不同的业务,使用 nacos 作为配置中心来模拟不同用户确定的不同分支(为了实现动态配置策略,这里卡的时间比较久)。
为了省事,找了一些 demo,但是由于和我的 nacos (1.X和 2.X之间的差距还是比较大的,后期有时间了去看看具体源码) 和springBoot 的版本不兼容问题,也走了很多弯路,如果是匹配的就不用处理这个问题,如果不一样,先解决版本问题,以下是我执行完成的环境信息。
JDK:1.8
springboot、springcloud、nacos 版本见 pom.xml 文件

	<properties>
        <java.version>1.8</java.version>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <spring-cloud-alibaba.version>2.2.9.RELEASE</spring-cloud-alibaba.version>
	</properties>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.3.12.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>com.alibaba.cloud</groupId>
                <artifactId>spring-cloud-alibaba-dependencies</artifactId>
                <version>${spring-cloud-alibaba.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>
    
    <dependencies>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-config</artifactId>
        </dependency>
        <dependency>
            <groupId>com.alibaba.cloud</groupId>
            <artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

bootstrap.yml

spring:
  application:
    name: myconfig
  cloud:
    nacos:
      discovery:
        server-addr: localhost:8848
      config:
        server-addr: localhost:8848
        file-extension: yml

nacos 配置如下
在这里插入图片描述

二、代码

代码结构

在这里插入图片描述

策略模式具体实现

策略模式类图

主要是根据这个 demo 画的,不过策略模式的类图基本结构就是这样的。
在这里插入图片描述

策略抽象类

实现 InitializingBean 接口,重写afterPropertiesSet() 将策略bean交给 spring

import org.springframework.beans.factory.InitializingBean;

import javax.annotation.Resource;

/**
 * 实现 InitializingBean 接口提供了 bean 被 BeanFactory 设置了所有属性后执行的处理结果
 */
public abstract class APlanStrategy implements InitializingBean {

    @Resource
    private PlanStrategyContext context;

    /**
     * 策略名称
     * 
     * @return 策略名称
     */
    public abstract String getStrategyName();

    /**
     * 业务方法 1
     */
    public abstract void createPlan();

    /**
     * 业务方法 2
     */
    public abstract String queryPlan();

    /**
     * 业务方法 3
     */
    public abstract String handlerPlan();

    /**
     * 后置处理方法
     * @throws Exception Exception
     */
    @Override
    public void afterPropertiesSet() throws Exception {
        // 将具体的实现类注册到策略上下文中
        context.register(this);
        // spring 启动时可以查看具体注入 spring 的策略
        System.out.println("注册策略" + this.getStrategyName());
    }
}
策略A

使用 @Component 交个 spring 管理

import org.springframework.stereotype.Component;

@Component
public class PlanAStrategy extends APlanStrategy {

    /**
     * 策略名称
     */
    private static final String STRATEGY_NAME = "PLAN_A";
    
    @Override
    public String getStrategyName() {
        return STRATEGY_NAME;
    }

    @Override
    public void createPlan() {
        System.out.println("create plan " + STRATEGY_NAME);
    }

    @Override
    public String queryPlan() {
        return "result: " + STRATEGY_NAME;
    }

    @Override
    public String handlerPlan() {
        return "handler plan..." + STRATEGY_NAME;
    }
}
策略B

使用 @Component 交个 spring 管理

import org.springframework.stereotype.Component;

@Component
public class PlanBStrategy extends APlanStrategy {

    /**
     * 策略名称
     */
    private static final String STRATEGY_NAME = "PLAN_B";

    @Override
    public String getStrategyName() {
        return STRATEGY_NAME;
    }

    @Override
    public void createPlan() {
        System.out.println("create plan " + STRATEGY_NAME);
    }

    @Override
    public String queryPlan() {
        return "result: " + STRATEGY_NAME;
    }

    @Override
    public String handlerPlan() {
        return "handler plan..." + STRATEGY_NAME;
    }
}
策略上下文

使用 @Component 交个 spring 管理
@RefreshScope 监听 nacos 中配置文件修改后即使更新变化。

import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

@Component
@RefreshScope
public class PlanStrategyContext {

    /**
     * 将具体策略存放到 strategyMap
     */
    public static Map<String, APlanStrategy> strategyMap = new HashMap<>();

    /**
     * 将具体策略存放到 strategyMap 中
     * 策略抽象类中调用
     *
     * @param strategy strategy
     */
    public void register(APlanStrategy strategy) {
        strategyMap.put(strategy.getStrategyName(), strategy);
    }

    /**
     * nacos 中读取需要的具体策略
     */
    @Value("${planName}")
    private String planName;

    /**
     * 根据 nacos 配置动态获取 bean
     * 可以根据业务来获具体的策略 bean
     *
     * @return APlanStrategy
     */
    public APlanStrategy getOneStrategy() {
        return strategyMap.get(planName);
    }
}

三、启动项目调试

策略bean注入spring

启动时,会将所有继承抽象类的具体实现策略注入到 spring
在这里插入图片描述

执行策略A

在这里插入图片描述

执行策略B

在这里插入图片描述

四、后记

在策略上下文中获取具体策略的时候,刚开始想不来怎么才能根据具体的配置动态获取bean的,想了好久,其实很简单,context 本身也是一个 Bean,所有的策略 bean 已经在 map 中了,想办法根据配置获取到具体的 bean 即可,即使配置在数据库中,也可以写 sql 调用 DTO 层来获取,哈哈。有时候遇到想不通的问题的时候,所有的逻辑都通了,稍微远离以下问题本身,也许就想通了,就差那么灵光一闪。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值