1 /** 2 * @author NiceBin 3 * @description: 缓存更新接口,在Cache实现类的get方法上注解即可 4 * @date 2019/11/18 8:56 5 */ 6 @Target({ElementType.METHOD,ElementType.TYPE}) 7 @Retention(RetentionPolicy.RUNTIME) 8 @Inherited 9 public @interface UpdateCache { 10 }
* 本系统的缓存接口,SystemCacheMgr统一保存数据记录的时间和控制缓存自动刷新流程 * * 为了实现数据快过期前的自动刷新,需要以下操作: 5 * 1.实现此接口 * 如果用如RedisCacheManager这种写好的类,需要子类继承再实现此接口 * 如果Cache是CacheManager内部生成的,还需要重写createCache方法 * 使生成的Cache走一遍Spring初始化Bean的过程,交给Spring管理 * 这里主要为了Spring帮忙生成代理类,让注解生效 10 * 2.实现了 {@link Cache} 接口的类在get方法上加上注解 { UpdateCache} 才有更新效果,所以如果要用如RedisCache 11 * 这种写好的类,需要子类继承,并重写get方法 12 * 然后在get方法上加@UpdateCache 13 14 public interface I_SystemCacheMgr extends CacheManager{ 15 16 * 该数据是否过期 17 * true为已经过期 18 * @param cacheName 缓存名字 19 id 数据id 20 saveTime 该缓存内该数据的存储时间 21 @return 22 @throws Exception 23 24 boolean isApproachExpire(String cacheName,Object id,Timestamp saveTime) throws Exception; 25 26 27 * 删除指定Cache里的指定数据 28 cacheName 29 id 30 31 32 void remove(String cacheName,Object id) 33 34 35 * 清除所有缓存内容 36 37 38 void clearAll() 39 40 41 * 获得所有的Cache 42 43 44 ConcurrentMap<String,Cache> getAllCaches(); 45 }
* @description: 增强RedisCache * 为了能在get方法写上@Update注解,实现自动刷新 * @date 2019/7/4 13:24 6 class RedisCacheEnhance RedisCache { 8 9 * Create new { RedisCacheEnhance}. * name must not be {@literal null}. 13 cacheWriter must not be { cacheConfig must not be {15 16 protected RedisCacheEnhance(String name,RedisCacheWriter cacheWriter,RedisCacheConfiguration cacheConfig) { 17 super(name,cacheWriter,cacheConfig); } 19 @UpdateCache 21 public ValueWrapper get(Object key){ 22 System.out.println("进入get方法"); 23 return .get(key); 24 26 27 public <T> T get(Object key,@Nullable Class<T> type){ 28 .get(key,type); 30 31 valueLoader){ 33 34 }
1 2 3 * @description: RedisCacheManager增强类,为了实现本系统缓存自动更新功能 4 * @date 2019/11/25 9:07 5 6 class RedisCacheMgr extends RedisCacheManager implements I_SystemCacheMgr { 7 8 private final RedisCacheWriter cacheWriter; 9 private ConcurrentMap<String,Cache> caches = new ConcurrentHashMap<>(); 10 11 private DefaultListableBeanFactory defaultListableBeanFactory; 12 13 public RedisCacheMgr(RedisCacheWriter cacheWriter,RedisCacheConfiguration defaultCacheConfiguration,Map<String,RedisCacheConfiguration> initialCacheConfigurations,boolean allowInFlightCacheCreation) { 14 (cacheWriter,defaultCacheConfiguration,initialCacheConfigurations,allowInFlightCacheCreation); 15 this.cacheWriter = cacheWriter; 16 17 18 19 20 * 重写createRedisCache的方法,生成自己定义的Cache 21 * 这里主要要让Spring来生成代理Cache,不然在Cache上的注解是无效的 22 name 23 cacheConfig 24 25 26 @Override 27 RedisCacheEnhance createRedisCache(String name,@Nullable RedisCacheConfiguration cacheConfig) { 28 //利用Spring生成代理Cache 29 BeanDefinition beanDefinition = new RootBeanDefinition(RedisCacheEnhance.class 30 因为只有有参构造方法,所以要添加参数 31 ConstructorArgumentValues constructorArgumentValues = beanDefinition.getConstructorArgumentValues(); 32 constructorArgumentValues.addIndexedArgumentValue(0,name); 33 constructorArgumentValues.addIndexedArgumentValue(1 34 constructorArgumentValues.addIndexedArgumentValue(2 35 36 如果有属性需要设置,还能这样做,不过需要有对应属性名的set方法 37 definition.getPropertyValues().add("propertyName",beanDefinition.getBeanClassName()); 38 39 ApplicationContext applicationContext = SystemContext.getSystemContext() 40 .getApplicationContext(); 41 需要这样获取的DefaultListableBeanFactory类才能走一遍完整的Bean初始化流程!! 42 像applicationContext.getBean(DefaultListableBeanFactory.class)都不好使!! 43 DefaultListableBeanFactory defaultListableBeanFactory = (DefaultListableBeanFactory)applicationContext.getAutowireCapableBeanFactory(); 44 defaultListableBeanFactory.registerBeanDefinition(name,beanDefinition); 45 46 RedisCacheEnhance redisCacheEnhance = (RedisCacheEnhance)applicationContext.getBean(name); 47 caches.put(name,redisCacheEnhance); 48 return redisCacheEnhance; 49 50 51 52 * 过期规则为:缓存有效时间-(目前时间-记录时间)<= 随机时间 53 * 随机时间是防止同一时刻过期时间太多,造成缓存雪崩,在SystemStaticValue中缓存项里配置 54 * true为将要过期(可以刷新了) 55 56 cacheName 缓存名称 57 58 saveTime 储存时间 59 60 61 62 NoSuchAlgorithmException { 63 long ttl = -1; 64 65 RedisCacheConfiguration configuration = this.getCacheConfigurations().get(cacheName); 66 ttl = configuration.getTtl().getSeconds(); 67 68 if (ttl != -1 && saveTime!=null) { 69 int random = Tool.getSecureRandom(SystemStaticValue.CACHE_MIN_EXPIRE,SystemStaticValue.CACHE_MAX_EXPIRE); 70 Date date = new Date(); 71 long theNowTime = date.getTime() / 1000 72 long theSaveTime = saveTime.getTime() / 1000 73 if (ttl - (theNowTime - theSaveTime) <= random) { 74 true 75 } 76 } 77 false 78 79 80 81 Exception { 82 Cache cache = .getCache(cacheName); 83 cache.evict(id); 84 85 86 87 88 89 90 91 92 93 94 Collection<String> cacheNames = .getCacheNames(); 95 Iterator<String> iterator = cacheNames.iterator(); 96 while (iterator.hasNext()) { 97 String cacheName = iterator.next(); 98 Cache redisCache = 99 redisCache.clear(); 100 101 102 103 104 public ConcurrentMap<String,1)"> getAllCaches() { 105 caches; 106 107 }
1 RedisCacheManager redisCacheManager = RedisCacheManager.builder(redisConnectionFactory) 2 .cacheDefaults(defaultCacheConfig) 默认配置(强烈建议配置上)。 比如动态创建出来的都会走此默认配置 3 .withInitialCacheConfigurations(initialCacheConfiguration) 不同cache的个性化配置 4 .build();
* Create new instance of { RedisCacheManager} with configuration options applied. * * @return new instance of { RedisCacheManager}. 6 RedisCacheManager build() { 7 8 RedisCacheManager cm = RedisCacheManager(cacheWriter,initialCaches, allowInFlightCacheCreation); 10 cm.setTransactionAware(enableTransactions); 12 13 cm; 14 }
1 RedisCacheManager(RedisCacheWriter cacheWriter,1)">2 Map<String,1)">3 4 5 6 Assert.notNull(initialCacheConfigurations,"InitialCacheConfigurations must not be null!"7 8 .initialCacheConfiguration.putAll(initialCacheConfigurations); 9 }
RedisCacheManagerBuilder(RedisCacheWriter cacheWriter) { 2 3 }
1 static RedisCacheManagerBuilder fromConnectionFactory(RedisConnectionFactory connectionFactory) { 2 3 Assert.notNull(connectionFactory,"ConnectionFactory must not be null!"4 5 return builder( DefaultRedisCacheWriter(connectionFactory)); 6 }
RedisCacheWriter { 2 3 RedisCacheWriter} without locking behavior. connectionFactory must not be { DefaultRedisCacheWriter}. 8 RedisCacheWriter nonLockingRedisCacheWriter(RedisConnectionFactory connectionFactory) { 11 Assert.notNull(connectionFactory,1)">13 DefaultRedisCacheWriter(connectionFactory); 15 RedisCacheWriter} with locking behavior. 21 22 RedisCacheWriter lockingRedisCacheWriter(RedisConnectionFactory connectionFactory) { 23 24 Assert.notNull(connectionFactory,1)">26 new DefaultRedisCacheWriter(connectionFactory,Duration.ofMillis(50)); 27 }
1 ApplicationContext applicationContext =2 3 5 DefaultListableBeanFactory defaultListableBeanFactory =6 defaultListableBeanFactory.registerBeanDefinition(name,beanDefinition);
* @description: CacheManager初始化 * 目前系统只用一个Manager,使用RedisCacheManager * 根据SystemStaticValue中的SystemCache枚举内容进行Cache的注册 * 配置启动前需要DefaultListableBeanFactory.class先加载完成 * 不然CacheManager或者Cache想用的时候会报错 * @date 2019/11/13 17:02 9 @Configuration 11 @Import(DefaultListableBeanFactory.) CacheConfig { 13 @Autowired 15 RedisConnectionFactory redisConnectionFactory; 16 @Bean 18 RedisCacheMgr cacheManager() { 20 创建Json自定义序列化器 21 FastJsonRedisSerializer<Object> fastJsonRedisSerializer = new FastJsonRedisSerializer<>(Object.22 包装成SerializationPair类型 23 RedisSerializationContext.SerializationPair serializationPair = RedisSerializationContext.SerializationPair.fromSerializer(fastJsonRedisSerializer); 24 25 RedisCacheConfiguration defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig() 26 .entryTtl(Duration.ofDays(1)) 27 .computePrefixWith(cacheName -> "Cache"+cacheName); 针对不同cacheName,设置不同的过期时间,用了双括号初始化方法~ 29 Map<String,RedisCacheConfiguration> initialCacheConfiguration = new HashMap<String,RedisCacheConfiguration>() {{ 30 SystemStaticValue.SystemCache[] systemCaches = SystemStaticValue.SystemCache.values(); 31 Arrays.asList(systemCaches).forEach((systemCache)-> 32 put(systemCache.getCacheName(),RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofSeconds(systemCache.getSurviveTime())) 33 .serializeValuesWith(serializationPair))); 34 }}; 35 RedisCacheMgr redisCacheMgr = new RedisCacheMgr(RedisCacheWriter.lockingRedisCacheWriter(redisConnectionFactory),defaultCacheConfig,initialCacheConfiguration,1)">36 37 设置白名单---非常重要******** 38 /* 39 使用fastjson的时候:序列化时将class信息写入,反解析的时候, 40 fastjson默认情况下会开启autoType的检查,相当于一个白名单检查, 如果序列化信息中的类路径不在autoType中,autoType会默认开启 反解析就会报com.alibaba.fastjson.JSONException: autoType is not support的异常 43 44 ParserConfig.getGlobalInstance().addAccept("com.tophousekeeper"45 redisCacheMgr; 46 47 }
要实现对象的缓存,定义自己的序列化和反序列化器。使用阿里的fastjson来实现的方便多。 3 class FastJsonRedisSerializer<T> implements RedisSerializer<T> { 5 static final Charset DEFAULT_CHARSET = Charset.forName("UTF-8" 6 private Class<T> clazz; 8 public FastJsonRedisSerializer(Class<T> clazz) { 9 10 this.clazz =14 byte[] serialize(T t) SerializationException { 15 if (null == t) { 16 new byte[0]; 18 JSON.toJSONString(t,SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET); 20 public T deserialize(byte[] bytes) null == bytes || bytes.length <= 024 25 26 String str = String(bytes,DEFAULT_CHARSET); 27 (T) JSON.parSEObject(str,clazz); 29 }
SystemStaticValue { ..... 以下为缓存信息的配置(CACHE开头)-------------------------------------------------------- 4 系统缓存名称及过期时间(秒) enum SystemCache{ 6 每日缓存,有效时间24小时 7 DAY("dailyCache",60),1)"> 8 半日缓存,有效时间12小时 9 HALF_DAY("halfDayCache",12*60*601小时缓存 11 ONE_HOUR("oneHour",1*60*6012 半小时缓存 13 HALF_HOUR("halfHour",30*6014 String cacheName; long surviveTime; 17 SystemCache(String cacheName,1)"> surviveTime){ 18 this.cacheName = cacheName; 19 this.surviveTime =21 String getCacheName() { 23 void setCacheName(String cacheName) { 27 29 30 getSurviveTime() { 31 34 void setSurviveTime( surviveTime) { 35 37 38 }
* @description: 记录被 { Cacheable} 注解过的方法信息,为了主动更新缓存去调用对应方法 * @date 2019/11/26 16:28 CacheInvocation { 7 Object key; Object targetBean; Method targetMethod; 10 Object[] arguments; 11 12 CacheInvocation(Object key,Object targetBean,Method targetMethod,Object[] arguments) { this.key = key; this.targetBean = targetBean; this.targetMethod = targetMethod; 16 反射时不用检查修饰符,略微提高性能 this.targetMethod.setAccessible(if (arguments != null && arguments.length != 0this.arguments = Arrays.copyOf(arguments,arguments.length); 22 23 Object[] getArguments() { 24 arguments; 26 Object getTargetBean() { 31 Method getTargetMethod() { 32 34 35 Object getKey() { 36 38 }
* @description: 刷新缓存某个数据的任务 * @date 2019/11/29 15:29 class UpdateDataTask Callable { 将要执行的方法信息 CacheInvocation cacheInvocation; 对应要操作的缓存 Cache cache; 11 对应要更新的数据id Object id; * 初始化任务 cacheInvocation cache 19 20 UpdateDataTask(CacheInvocation cacheInvocation,Cache cache,Object id){ 21 this.cacheInvocation = cacheInvocation; this.cache = cache; this.id = id; public Object call() if(cacheInvocation == ){ 29 throw new SystemException(SystemStaticValue.CACHE_EXCEPTION_CODE,"更新数据线程方法信息不能为null" cache.put(id,methodInvoke()); * 代理方法的调用 38 39 private Object methodInvoke() Exception{ 40 MethodInvoker methodInvoker = MethodInvoker(); methodInvoker.setArguments(cacheInvocation.getArguments()); methodInvoker.setTargetMethod(cacheInvocation.getTargetMethod().getName()); 43 methodInvoker.setTargetObject(cacheInvocation.getTargetBean()); 44 methodInvoker.prepare(); methodInvoker.invoke(); 47 }
* @description: 本系统的缓存管理器 * 数据自动刷新功能,要配合 { UpdateCache}才能实现 5 * 目前没办法实现缓存纯自动更新,必须要使用到该缓存拿数据进行触发 7 * 纯自动更新没有意义,假设一个数据放了半小时没人访问要过期了,那就过期吧 8 * 因为缓存前提是一段时间频繁访问的数据,如果都没人访问了,就不能称之为缓存 9 * 不然就是一个系统长期存在的动态变量,不适用于缓存 10 * @date 2019/11/14 16:18 11 12 @Component 13 SystemCacheMgr { 14 目前系统只考虑一个CacheManager 15 必须有一个I_SystemCache的实现类,多个实现类用@Primary注解,类似于Spring的缓存管理器 16 17 I_SystemCacheMgr defaultCacheMgr; 18 系统的线程池类 19 20 SystemThreadPool systemThreadPool; 21 所有缓存的所有数据记录Map 22 外部Map中,key为缓存名称,value为该缓存内的数据储存信息Map 23 内部Map中,key为数据的id,value为记录该数据的储存信息 24 private ConcurrentHashMap<String,ConcurrentHashMap<Object,DataInfo>> dataInfoMaps = 25 26 27 * 储存信息内部类,用于记录 28 * 获取要调用获取方法,因为加锁了线程才安全 29 30 DataInfo { 31 记录该数据的时间 32 Timestamp saveTime; 33 获得此数据的方法信息 34 35 保证只有一个线程提前更新此数据 ReentrantLock lock; 37 38 synchronized setSaveTime(Timestamp saveTime) { 39 this.saveTime = saveTime; 41 42 43 setCacheInvocation(CacheInvocation cacheInvocation) { 44 45 46 47 setLock(ReentrantLock lock) { 48 this.lock = lock; 50 51 52 * 获得DataInfo类,如果为空则创建一个 57 58 DataInfo getDataInfo(String cacheName,Object id) { 59 ConcurrentHashMap<Object,DataInfo> dateInfoMap = dataInfoMaps.get((cacheName)); 60 DataInfo dataInfo; 61 if (dateInfoMap == 62 简单的锁住了,因为创建这个对象挺快的 63 synchronized ( 64 重新获取一次进行判断,因为dateInfoMap是局部变量,不能保证同步 65 dateInfoMap = 66 67 dateInfoMap = 68 dataInfo = DataInfo(); 69 dataInfo.setLock(new ReentrantLock( 70 dateInfoMap.put(id,dataInfo); 71 dataInfoMaps.put(cacheName,dateInfoMap); 72 73 } 74 75 这里不能用else,因为多线程同时进入if,后面进的dataInfo会是null 76 dataInfo = dateInfoMap.get(id); 77 78 dataInfo; 79 80 82 * 为该数据放入缓存的时间记录 85 86 recordDataSaveTime(String cacheName,1)"> 87 Date date = 88 Timestamp nowtime = Timestamp(date.getTime()); 89 DataInfo dataInfo = getDataInfo(cacheName,id); dataInfo.setSaveTime(nowtime); 91 92 94 * 记录获得此数据的方法信息,为了主动更新缓存时的调用 95 96 cacheName 缓存名称 97 id 数据id 98 targetBean 目标类 targetMethod 目标方法 arguments 目标方法的参数 101 102 recordCacheInvocation(String cacheName,String id,1)">103 DataInfo dataInfo =104 CacheInvocation cacheInvocation = CacheInvocation(id,targetBean,targetMethod,arguments); 锁在这方法里面有 dataInfo.setCacheInvocation(cacheInvocation); 107 108 109 110 * 数据自动刷新功能,要配合 {111 * 原理:先判断数据是否过期,如果数据过期则从缓存删除。 112 113 114 id 数据id 115 116 117 void autoUpdate(String cacheName,1)">118 DataInfo dataInfo =119 Cache cache = defaultCacheMgr.getCache(cacheName); 120 121 122 如果没有保存的时间,说明该数据还从未载入过 123 if (dataInfo.saveTime == 124 125 126 if (defaultCacheMgr.isApproachExpire(cacheName,id,dataInfo.saveTime)) { 127 (dataInfo.lock.tryLock()) { //这里用非阻塞的tryLock,有一个用户进来触发缓存过期判断即可,其他用户继续获得当时的缓存值 128 获取锁后再次判断数据是否过期 129 130 ThreadPoolExecutor threadPoolExecutor = systemThreadPool.getThreadPoolExecutor(); 131 UpdateDataTask updateDataTask = UpdateDataTask(dataInfo.cacheInvocation,cache,1)">132 FutureTask futureTask = FutureTask(updateDataTask); 133 134 try135 threadPoolExecutor.submit(futureTask); 136 futureTask.get(1137 如果上一步执行完成没报错,那么重新记录保存时间 138 recordDataSaveTime(cacheName,1)">139 } catch (TimeoutException ex) { 140 如果访问数据库超时 141 142 } (RejectedExecutionException ex) { 143 如果被线程池拒绝了 144 145 } finally146 dataInfo.lock.unlock(); 147 } 148 149 150 151 152 153 154 155 156 157 defaultCacheMgr.clearAll(); 158 159 160 以下为Set和Get 161 I_SystemCacheMgr getDefaultCacheMgr() { 162 defaultCacheMgr; 163 164 165 setDefaultCacheMgr(I_SystemCacheMgr defaultCacheMgr) { 166 this.defaultCacheMgr =167 168 169 public ConcurrentHashMap<String,DataInfo>> getDataInfoMaps() { 170 dataInfoMaps; 171 172 173 void setDataInfoMaps(ConcurrentHashMap<String,1)"> dataInfoMaps) { 174 this.dataInfoMaps =175 176 }
* @description: 处理缓存注解的地方:包括@UpdateCache,@Cacheable * @date 2019/11/18 14:57 @Aspect CacheAspect { SystemCacheMgr systemCacheMgr; 13 * 数据注册到SystemCacheMgr * 为数据自动更新做准备 16 17 @Before("@annotation(org.springframework.cache.annotation.Cacheable)" registerCache(JoinPoint joinPoint){ 19 System.out.println("拦截了@Cacheable"获取到该方法前的@Cacheable注解,来获取CacheName和key的信息 21 Method method = Tool.getSpecificMethod(joinPoint); 22 Cacheable cacleable = method.getAnnotation(Cacheable.23 String[] cacheNames = cacleable.value()!=null?cacleable.value():cacleable.cacheNames(); 24 String theKey = cacleable.key(); 25 取出来的字符串是'key',需要去掉'' 26 String key = theKey.substring(1,theKey.length()-127 Arrays.stream(cacheNames).forEach(cacheName ->{ 28 记录数据保存时间 systemCacheMgr.recordDataSaveTime(cacheName,key); 30 记录数据对应的方法信息 systemCacheMgr.recordCacheInvocation(cacheName,key,joinPoint.getTarget(),method,joinPoint.getArgs()); }); * 检测该键是否快过期了 * 如果快过期则进行自动更新 38 joinPoint 39 40 @Before(value = "@annotation(com.tophousekeeper.system.annotation.UpdateCache)&&args(id)"41 void checkExpire(JoinPoint joinPoint,String id) 42 System.out.println("拦截了@UpdateCache"43 RedisCacheEnhance redisCacheEnhance = (RedisCacheEnhance) joinPoint.getTarget(); systemCacheMgr.autoUpdate(redisCacheEnhance.getName(),1)">45 46 }
Tool { * 获得代理类方法中真实的方法 * 小知识: * ClassUtils.getMostSpecificMethod(Method method,Class<?> targetClass) * 该方法是一个有趣的方法,他能从代理对象上的一个方法,找到真实对象上对应的方法。 * 举个例子,MyComponent代理之后的对象上的someLogic方法,肯定是属于cglib代理之后的类上的method, * 使用这个method是没法去执行目标MyComponent的someLogic方法, * 这种情况下,就可以使用getMostSpecificMethod, * 找到真实对象上的someLogic方法,并执行真实方法 * BridgeMethodResolver.findBridgedMethod(Method bridgeMethod) * 如果当前方法是一个泛型方法,则会找Class文件中实际实现的方法 poxyMethod 代理的方法 targetclass 真实的目标类 18 19 Method getSpecificMethod(Method poxyMethod,Class targetclass){ 20 Method specificMethod = ClassUtils.getMostSpecificMethod(poxyMethod,targetclass); 21 specificMethod = BridgeMethodResolver.findBridgedMethod(specificMethod); specificMethod; 23 25 * AopProxyUtils.ultimateTargetClass() * 获取一个代理对象的最终对象类型 joinPoint 切面的切点类 32 33 Method getSpecificMethod(JoinPoint joinPoint){ 34 MethodSignature methodSignature = (MethodSignature) joinPoint.getSignature(); 35 Method poxyMethod = methodSignature.getMethod(); 36 Class targetClass = AopProxyUtils.ultimateTargetClass(joinPoint.getTarget()); Tool.getSpecificMethod(poxyMethod,targetClass); 39 }
本站采用系统自动发货方式,付款后即出现下载入口,如有疑问请咨询在线客服!
售后时间:早10点 - 晚11:30点Copyright © 2024 jiecseo.com All rights reserved. 粤ICP备18085929号
欢迎光临【捷杰建站】,本站所有资源仅供学习与参考,禁止用于商业用途或从事违法行为!
技术营运:深圳市晟艺互动传媒有限公司