责任链设计模式
介绍:
客户端发出一个请求,链上的对象都有机会来处理这一请求,而客户端不需要知道谁是具体的处理对象。这样就实现了请求者和接受者之间的解耦,并且在客户端可以实现动态的组合职责链。使编程更有灵活性。
定义:使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。其过程实际上是一个递归调用。
要点主要是:
1、有多个对象共同对一个任务进行处理。
2、这些对象使用链式存储结构,形成一个链,每个对象知道自己的下一个对象。
3、一个对象对任务进行处理,可以添加一些操作后将对象传递个下一个任务。也可以在此对象上结束任务的处理,并结束任务。
4、客户端负责组装链式结构,但是客户端不需要关心最终是谁来处理了任务。
结构图:
优缺点:
优点:
职责链模式的最主要功能就是:动态组合,请求者和接受者解耦。
请求者和接受者松散耦合:请求者不需要知道接受者,也不需要知道如何处理。每个职责对象只负责自己的职责范围,其他的交给后继者。各个组件间完全解耦。
动态组合职责:职责链模式会把功能分散到单独的职责对象中,然后在使用时动态的组合形成链,从而可以灵活的分配职责对象,也可以灵活的添加改变对象职责。
缺点:
产生很多细粒度的对象:因为功能处理都分散到了单独的职责对象中,每个对象功能单一,要把整个流程处理完,需要很多的职责对象,会产生大量的细粒度职责对象。
不一定能处理:每个职责对象都只负责自己的部分,这样就可以出现某个请求,即使把整个链走完,都没有职责对象处理它。这就需要提供默认处理,并且注意构造链的有效性。
代码:
代码结构:
数据库:
CREATE TABLE `gateway_handler` (
`ID` int(11) NOT NULL AUTO_INCREMENT COMMENT '主键ID',
`handler_name` varchar(32) DEFAULT NULL COMMENT 'handler名称',
`handler_id` varchar(32) DEFAULT NULL COMMENT 'handler主键id',
`prev_handler_id` varchar(32) DEFAULT NULL,
`next_handler_id` varchar(32) DEFAULT NULL COMMENT '下一个handler',
PRIMARY KEY (`ID`)
) ENGINE=InnoDB AUTO_INCREMENT=19 DEFAULT CHARSET=utf8 COMMENT='权限表';
-- ----------------------------
-- Records of gateway_handler
-- ----------------------------
INSERT INTO `gateway_handler` VALUES ('1', 'Api接口限流', 'currentLimitHandler', null, 'blacklistHandler');
INSERT INTO `gateway_handler` VALUES ('2', '黑名单拦截', 'blacklistHandler', 'currentLimitHandler', 'conversationHandler');
INSERT INTO `gateway_handler` VALUES ('3', '会话验证', 'conversationHandler', 'blacklistHandler', null);
maven依赖:
<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>2.0.1.RELEASE</version> </parent> <dependencies> <!-- sprinboot web --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.projectlombok</groupId> <artifactId>lombok</artifactId> <version>1.16.10</version> </dependency> <dependency> <groupId>commons-lang</groupId> <artifactId>commons-lang</artifactId> <version>2.6</version> </dependency> <dependency> <groupId>org.mybatis.spring.boot</groupId> <artifactId>mybatis-spring-boot-starter</artifactId> <version>1.1.1</version> </dependency> <!-- mysql 依赖 --> <dependency> <groupId>mysql</groupId> <artifactId>mysql-connector-java</artifactId> </dependency> </dependencies>
HandlerController
package com.ldq.controller; import com.ldq.handler.GetWayHandler; import com.ldq.service.GatewayHandlerService; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; @RestController public class HandlerController { @Autowired private GatewayHandlerService gatewayHandlerService; @RequestMapping("/client") public String client() { GetWayHandler firstGatewayHandler = gatewayHandlerService.getFirstGateWayHandler(); firstGatewayHandler.service(); return "success"; } } BlacklistHandler
package com.ldq.handler.impl; import com.ldq.handler.GetWayHandler; import org.springframework.stereotype.Component; /** * @Auther: liangdeqiang * @Date: 2019-5-10 14:37 * @Description: */ @Component public class BlacklistHandler extends GetWayHandler { @Override public void service() { System.out.println("第二关 黑名单拦截......."); nextService(); } }
ConversationHandler
package com.ldq.handler.impl; import com.ldq.handler.GetWayHandler; import org.springframework.stereotype.Component; /** * @Auther: liangdeqiang * @Date: 2019-5-10 14:37 * @Description: */ @Component public class ConversationHandler extends GetWayHandler { public void service() { System.out.println("第三关 用户的会话信息拦截......."); nextService(); } }
CurrentLimitHandler
package com.ldq.handler.impl; import com.ldq.handler.GetWayHandler; import org.springframework.stereotype.Component; /** * @Auther: liangdeqiang * @Date: 2019-5-10 14:37 * @Description: */ @Component public class CurrentLimitHandler extends GetWayHandler { public void service() { System.out.println("第一关 API接口限流操作....."); nextService();// 指向下一关黑名单 } }
GetWayHandler
package com.ldq.handler; /** * @Auther: liangdeqiang * @Date: 2019-5-10 14:33 * @Description: */ public abstract class GetWayHandler { /** * 指向下一个handler */ protected GetWayHandler nextGetWayHandler; /** * 实现的handler,处理方案,强制致实现 */ public abstract void service(); public void setNextGetWayHandler(GetWayHandler nextGetWayHandler) { this.nextGetWayHandler = nextGetWayHandler; } /** * 指向下一关 */ protected void nextService(){ if(nextGetWayHandler != null){ nextGetWayHandler.service(); } } }
GatewayHandlerEntity
package com.ldq.mapper.entity; import lombok.Data; import java.io.Serializable; @Data public class GatewayHandlerEntity implements Serializable, Cloneable { /** * 主键ID */ private Integer id; /** * handler名称 */ private String handlerName; /** * handler主键id */ private String handlerId; /** * 下一个handler */ private String nextHandlerId; }
GatewayHandlerMapper
package com.ldq.mapper; import com.ldq.mapper.entity.GatewayHandlerEntity; import org.apache.ibatis.annotations.Select; public interface GatewayHandlerMapper { /** * 获取第一个GatewayHandler * * @return */ @Select("SELECT handler_name AS handlerName,handler_id AS handlerid ,prev_handler_id AS prevhandlerid ,next_handler_id AS nexthandlerid FROM gateway_handler WHERE prev_handler_id is null;;") public GatewayHandlerEntity getFirstGatewayHandler(); @Select("SELECT handler_name AS handlerName,handler_id AS handlerid ,prev_handler_id AS prevhandlerid ,next_handler_id AS nexthandlerid FROM gateway_handler WHERE handler_id=#{handlerId}") public GatewayHandlerEntity getByHandler(String handlerId); }
GatewayHandlerService
package com.ldq.service; import com.ldq.handler.GetWayHandler; import com.ldq.mapper.GatewayHandlerMapper; import com.ldq.mapper.entity.GatewayHandlerEntity; import com.ldq.utils.SpringUtils; import org.apache.commons.lang.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; /** * @Auther: liangdeqiang * @Date: 2019-5-10 14:41 * @Description: */ @Service public class GatewayHandlerService { @Autowired private GatewayHandlerMapper gatewayHandlerMapper; public GetWayHandler getFirstGateWayHandler() { //1.从数据库中拿到第一对象 GatewayHandlerEntity firstGatewayHandlerEntity = gatewayHandlerMapper.getFirstGatewayHandler(); if (firstGatewayHandlerEntity == null) { return null; } //2.获取对象的handlerId及beanId String handlerId = firstGatewayHandlerEntity.getHandlerId(); //3.根据beanId获取实例对象 GetWayHandler firstGatewayHandler = SpringUtils.getBean(handlerId, GetWayHandler.class); //4.获取下一个beanId String nextHandlerId = firstGatewayHandlerEntity.getNextHandlerId(); //5.记录循环的上一个实例 GetWayHandler tempGatewayHandler = firstGatewayHandler; while (!StringUtils.isEmpty(nextHandlerId)) { //6.从spring容器中获取下一个handler对象 GetWayHandler nextGatewayHandler = SpringUtils.getBean(nextHandlerId, GetWayHandler.class); //7.设置第一个实例对象的指向下一个handler对象 tempGatewayHandler.setNextGetWayHandler(nextGatewayHandler); //8.通过数据库查询下一个实例 GatewayHandlerEntity nextGatewayHandlerEntity = gatewayHandlerMapper.getByHandler(nextHandlerId); if (nextGatewayHandlerEntity == null) { break; } //9.拿到下一个beanId nextHandlerId = nextGatewayHandlerEntity.getNextHandlerId(); tempGatewayHandler = nextGatewayHandler; } return firstGatewayHandler; } }
SpringUtils
package com.ldq.utils; import org.springframework.beans.BeansException; import org.springframework.context.ApplicationContext; import org.springframework.context.ApplicationContextAware; import org.springframework.stereotype.Component; @Component public class SpringUtils implements ApplicationContextAware { private static ApplicationContext applicationContext; @Override public void setApplicationContext(ApplicationContext applicationContext) throws BeansException { this.applicationContext = applicationContext; } //获取applicationContext public static ApplicationContext getApplicationContext() { return applicationContext; } //通过name获取 Bean. public static Object getBean(String name){ return getApplicationContext().getBean(name); } //通过class获取Bean. public static <T> T getBean(Class<T> clazz){ return getApplicationContext().getBean(clazz); } //通过name,以及Clazz返回指定的Bean public static <T> T getBean(String name,Class<T> clazz){ return getApplicationContext().getBean(name, clazz); } }
AppHandler
package com.ldq; import org.mybatis.spring.annotation.MapperScan; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * @Auther: liangdeqiang * @Date: 2019-5-10 15:02 * @Description: */ @SpringBootApplication @MapperScan("com.ldq.mapper") public class AppHandler { public static void main(String[] args) { SpringApplication.run(AppHandler.class); } }
application.yml
###服务启动端口号 server: port: 8080 spring: datasource: url: jdbc:mysql://127.0.0.1:3306/design_pattern?useUnicode=true&characterEncoding=UTF-8 username: ...... password: ...... driver-class-name: com.mysql.jdbc.Driver ####打印MyBatias日志 logging: level: ### 开发环境使用DEBUG 生产环境info或者error com.ldq.member.mapper: DEBUG