java设计模式(实战)-责任链模式
一:模式说明
模式定义:使多个对象都有机会处理请求,从而避免了请求的发送者和接受者之间的耦合关系。将这些对象连成一条链,并沿着这条链传递该请求,直到有对象处理它为止。
责任链模式的重点是在“链”上,由一条链去处理相似的请求在链中决定谁来处理这个请求,并返回相应的结果(取自《设计模式之禅》)。
翻译:Client对象调用一个处理者(类)的方法,可能有多个处理者(实现类),但是该对象只需要调用第一个处理者(类)即可,该模式会自动分配谁来处理这个请求;这多个处理者继承同一个父类(即在一条链上)。
通用类图如下:
Client发送请求到Handler,Handler自动分配请求到子类的实现类ConcreteHandler中。
二:项目实战
在文章 >手写redis@Cacheable注解 支持过期时间设置< 的基础之上做修改,原版为redis缓存注解实现,
原版实现功能:
- 将数据存放到redis中
- 设置过期时间
原业务逻辑查询人员列表listleader()接口,数据存放redis中,减少数据库负载。
由于业务发展,需要进一步优化查询接口;目前每个人都会操作redis中存放的人员列表,导致该列表会实时发生变动(比如
每个人员对应的分数),每个人都有自己的缓存人员列表而不是统一的人员列表;原列表已经无法满足现需求,每个人第一次登
录都会查询数据库,将自己的列表存放在redis中。
解决方法:设置两级缓存,第一级为该用户(uuid)唯一缓存,key值设置为参数1+uuid+参数2;第二级为第一次登录查询返
回redis中的原始leader列表,key值设置为参数1+参数2。如果当前用户leader列表(一级缓存)为空,则查询原始leader列表
(二级缓存),在操作分数的时候修改二级缓存(初始人员列表)来产生一级缓存,存放进redis,减少了数据库的直接访问。
项目中责任链相关设计类图如下:
说明:抽象类CacheHandler 一是定义了处理请求方法handleMessage;二是定义一个链的编排方法setNext,设置下一个处理者;三是定义了具体的请求者必须实现的两个方法:定义自己能够处理的级别getHandlerLevel和具体的处理任务response;
FirstCacheHadler为一级缓存处理者,SecondCacheHadler为二级缓存处理者。缓存处理的方式通过CacheableAspect类调用。
三:源代码
CacheableAspect:client调用
package com.huajie.aspect; import java.lang.reflect.Method; import java.util.ArrayList; import java.util.List; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut; import org.aspectj.lang.reflect.MethodSignature; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; import com.huajie.annotation.ExtCacheable; import com.huajie.common.cache.CacheHandler; import com.huajie.common.cache.FirstCacheHadler; import com.huajie.common.cache.RedisResult; import com.huajie.common.cache.SecondCacheHadler; import com.huajie.utils.RedisUtil; import com.huajie.utils.StringUtil; /** * redis缓存处理 不适用与内部方法调用(this.)或者private */ @Component @Aspect public class CacheableAspect { @Autowired private RedisUtil redisUtil; @Pointcut("@annotation(com.huajie.annotation.ExtCacheable)") public void annotationPointcut() { } @Around("annotationPointcut()") public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable { // 获得当前访问的class Class<?> className = joinPoint.getTarget().getClass(); // 获得访问的方法名 String methodName = joinPoint.getSignature().getName(); // 得到方法的参数的类型 Class<?>[] argClass = ((MethodSignature) joinPoint.getSignature()).getParameterTypes(); Object[] args = joinPoint.getArgs(); String key = ""; String nextKey = ""; int expireTime = 1800; try { // 得到访问的方法对象 Method method = className.getMethod(methodName, argClass); method.setAccessible(true); // 判断是否存在@ExtCacheable注解 if (method.isAnnotationPresent(ExtCacheable.class)) { ExtCacheable annotation = method.getAnnotation(ExtCacheable.class); key = getRedisKey(args, annotation.key()); nextKey = getRedisKey(args,annotation.nextKey()); expireTime = getExpireTime(annotation); } } catch (Exception e) { throw new RuntimeException("redis缓存注解参数异常", e); } //责任链模式 CacheHandler firstCacheHadler = new FirstCacheHadler(); CacheHandler secondCacheHadler = new SecondCacheHadler(); //设置下级处理者 firstCacheHadler.setNext(secondCacheHadler); //获取处理级别 int cacheLevel = getCacheLevel(key, nextKey); RedisResult result = new RedisResult(redisUtil, key, nextKey, joinPoint, cacheLevel, expireTime); //客户端调用 return firstCacheHadler.HandleMessage(result); } private int getCacheLevel(String key, String nextKey) { if (StringUtil.isNotEmpty(key) && StringUtil.isNotEmpty(nextKey)) { return 2; } else { return 1; } } private int getExpireTime(ExtCacheable annotation) { return annotation.expireTime(); } private String getRedisKey(Object[] args, String primalKey) { // 获取#p0...集合 List<String> keyList = getKeyParsList(primalKey); for (String keyName : keyList) { int keyIndex = Integer.parseInt(keyName.toLowerCase().replace("#p", "")); Object parValue = args[keyIndex]; primalKey = primalKey.replace(keyName, String.valueOf(parValue)); } return primalKey.replace("+", "").replace("'", ""); } // 获取key中#p0中的参数名称 private static List<String> getKeyParsList(String key) { List<String> ListPar = new ArrayList<String>(); if (key.indexOf("#") >= 0) { int plusIndex = key.substring(key.indexOf("#")).indexOf("+"); int indexNext = 0; String parName = ""; int indexPre = key.indexOf("#"); if (plusIndex > 0) { indexNext = key.indexOf("#") + key.substring(key.indexOf("#")).indexOf("+"); parName = key.substring(indexPre, indexNext); } else { parName = key.substring(indexPre); } ListPar.add(parName.trim()); key = key.substring(indexNext + 1); if (key.indexOf("#") >= 0) { ListPar.addAll(getKeyParsList(key)); } } return ListPar; } }
CacheHandler:
package com.huajie.common.cache; /** * @author xiewenfeng 缓存处理接口 * 责任链模式 */ public abstract class CacheHandler { // 定义处理级别 protected final static int FirstCache_LEVEL_REQUEST = 1; protected final static int SecondCache_LEVEL_REQUEST = 2; // 能处理的级别 private int level = 0; // 责任传递,下一个责任人是谁 private CacheHandler nextHandler; // 每个类自己能处理那些请求 public CacheHandler(int level) { this.level = level; } // 处理请求 public final Object HandleMessage(RedisResult redisResult) throws Throwable { //如果women类型为当前处理的level if(redisResult.getCacheLevel()==this.level){ return this.response(redisResult); }else{ if(null!=this.nextHandler){ return this.nextHandler.HandleMessage(redisResult); }else{ //没有下级不处理 return null; } } } public void setNext(CacheHandler handler) { this.nextHandler = handler; } // 有请示的回应 protected abstract Object response(RedisResult redisResult) throws Throwable; }
FirstCacheHadler:一级缓存处理者
package com.huajie.common.cache; import org.aspectj.lang.ProceedingJoinPoint; import com.huajie.utils.RedisUtil; public class FirstCacheHadler extends CacheHandler{ public FirstCacheHadler() { super(CacheHandler.FirstCache_LEVEL_REQUEST); } @Override protected Object response(RedisResult redisResult) throws Throwable { String key = redisResult.getKey(); RedisUtil redisUtil = redisResult.getRedisUtil(); boolean hasKey = redisUtil.hasKey(key); ProceedingJoinPoint joinPoint = redisResult.getJoinPoint(); int expireTime = redisResult.getExpireTime(); if (hasKey) { return redisUtil.get(key); } else { Object res = joinPoint.proceed(); redisUtil.set(key, res); redisUtil.expire(key, expireTime); return res; } } }
SecondCacheHadler:二级缓存处理者
package com.huajie.common.cache; import org.aspectj.lang.ProceedingJoinPoint; import com.huajie.utils.RedisUtil; public class SecondCacheHadler extends CacheHandler { public SecondCacheHadler() { super(CacheHandler.SecondCache_LEVEL_REQUEST); } @Override protected Object response(RedisResult redisResult) throws Throwable { String nextKey = redisResult.getNextKey(); String key = redisResult.getKey(); RedisUtil redisUtil = redisResult.getRedisUtil(); ProceedingJoinPoint joinPoint = redisResult.getJoinPoint(); int expireTime = redisResult.getExpireTime(); boolean hasKey = redisUtil.hasKey(key); if (hasKey) { return redisUtil.get(key); } else { boolean hasNextKey = redisUtil.hasKey(nextKey); if (hasNextKey) { return redisUtil.get(nextKey); } else { Object res = joinPoint.proceed(); redisUtil.set(nextKey, res); redisUtil.expire(nextKey, expireTime); return res; } } } }
RedisResult:该业务场景对象,用于传递参数
package com.huajie.common.cache; import org.aspectj.lang.ProceedingJoinPoint; import com.huajie.utils.RedisUtil; import lombok.Data; @Data public class RedisResult implements IRedisResult { private int cacheLevel; private Object result; private RedisUtil redisUtil; private String key; private String nextKey; private int expireTime; private ProceedingJoinPoint joinPoint; @Override public int getCacheLevel() { return cacheLevel; } @Override public Object getResult() { return result; } public RedisResult(RedisUtil redisUtil, String key, String nextKey, ProceedingJoinPoint joinPoint, int cacheLevel,int expireTime) { this.redisUtil = redisUtil; this.key = key; this.joinPoint = joinPoint; this.cacheLevel = cacheLevel; this.nextKey = nextKey; this.expireTime = expireTime; } }
使用方法如下:
@Override @ExtCacheable(key = "middle+#p0+#p1+#p2", nextKey = "middle+#p0+#p2") public List<MiddleManage> listMiddleManageInfo(String leadergroupId, String uuid, String yearDetailId) { Map<String, Object> map = new HashMap<String, Object>(); map.put("leadergroupId", leadergroupId); map.put("uuid", uuid); map.put("yearDetailId", yearDetailId); List<MiddleManage> middleManageDetailList = middleManageMapper.listMiddleManageInfo(map); return middleManageDetailList; }
以上为个人经验,希望能给大家一个参考,也希望大家多多支持猪先飞。
原文出处:https://blog.csdn.net/xiewenfeng520/article/details/83819487
相关文章
- 这篇文章主要介绍了如何利用java语言实现经典《复杂迷宫》游戏,文中采用了swing技术进行了界面化处理,感兴趣的小伙伴可以动手试一试...2022-02-01
java 运行报错has been compiled by a more recent version of the Java Runtime
java 运行报错has been compiled by a more recent version of the Java Runtime (class file version 54.0)...2021-04-01- 这篇文章主要介绍了在java中获取List集合中最大的日期时间操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-15
- 这篇文章主要介绍了教你怎么用Java获取国家法定节假日,文中有非常详细的代码示例,对正在学习java的小伙伴们有非常好的帮助,需要的朋友可以参考下...2021-04-23
- 这篇文章主要介绍了Java如何发起http请求的实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2021-03-31
- 说起C#和Java这两门语言(语法,数据类型 等),个人以为,大概有90%以上的相似,甚至可以认为几乎一样。但是在工作中,我也发现了一些细微的差别...2020-06-25
- 神马是“解释器模式”?先翻开《GOF》看看Definition:给定一个语言,定义它的文法的一种表示,并定义一个解释器,这个解释器使用该表示来解释语言中的句子。在开篇之前还是要科普几个概念: 抽象语法树: 解释器模式并未解释如...2014-06-07
- 这篇文章主要介绍了解决Java处理HTTP请求超时的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-03-29
- 这篇文章主要为大家介绍了JavaScript设计模式中的装饰者模式,对JavaScript设计模式感兴趣的小伙伴们可以参考一下...2016-01-21
- 这篇文章主要介绍了java 判断两个时间段是否重叠的案例,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-15
- 这篇文章主要介绍了超简洁java实现双色球若干注随机号码生成(实例代码),本文通过实例代码给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-04-02
- 这篇文章主要介绍了Java生成随机姓名、性别和年龄的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧...2020-10-01
java 画pdf用itext调整表格宽度、自定义各个列宽的方法
这篇文章主要介绍了java 画pdf用itext调整表格宽度、自定义各个列宽的方法,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2021-01-31- 这篇文章主要介绍了java正则表达式判断前端参数修改表中另一个字段的值,需要的朋友可以参考下...2021-05-07
Java使用ScriptEngine动态执行代码(附Java几种动态执行代码比较)
这篇文章主要介绍了Java使用ScriptEngine动态执行代码,并且分享Java几种动态执行代码比较,需要的朋友可以参考下...2021-04-15- 这篇文章主要介绍了Java开发实现人机猜拳游戏,文中示例代码介绍的非常详细,具有一定的参考价值,感兴趣的小伙伴们可以参考一下...2020-08-03
Java 8 Stream 的终极技巧——Collectors 功能与操作方法详解
这篇文章主要介绍了Java 8 Stream Collectors 功能与操作方法,结合实例形式详细分析了Java 8 Stream Collectors 功能、操作方法及相关注意事项,需要的朋友可以参考下...2020-05-20- 这篇文章主要介绍了Java List集合返回值去掉中括号('[ ]')的操作,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧...2020-08-29
Java中lombok的@Builder注解的解析与简单使用详解
这篇文章主要介绍了Java中lombok的@Builder注解的解析与简单使用,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下...2021-01-06- 下面小编就为大家带来一篇java中String类型变量的赋值问题介绍。小编觉得挺不错的。现在分享给大家,给大家一个参考。...2016-03-28