Spring责任链模式使用实例讲解
weng@ 人气:0前言
在工作中会存在这种情况某些业务场景会对一份数据的属性进行多次设置,这种情况在传统的代码风格就是在一个方法中对应属性进行依次的set这种编码会在以后的业务迭代中造成很大的耦合,代码复用程度很低,后期维护成本会变大,这个时候就可以使用责任链模式对业务set这个步骤进行拆分做到可插拔式,从而做到减少迭代成本和维护成本对以后接手项目的同事友好
开始编写案例
我这里以电商项目中的商品属性设置为例productModel
1.开始搭建一个spring boot 项目
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>org.example</groupId> <artifactId>spring-batch</artifactId> <version>1.0-SNAPSHOT</version> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.2.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <maven.compiler.source>8</maven.compiler.source> <maven.compiler.target>8</maven.compiler.target> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> </dependency> <!-- https://mvnrepository.com/artifact/org.projectlombok/lombok --> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.18.24</version> <scope>provided</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
2.创建对应案例数据
package com.wym.pojo; import lombok.Data; /** * 商品基础模型 */ @Data public class ProductModel { private Integer id; private String productCode; private String productName; private String productTitle; private String productTag; private String productRegion; private Integer stockNumber; private String brandName; }
package com.wym.pojo; import lombok.Data; /** * 商品附带参数 方便后期设置参数 */ @Data public class ProductParamModel { private String vendorCode; }
3.创建责任链处理器
创建一个处理接口,子类根据自己的业务特色去实现接口,对数据进行加工
/** * 商品处理 */ public interface ProductPostProcessor { /** * 商品加工 * @param productModelList 商品 * @param productParamModel 处理参数 */ void productDataProcess(List<? extends ProductModel> productModelList, ProductParamModel productParamModel); }
这里我设置3个常用的商品处理
/** * 商品品牌设置 */ @Component @Slf4j @Order(1) //为了后期方便业务操作顺序编排 public class ProductBrandMappingPostProcessor implements ProductPostProcessor { @Override public void productDataProcess(List<? extends ProductModel> productModelList, ProductParamModel productParamModel) { log.error("进入商品品牌设置"); } }
/** * 商品映射设置 */ @Component @Slf4j @Order(2) public class ProductCodeMappingPostProcessor implements ProductPostProcessor { @Override public void productDataProcess(List<? extends ProductModel> productModelList, ProductParamModel productParamModel) { log.error("进入商品映射设置"); } }
/** * 商品库存设置 */ @Slf4j @Component @Order(3) public class ProductStockNumberPostProcessor implements ProductPostProcessor { @Override public void productDataProcess(List<? extends ProductModel> productModelList, ProductParamModel productParamModel) { log.error("进入商品库存设置"); } }
这个时候就已经创建好了责任链处理器,接下来就是开始业务调用
4.创建mvc调用
controller
@RequestMapping("/test") @Slf4j @RestController public class ProductController { @Autowired private ProductServer productServer; @GetMapping("/query") public String queryProduct() { productServer.queryProduct(); return "成功"; } }
server
public interface ProductServer { /** * 查询商品 * @return List<ProductModel> */ public List<ProductModel> queryProduct(); }
impl 这里就是核心点如何操作这个责任链 ApplicationListener 这个取获取容器中的处理器,并排序
@Service @Slf4j public class ProductServerImpl implements ProductServer, ApplicationListener<ContextRefreshedEvent> { private static List<ProductPostProcessor> productPostProcessorList; /** * 处理排序 * * @param contextRefreshedEvent 环 * @return */ private List<ProductPostProcessor> getProductPostProcesser(ContextRefreshedEvent contextRefreshedEvent) { //子容器加载完毕不进这个逻辑 if (contextRefreshedEvent.getApplicationContext().getParent() == null) { //监听容器加载完毕 Map<String, ProductPostProcessor> beansOfType = contextRefreshedEvent.getApplicationContext().getBeansOfType(ProductPostProcessor.class); log.error("商品后置处理器注入"); List<ProductPostProcessor> postProcessList = new ArrayList<>(beansOfType.values()); postProcessList.sort((a, b) -> { Order aOrder = AnnotationUtils.findAnnotation(a.getClass(), Order.class); Order bOrder = AnnotationUtils.findAnnotation(b.getClass(), Order.class); if (aOrder == null && bOrder == null) { return 0; } if (aOrder == null) { return 1; } if (bOrder == null) { return -1; } return aOrder.value() - bOrder.value(); } ); return postProcessList; } return null; } @Override public void onApplicationEvent(ContextRefreshedEvent contextRefreshedEvent) { List<ProductPostProcessor> postProcessor = getProductPostProcesser(contextRefreshedEvent); if (!CollectionUtils.isEmpty(postProcessor)) { productPostProcessorList = postProcessor; } } @Override public List<ProductModel> queryProduct() { ProductModel productModel = new ProductModel(); productModel.setId(0); productModel.setProductCode(""); productModel.setProductName(""); productModel.setProductTitle(""); productModel.setProductTag(""); productModel.setProductRegion(""); productModel.setStockNumber(0); productModel.setBrandName(""); ProductParamModel productParamModel = new ProductParamModel(); productParamModel.setVendorCode("1231"); productPostProcessorList.forEach(item -> item.productDataProcess(Arrays.asList(productModel), productParamModel)); return null; } }
进行验证
调用 http://localhost:8080/test/query 就会在控制台中打印出
2023-01-09 11:07:42.178 ERROR 25096 --- [nio-8080-exec-2] c.w.p.i.ProductBrandMappingPostProcessor : 进入商品品牌设置
2023-01-09 11:07:42.178 ERROR 25096 --- [nio-8080-exec-2] c.w.p.i.ProductCodeMappingPostProcessor : 进入商品映射设置
2023-01-09 11:07:42.179 ERROR 25096 --- [nio-8080-exec-2] c.w.p.i.ProductStockNumberPostProcessor : 进入商品库存设置
这样就做到了业务分离对后期的维护有帮助
加载全部内容