玄星幻月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.8java.version><maven.compiler.source>8maven.compiler.source><maven.compiler.target>8maven.compiler.target><spring-cloud-alibaba.version>2.2.9.RELEASEspring-cloud-alibaba.version>properties><parent><groupId>org.springframework.bootgroupId><artifactId>spring-boot-starter-parentartifactId><version>2.3.12.RELEASEversion><relativePath/> parent><dependencyManagement><dependencies><dependency><groupId>com.alibaba.cloudgroupId><artifactId>spring-cloud-alibaba-dependenciesartifactId><version>${spring-cloud-alibaba.version}version><type>pomtype><scope>importscope>dependency>dependencies>dependencyManagement><dependencies><dependency><groupId>com.alibaba.cloudgroupId><artifactId>spring-cloud-starter-alibaba-nacos-configartifactId>dependency><dependency><groupId>com.alibaba.cloudgroupId><artifactId>spring-cloud-starter-alibaba-nacos-discoveryartifactId>dependency><dependency><groupId>org.springframework.bootgroupId><artifactId>spring-boot-starterartifactId>dependency><dependency><groupId>org.springframework.bootgroupId><artifactId>spring-boot-starter-webartifactId>dependency>dependencies>
bootstrap.yml
spring:application:name: myconfigcloud:nacos:discovery:server-addr: localhost:8848config:server-addr: localhost:8848file-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 {@Resourceprivate 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*/@Overridepublic 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";@Overridepublic String getStrategyName() {return STRATEGY_NAME;}@Overridepublic void createPlan() {System.out.println("create plan " + STRATEGY_NAME);}@Overridepublic String queryPlan() {return "result: " + STRATEGY_NAME;}@Overridepublic 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";@Overridepublic String getStrategyName() {return STRATEGY_NAME;}@Overridepublic void createPlan() {System.out.println("create plan " + STRATEGY_NAME);}@Overridepublic String queryPlan() {return "result: " + STRATEGY_NAME;}@Overridepublic 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 层来获取,哈哈。有时候遇到想不通的问题的时候,所有的逻辑都通了,稍微远离以下问题本身,也许就想通了,就差那么灵光一闪。
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
