当前位置: 首页 > 科技观察

巧妙地使用责任链模型,让你的代码脱颖而出!

时间:2023-03-19 21:56:15 科技观察

本文转载自微信公众号《Java极客技术》,作者鸭血范。转载本文请联系Java极客技术公众号。1.简介什么是责任链模式?(ChainofResponsibilityPattern),简单来说,就是在请求者和接收者之间建立一个对象处理链接,避免将请求发送者和接收者耦合在一起!例如,如下图:从设计的角度来看,责任链模式涉及四种角色:请求角色:可以是外部请求,也可以是内部请求,最终体现为请求数据体;抽象处理器角色:定义了一些基本的处理规范;具体处理器作用:实现或继承抽象处理器,完成具体的计算任务;then作用:用于接受请求数据的最终处理结果;下面就来看看具体的实际应用吧!2.实际开发中的例子,很多时候都免不了要和其他公司对接。大多数请求参数被加密后发送到互联网。下面我们以请求参数的校验和打包为例,来讲解一下责任链模型的玩法,实现流程如下!我们先写一个加密工具类,使用的是AES加密算法)));}/***encryption*@paramcontent*@paramkey*@return*@throwsException*/publicstaticStringencrypt(Stringcontent,Stringkey){try{SecretKeySpecsecretKeySpec=newSecretKeySpec(key.getBytes(),AES);Ciphercipher=密码.getInstance(AES_CVC_PKC);IvParameterSpeciv=newIvParameterSpec(newbyte[16]);cipher.init(Cipher.ENCRYPT_MODE,secretKeySpec,iv);字节[]加密ted=cipher.doFinal(content.getBytes());returnBase64.getEncoder().encodeToString(encrypted);}catch(Exceptione){log.warn("AES加密失败,参数:{},错误信息:{}",content,ExceptionUtils.getStackTrace(e));return"";}}/***解密*@paramcontent*@paramkey*@return*@throwsException*/publicstaticStringdecrypt(Stringcontent,Stringkey){try{SecretKeySpecsecretKeySpec=newSecretKeySpec(key.getBytes(),AES);Ciphercipher=Cipher.getInstance(AES_CVC_PKC);IvParameterSpeciv=newIvParameterSpec(newbyte[16]);cipher.init(Cipher.DECRYPT_MODE,secretKeySpec,iv);字节[]加密=Base64.getDecoder().decode(content);byte[]original=cipher.doFinal(encrypted);returnnewString(original,"UTF-8");}catch(Exceptione){log.warn("AES破解失败,参数:{},错误信息:{}",content,ExceptionUtils.getStackTrace(e));return"";}}publicstaticvoidmain(String[]args)throwsException{Stringkey="1234567890123456";Stringcontent="{\"userCode\":\"zhangsan\",\"userPwd\":\"123456\"}";StringencryptContext=encrypt(content,"1234567890123456");System.out.println("加密内容:"+encryptContext);StringdecryptContext=decrypt(encryptContext,key);System.out.println("解密内容:"+decryptContext);}}执行结果如下:加密内容:5ELORDsYKxCz6Ec377udct7dBMI74ZtJDCFL4B3cpoBsPC8ILH/aiaRFnZa/oTC5解密内容:{"userCode":"zhangsan","userPwd":"123456"}content可以看作是请求者传递的参数!同时创建数据记录的上下文实体类ServiceContext/***Context*/publicclassServiceContext{/***请求参数*/privateStringrequestParam;/***解密数据*/privateStringjsonData;/***UserAccount*/privateStringuserCode;/***UserPassword*/privateStringuserPwd;//省略set\getpublicServiceContext(){}publicServiceContext(StringrequestParam){this.requestParam=requestParam;}}然后,创建handler接口HandleInterceptpublicinterfaceHandleIntercept{/***处理参数*@paramcontext*@return*/ServiceContexthandle(ServiceContextcontext);}接下来创建两个handler实现类,使用Numberofrequests进行参数解密,业务数据验证/***解密根据*/publicclassDecodeDataHandleimplementsHandleIntercept{privateStringkey="1234567890123456";@OverridepublicServiceContexthandle(ServiceContextcontext){StringjsonData=AESUtil.decrypt(context.getRequestParam(),key);if(StringUtils.isEmpty(jsonData))"{thrownewIllegalArgument(解密失败)"}context.setJsonData(jsonData);returncontext;}}/***验证业务数据并封装*/publicclassValidDataHandleimplementsHandleIntercept{@OverridepublicServiceContexthandle(ServiceContextcontext){StringjsonData=context.getJsonData();JSONObjectjsonObject=JSONObject.parseObject(jsonData);if(!jsonObject.containsKey("userCode")){thrownewIllegalArgumentException("userCode不能为空");}context.setUserCode(jsonObject.getString("userCode"));if(!jsonObject.containsKey("userPwd")){thrownewIllegalArgumentException("userPwdcannotbeempty");}context.setUserPwd(jsonObject.getString("userPwd"));returncontext;}}最后创建一个处理链接管理器HandleChain/***请求处理链接管理器*/publicclassHandleChain{privateListhandleInterceptList=newArrayList<>();/***addhandler*@paramhandleIntercept*/publicvoidaddHandle(HandleIntercepthandleIntercept){handleInterceptList.add(handleIntercept);}/***执行处理*@paramcontext*@return*/publicServiceContextexecute(ServiceContextcontext){if(!handleInterceptList.isEmpty()){for(HandleIntercepthandleIntercept:handleInterceptList){context=handleIntercept.handle(context);}}returncontext;}}完成之后,我们写一个测试类ChainClientTestpublicclassChainClientTest{publicstaticvoidmain(String[]args){//获取请求参数StringrequestParam="5ELORDsYKxCz6Ec377udct7dBMI74ZtJDCFL4B3cpoBsPC8ILH/aiaRFnZa/oTC5";//封装请求参数Addque;ServiceContext/restContext(link)HandleChainhainhandle();andleChainhandleChain.addHandle(newDecodeDataHandle());//解密处理handleChain.addHandle(newValidDataHandle());//数据校验处理//执行处理链,得到处理结果serviceContext=handleChain.execute(serviceContext);System.out.println("Processingresult:"+JSONObject.toJSONString(serviceContext));}}执行后结果如下:Processingresult:{"jsonData":"{\"userCode\":\"zhangsan\",\"userPwd\":\"123456\"}","re??questParam":"5ELORDsYKxCz6Ec377udct7dBMI74ZtJDCFL4B3cpoBsPC8ILH/aiaRFnZa/oTC5","userCode":"zhangsan","userPwd":"123456"}可以清楚的看到数据发送了从请求者处理器链接后,数据被封装到上下文中!如果想继续验证用户和密码是否合法,可以继续添加新的处理者完成数据处理验证!如果是传统方式,可能是多个if嵌套,类似如下:if(condition){if(condition){if(condition){//业务处理}}},这种模式最大的缺点是可读性很差,而且代码不容易维护!责任链是从接口层封装和判断的,可扩展性很强!三、责任链模式的应用场景,这个就不多说了,最典型的就是Servlet中的Filter,通过上面的分析大家应该能明白责任链模式的工作原理在Servlet中,然后为什么每个Filter都需要在web.xml中进行配置,其实本质就是在处理器中注册过滤器publicclassTestFilterimplementsFilter{publicvoiddoFilter(ServletRequestrequest,ServletResponseresponse,FilterChainchain)throwsIOException,ServletException{chain.doFilter(request,response);}publicvoiddestroy(){}publicvoidinit(FilterConfigfilterConfig)throwsServletException{}}四、总结既然责任链模式这么好用,那什么什么时候使用责任链模式?在系统设计中,如果每个if都有一个统一的抽象,比如参数加密、系统数据校验、业务参数校验等,就可以将其抽象出来,用对象处理的方式进行链式调用,不仅优雅,而且便于复用并扩大。五、参考1.五月仓颉——责任链模型