赞
踩
责任链是一种非常常见的设计模式, 具体我就不介绍了, 本文是讲解如何在SpringBoot中优雅的使用责任链模式
基本步骤如下 :
<?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 https://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.3.5.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <groupId>cn.knightzz</groupId> <artifactId>chain-responsibility-pattern-example</artifactId> <version>0.0.1-SNAPSHOT</version> <name>chain-responsibility-pattern-example</name> <description>责任链模式demo</description> <properties> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <optional>true</optional> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build> </project>
这个类是用于存储实体类的
package cn.knightzz.pattern.dto.req;
/**
* @author 王天赐
* @title: PurchaseTicketReqDTO
* @description:
* @create: 2023-08-29 18:09
*/
public class PurchaseTicketReqDTO {
}
package cn.knightzz.pattern.common.enums; /** * @author 王天赐 * @title: TicketChainMarkEnum * @description: 存储标记责任链的注解 * @create: 2023-08-29 18:10 */ public enum TicketChainMarkEnum { /** * 用于标记购票的责任链过滤器 */ TRAIN_PURCHASE_TICKET_FILTER("train_purchase_ticket_filter"); private String name; TicketChainMarkEnum(String name) { this.name = name; } }
枚举类主要是用于标记某一类业务的责任链
package cn.knightzz.pattern.context; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; import java.lang.annotation.Annotation; import java.util.Map; /** * @author 王天赐 * @title: ApplicationContextHolder * @description: * @create: 2023-08-29 18:31 */ @Component public class ApplicationContextHolder implements ApplicationContextAware { private static ApplicationContext CONTEXT; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { ApplicationContextHolder.CONTEXT = applicationContext; } /** * Get ioc container bean by type. * * @param clazz * @param <T> * @return */ public static <T> T getBean(Class<T> clazz) { return CONTEXT.getBean(clazz); } /** * Get ioc container bean by name and type. * * @param name * @param clazz * @param <T> * @return */ public static <T> T getBean(String name, Class<T> clazz) { return CONTEXT.getBean(name, clazz); } /** * Get a set of ioc container beans by type. * * @param clazz * @param <T> * @return */ public static <T> Map<String, T> getBeansOfType(Class<T> clazz) { return CONTEXT.getBeansOfType(clazz); } /** * Find whether the bean has annotations. * * @param beanName * @param annotationType * @param <A> * @return */ public static <A extends Annotation> A findAnnotationOnBean(String beanName, Class<A> annotationType) { return CONTEXT.findAnnotationOnBean(beanName, annotationType); } /** * Get ApplicationContext. * * @return */ public static ApplicationContext getInstance() { return CONTEXT; } }
ApplicationContextHolder 的作用是, 当Bean被创建时, 将Spring中存储Bean的容器注入到CONTEXT中, 这样我们就可以在其他类中使用 Bean
package cn.knightzz.pattern.chain; import cn.knightzz.pattern.common.enums.TicketChainMarkEnum; import org.springframework.core.Ordered; /** * @author 王天赐 * @title: AbstractChainHandler * @description: * @create: 2023-08-29 18:15 */ public interface AbstractChainHandler<T> extends Ordered { /** * 执行责任链逻辑 * * @param requestParam 责任链执行入参 */ void handler(T requestParam); /** * @return 责任链组件标识 */ String mark(); }
package cn.knightzz.pattern.filter; import cn.knightzz.pattern.chain.AbstractChainHandler; import cn.knightzz.pattern.common.enums.TicketChainMarkEnum; import cn.knightzz.pattern.dto.req.PurchaseTicketReqDTO; /** * @author 王天赐 * @title: TrainPurchaseTicketChainFilter * @description: * @create: 2023-08-29 18:10 */ public interface TrainPurchaseTicketChainFilter<T extends PurchaseTicketReqDTO> extends AbstractChainHandler<PurchaseTicketReqDTO> { @Override default String mark() { return TicketChainMarkEnum.TRAIN_PURCHASE_TICKET_FILTER.name(); } }
通过实现通过责任链接口, 编写默认 mark 方法, 用于标记当前责任链处理器集合
package cn.knightzz.pattern.filter.handler; import cn.knightzz.pattern.dto.req.PurchaseTicketReqDTO; import cn.knightzz.pattern.filter.TrainPurchaseTicketChainFilter; import org.springframework.stereotype.Component; /** * @author 王天赐 * @title: TrainPurchaseTicketParamNotNullChainHandler * @description: * @create: 2023-08-29 18:18 */ @Component public class TrainPurchaseTicketParamNotNullChainHandler implements TrainPurchaseTicketChainFilter<PurchaseTicketReqDTO> { @Override public void handler(PurchaseTicketReqDTO requestParam) { System.out.println("参数不能为空 , 过滤器执行成功"); } @Override public int getOrder() { return 10; } }
package cn.knightzz.pattern.filter.handler; import cn.knightzz.pattern.dto.req.PurchaseTicketReqDTO; import cn.knightzz.pattern.filter.TrainPurchaseTicketChainFilter; import org.springframework.stereotype.Component; /** * @author 王天赐 * @title: TrainPurchaseTicketParamVerifyChainHandler * @description: 购票流程过滤器之验证参数是否有效 * @create: 2023-08-29 18:23 */ @Component public class TrainPurchaseTicketParamVerifyChainHandler implements TrainPurchaseTicketChainFilter<PurchaseTicketReqDTO> { @Override public void handler(PurchaseTicketReqDTO requestParam) { System.out.println("参数合法 , 过滤器执行成功"); } @Override public int getOrder() { return 20; } }
package cn.knightzz.pattern.filter.handler; import cn.knightzz.pattern.dto.req.PurchaseTicketReqDTO; import cn.knightzz.pattern.filter.TrainPurchaseTicketChainFilter; import org.springframework.stereotype.Component; /** * @author 王天赐 * @title: TrainPurchaseTicketRepeatChainHandler * @description: 购票流程过滤器之验证乘客是否重复购买 * @create: 2023-08-29 18:24 */ @Component public class TrainPurchaseTicketRepeatChainHandler implements TrainPurchaseTicketChainFilter<PurchaseTicketReqDTO> { @Override public void handler(PurchaseTicketReqDTO requestParam) { System.out.println("未重复购票 , 过滤器执行成功"); } @Override public int getOrder() { return 30; } }
package cn.knightzz.pattern.context; import cn.knightzz.pattern.chain.AbstractChainHandler; import lombok.extern.slf4j.Slf4j; import org.springframework.boot.CommandLineRunner; import org.springframework.core.Ordered; import org.springframework.stereotype.Component; import org.springframework.util.CollectionUtils; import java.util.*; import java.util.stream.Collectors; /** * @author 王天赐 * @title: AbstractChainContext * @description: CommandLineRunner:SpringBoot 启动完成后执行的回调函数 * @create: 2023-08-29 18:27 */ @Component @Slf4j public final class AbstractChainContext<T> implements CommandLineRunner { // CommandLineRunner:SpringBoot 启动完成后执行的回调函数 // 存储责任链组件实现和责任链业务标识的容器 // 比如:Key:购票验证过滤器 Val:HanlderA、HanlderB、HanlderC、...... private final Map<String, List<AbstractChainHandler>> abstractChainHandlerContainer = new HashMap<>(); public void handler(String mark, T requestParam) { List<AbstractChainHandler> abstractChainHandlers = abstractChainHandlerContainer.get(mark); if (CollectionUtils.isEmpty(abstractChainHandlers)) { throw new RuntimeException(String.format("[%s] Chain of Responsibility ID is undefined.", mark)); } abstractChainHandlers.forEach(each -> each.handler(requestParam)); } @Override public void run(String... args) throws Exception { // 通过ApplicationContextHolder获取所有的Bean Map<String, AbstractChainHandler> chainFilterMap = ApplicationContextHolder.getBeansOfType(AbstractChainHandler.class); chainFilterMap.forEach((beanName, bean) -> { // 获取指定类型的责任链集合, 如果没有就创建 // 需要将同一个mark的放到一起 List<AbstractChainHandler> abstractChainHandlers = abstractChainHandlerContainer.get(bean.mark()); if (CollectionUtils.isEmpty(abstractChainHandlers)) { abstractChainHandlers = new ArrayList<>(); } // 添加到处理器集合中 abstractChainHandlers.add(bean); // 对处理器集合顺序进行排序 List<AbstractChainHandler> actualAbstractChainHandlers = abstractChainHandlers .stream() .sorted(Comparator.comparing(Ordered::getOrder)) .collect(Collectors.toList()); log.info("mark {} , bean : {} add container", bean.mark(), bean); //将排好序的Bean存入到容器等待运行时被调用 abstractChainHandlerContainer.put(bean.mark(), actualAbstractChainHandlers); }); } }
这个类主要是需要实现 CommandLineRunner
接口, 这个接口提供一个run方法, 会在SpringBoot启动后执行
handler 方法
package cn.knightzz.pattern.service; import cn.knightzz.pattern.common.enums.TicketChainMarkEnum; import cn.knightzz.pattern.context.AbstractChainContext; import cn.knightzz.pattern.dto.req.PurchaseTicketReqDTO; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; /** * @author 王天赐 * @title: TicketService * @description: * @create: 2023-08-29 19:04 */ @Service @RequiredArgsConstructor public class TicketService { private final AbstractChainContext<PurchaseTicketReqDTO> purchaseTicketAbstractChainContext; public void purchase(PurchaseTicketReqDTO requestParam) { purchaseTicketAbstractChainContext.handler(TicketChainMarkEnum.TRAIN_PURCHASE_TICKET_FILTER.name(), requestParam); } }
如上面代码所示 , 使用时 直接调用 AbstractChainContext 提供的handler方法既可
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。