spring-data-redis时效设置
本人转自http://hbxflihua.iteye.com/blog/2320584#bc2396403
spring目前在@Cacheable和@CacheEvict等注解上不支持缓存时效设置,只允许通过配置文件设置全局时效。这样就很不方便设定时间。比如系统参数和业务数据的时效是不一样的,这给程序开发造成很大的困扰。不得已,我重写了spring的这两个注解。以下是具体实现。
首先定义@Cacheable和@CacheEvict注解类。
- package com.lh.common.annotation;
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
- import com.rd.ifaes.common.dict.ExpireTime;
- @Retention(RetentionPolicy.RUNTIME)
- @Target({ElementType.METHOD})
- public @interface Cacheable {
- public String key() default ""; // 缓存key
- public ExpireTime expire() default ExpireTime.NONE; // 缓存时效,默认无限期
- }
- package com.lh.common.annotation;
- import java.lang.annotation.Documented;
- import java.lang.annotation.ElementType;
- import java.lang.annotation.Inherited;
- import java.lang.annotation.Retention;
- import java.lang.annotation.RetentionPolicy;
- import java.lang.annotation.Target;
- /**
- * 缓存清除
- * @author lh
- * @version 3.0
- * @since 2016-8-28
- *
- */
- @Target({ ElementType.METHOD })
- @Retention(RetentionPolicy.RUNTIME)
- @Inherited
- @Documented
- public @interface CacheEvict {
- String key() default "";// 缓存key
- }
具体的切面代码(CacheAspect.java)如下:
- package com.lh.common.annotation;
- import java.util.List;
- import java.util.Set;
- import java.util.concurrent.TimeUnit;
- import org.aspectj.lang.ProceedingJoinPoint;
- import org.aspectj.lang.annotation.Around;
- import org.aspectj.lang.annotation.Aspect;
- import org.springframework.beans.factory.annotation.Autowired;
- import org.springframework.data.redis.core.RedisTemplate;
- import org.springframework.data.redis.core.ValueOperations;
- import org.springframework.stereotype.Component;
- import org.springframework.util.CollectionUtils;
- import com.rd.ifaes.common.util.ReflectionUtils;
- import com.rd.ifaes.common.util.StringUtils;
- import com.rd.ifaes.core.core.util.CacheUtils;
- @Aspect
- @Component
- public class CacheAspect {
- @SuppressWarnings("rawtypes")
- @Autowired
- private RedisTemplate redisTemplate;
- @Around("@annotation(cache)")
- public Object cacheable(final ProceedingJoinPoint pjp, Cacheable cache) throws Throwable {
- String key = getCacheKey(pjp, cache.key());
- // //方案一:使用自定义缓存工具类操作缓存
- // Object value = CacheUtils.getObj(key);// 从缓存获取数据
- // if (value != null) {
- // return value; // 如果有数据,则直接返回
- // }
- // value = pjp.proceed(); // 缓存,到后端查询数据
- // if (value != null) {
- // CacheUtils.set(key, value, cache.expire());
- // }
- // 方案二:使用redisTemplate操作缓存
- @SuppressWarnings("unchecked")
- ValueOperations<String, Object> valueOper = redisTemplate.opsForValue();
- Object value = valueOper.get(key); // 从缓存获取数据
- if (value != null) {
- return value; // 如果有数据,则直接返回
- }
- value = pjp.proceed(); // 缓存,到后端查询数据
- CacheUtils.set(key, value, cache.expire());
- if (cache.expire().getTime() <= 0) { // 如果没有设置过期时间,则无限期缓存
- valueOper.set(key, value);
- } else { // 否则设置缓存时间
- valueOper.set(key, value, cache.expire().getTime(), TimeUnit.SECONDS);
- }
- return value;
- }
- @SuppressWarnings("unchecked")
- @Around("@annotation(evict)")
- public Object cacheEvict(final ProceedingJoinPoint pjp, CacheEvict evict) throws Throwable {
- Object value = pjp.proceed(); // 执行方法
- String key = getCacheKey(pjp, evict.key());
- // //方案一:使用自定义缓存工具类操作缓存
- // CacheUtils.del(key);
- // 方案二:使用redisTemplate操作缓存
- if (evict.key().equals(key)) {// 支持批量删除
- Set<String> keys = redisTemplate.keys(key.concat("*"));
- redisTemplate.delete(keys);
- }else{
- redisTemplate.delete(key);
- }
- return value;
- }
- /**
- * 获取缓存的key值
- *
- * @param pjp
- * @param key
- * @return
- */
- private String getCacheKey(ProceedingJoinPoint pjp, String key) {
- StringBuilder buf = new StringBuilder();
- Object[] args = pjp.getArgs();
- if(StringUtils.isNotBlank(key)){
- buf.append(key);
- List<String> annoParamNames = AopUtils.getAnnoParams(key);
- String[] methodParamNames = AopUtils.getMethodParamNames(AopUtils.getMethod(pjp));
- if(!CollectionUtils.isEmpty(annoParamNames)){
- for (String ap : annoParamNames) {
- String paramValue = "";
- for (int i = 0; i < methodParamNames.length; i++) {
- if(ap.startsWith(methodParamNames[i])){
- Object arg = args[i];
- if (ap.contains(".")) {
- paramValue = String.valueOf(ReflectionUtils.invokeGetter(arg, ap.substring(ap.indexOf(".") + 1)));
- } else {
- paramValue = String.valueOf(arg);
- }
- }
- }
- int start = buf.indexOf("{" + ap);
- int end = start + ap.length() + 2;
- buf = buf.replace(start, end, paramValue);
- }
- }
- }else{
- buf.append(pjp.getSignature().getDeclaringTypeName()).append(":").append(pjp.getSignature().getName());
- for (Object arg : args) {
- buf.append(":").append(arg.toString());
- }
- }
- return buf.toString();
- }
- }
里面使用到AopUtils.java和ExpireTime.java两个类,具体代码如下:
- package com.lh.common.annotation;
- import org.aspectj.lang.ProceedingJoinPoint;
- import org.aspectj.lang.Signature;
- import org.aspectj.lang.reflect.MethodSignature;
- import org.springframework.asm.*;
- import java.io.IOException;
- import java.io.InputStream;
- import java.lang.reflect.Method;
- import java.lang.reflect.Modifier;
- import java.util.ArrayList;
- import java.util.List;
- import java.util.regex.Matcher;
- import java.util.regex.Pattern;
- /**
- * 切面编程工具类
- * @author lh
- * @version 3.0
- * @since 2016-8-26
- */
- public class AopUtils {
- /**
- * <p>获取方法的参数名</p>
- *
- * @param m
- * @return
- */
- public static String[] getMethodParamNames(final Method m) {
- final String[] paramNames = new String[m.getParameterTypes().length];
- final String n = m.getDeclaringClass().getName();
- final ClassWriter cw = new ClassWriter(ClassWriter.COMPUTE_MAXS);
- String className = m.getDeclaringClass().getSimpleName();
- ClassReader cr = null;
- InputStream resourceAsStream = null;
- try {
- // cr = new ClassReader(n);
- // String filePathName = Class.forName(n).getResource("EDayHqbProcessManagerImpl.class").getPath();
- resourceAsStream = Class.forName(n).getResourceAsStream(className + ".class");
- cr = new ClassReader(resourceAsStream);
- // cr = new ClassReader(ClassLoader.getSystemResourceAsStream(n + ".class"));
- } catch (IOException e) {
- //e.printStackTrace();
- // Exceptions.uncheck(e);
- } catch (ClassNotFoundException e) {
- //e.printStackTrace();
- } finally {
- if (resourceAsStream != null) {
- try {
- resourceAsStream.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- assert cr != null;
- cr.accept(new ClassVisitor(Opcodes.ASM4, cw) {
- @Override
- public MethodVisitor visitMethod(final int access,
- final String name, final String desc,
- final String signature, final String[] exceptions) {
- final Type[] args = Type.getArgumentTypes(desc);
- // 方法名相同并且参数个数相同
- if (!name.equals(m.getName())
- || !sameType(args, m.getParameterTypes())) {
- return super.visitMethod(access, name, desc, signature,
- exceptions);
- }
- MethodVisitor v = cv.visitMethod(access, name, desc, signature,
- exceptions);
- return new MethodVisitor(Opcodes.ASM4, v) {
- @Override
- public void visitLocalVariable(String name, String desc,
- String signature, Label start, Label end, int index) {
- int i = index - 1;
- // 如果是静态方法,则第一就是参数
- // 如果不是静态方法,则第一个是"this",然后才是方法的参数
- if (Modifier.isStatic(m.getModifiers())) {
- i = index;
- }
- if (i >= 0 && i < paramNames.length) {
- paramNames[i] = name;
- }
- super.visitLocalVariable(name, desc, signature, start,
- end, index);
- }
- };
- }
- }, 0);
- return paramNames;
- }
- /**
- * <p>比较参数类型是否一致</p>
- *
- * @param types asm的类型({@link Type})
- * @param clazzes java 类型({@link Class})
- * @return
- */
- private static boolean sameType(Type[] types, Class<?>[] clazzes) {
- // 个数不同
- if (types.length != clazzes.length) {
- return false;
- }
- for (int i = 0; i < types.length; i++) {
- if (!Type.getType(clazzes[i]).equals(types[i])) {
- return false;
- }
- }
- return true;
- }
- /**
- * 取得切面调用的方法
- * @param pjp
- * @return
- */
- public static Method getMethod(ProceedingJoinPoint pjp){
- Signature sig = pjp.getSignature();
- MethodSignature msig = null;
- if (!(sig instanceof MethodSignature)) {
- throw new IllegalArgumentException("该注解只能用于方法");
- }
- msig = (MethodSignature) sig;
- Object target = pjp.getTarget();
- Method currentMethod = null;
- try {
- currentMethod = target.getClass().getMethod(msig.getName(), msig.getParameterTypes());
- } catch (NoSuchMethodException e) {
- } catch (SecurityException e) {
- }
- return currentMethod;
- }
- public static List<String> getMatcher(String regex, String source) {
- List<String> list = new ArrayList<String>();
- Pattern pattern = Pattern.compile(regex);
- Matcher matcher = pattern.matcher(source);
- while (matcher.find()) {
- list.add(matcher.group());
- }
- return list;
- }
- /**
- * 取得注解参数
- (?=exp) 匹配exp前面的位置
- (?<=exp) 匹配exp后面的位置
- (?!exp) 匹配后面跟的不是exp的位置
- (?<!exp) 匹配前面不是exp的位置
- * @param managers
- * @return
- */
- public static List<String> getAnnoParams(String source){
- String regex = "(?<=\\{)(.+?)(?=\\})";
- return getMatcher(regex, source);
- }
- }
- package com.lh.common.dict;
- /**
- * 失效时间枚举类
- * @author lh
- * @version 3.0
- * @since 2016-8-25
- *
- */
- public enum ExpireTime {
- /**
- * 无固定期限
- */
- NONE(0, "无固定期限")
- /**
- * 1秒钟
- */
- ,ONE_SEC(1, "1秒钟")
- /**
- * 5秒钟
- */
- ,FIVE_SEC(5, "5秒钟")
- /**
- * 10秒钟
- */
- ,TEN_SEC(10, "10秒钟")
- /**
- * 30秒钟
- */
- ,HALF_A_MIN(30, "30秒钟")
- /**
- * 1分钟
- */
- ,ONE_MIN(60, "1分钟")
- /**
- * 5分钟
- */
- ,FIVE_MIN(5 * 60, "5分钟")
- /**
- * 10分钟
- */
- ,TEN_MIN(10 * 60, "10分钟")
- /**
- * 20分钟
- */
- ,TWENTY_MIN(20 * 60, "20分钟")
- /**
- * 30分钟
- */
- ,HALF_AN_HOUR(30 * 60, "30分钟")
- /**
- * 1小时
- */
- ,ONE_HOUR(60 * 60, "1小时")
- /**
- * 1天
- */
- ,ONE_DAY(24 * 60 * 60, "1天")
- /**
- * 1个月
- */
- ,ONE_MON(30 * 24 * 60 * 60, "1个月")
- /**
- * 1年
- */
- ,ONE_YEAR(365 * 24 * 60 * 60, "1年")
- ;
- /**
- * 时间
- */
- private final int time;
- /**
- * 描述
- */
- private final String desc;
- ExpireTime(int time, String desc) {
- this.time = time;
- this.desc = desc;
- }
- /**
- * 获取具体时间
- * @return
- */
- public int getTime() {
- return time;
- }
- /**
- * 获取时间描述信息
- * @return
- */
- public String getDesc() {
- return desc;
- }
- /**
- * 根据时间匹配失效期限
- * @param time
- * @return
- */
- public static ExpireTime match(int time){
- if(NONE.getTime() == time){
- return NONE;
- }else if(ONE_SEC.getTime() == time){
- return ONE_SEC;
- }else if(FIVE_SEC.getTime() == time){
- return FIVE_SEC;
- }else if(TEN_SEC.getTime() == time){
- return TEN_SEC;
- }else if(HALF_A_MIN.getTime() == time){
- return HALF_A_MIN;
- }else if(ONE_MIN.getTime() == time){
- return ONE_MIN;
- }else if(FIVE_MIN.getTime() == time){
- return FIVE_MIN;
- }else if(TEN_MIN.getTime() == time){
- return TEN_MIN;
- }else if(TWENTY_MIN.getTime() == time){
- return TWENTY_MIN;
- }else if(HALF_AN_HOUR.getTime() == time){
- return HALF_AN_HOUR;
- }else if(ONE_HOUR.getTime() == time){
- return ONE_HOUR;
- }else if(ONE_DAY.getTime() == time){
- return ONE_DAY;
- }else if(ONE_MON.getTime() == time){
- return ONE_MON;
- }else if(ONE_YEAR.getTime() == time){
- return ONE_YEAR;
- }
- return HALF_AN_HOUR;
- }
- }
配置中的RdRedisCache.java 代码如下:
- package com.lh.common.jedis;
- import java.io.ByteArrayInputStream;
- import java.io.ByteArrayOutputStream;
- import java.io.IOException;
- import java.io.ObjectInputStream;
- import java.io.ObjectOutputStream;
- import net.sf.ehcache.Element;
- import org.springframework.cache.Cache;
- import org.springframework.cache.support.SimpleValueWrapper;
- import org.springframework.dao.DataAccessException;
- import org.springframework.data.redis.connection.RedisConnection;
- import org.springframework.data.redis.core.RedisCallback;
- import org.springframework.data.redis.core.RedisTemplate;
- public class RdRedisCache implements Cache {
- private RedisTemplate<String, Object> redisTemplate;
- private String name;
- public RedisTemplate<String, Object> getRedisTemplate() {
- return redisTemplate;
- }
- public void setRedisTemplate(RedisTemplate<String, Object> redisTemplate) {
- this.redisTemplate = redisTemplate;
- }
- public void setName(String name) {
- this.name = name;
- }
- @Override
- public String getName() {
- return this.name;
- }
- @Override
- public Object getNativeCache() {
- return this.redisTemplate;
- }
- @Override
- public ValueWrapper get(Object key) {
- final String keyf = obj2Str(key);
- Object object = null;
- object = redisTemplate.execute(new RedisCallback<Object>() {
- public Object doInRedis(RedisConnection connection)
- throws DataAccessException {
- byte[] key = keyf.getBytes();
- byte[] value = connection.get(key);
- if (value == null) {
- return null;
- }
- return toObject(value);
- }
- });
- return (object != null ? new SimpleValueWrapper(object) : null);
- }
- @Override
- public void put(Object key, Object value) {
- final String keyf = obj2Str(key);
- final Object valuef = value;
- final long liveTime = 86400;
- redisTemplate.execute(new RedisCallback<Long>() {
- public Long doInRedis(RedisConnection connection)
- throws DataAccessException {
- byte[] keyb = keyf.getBytes();
- byte[] valueb = toByteArray(valuef);
- connection.set(keyb, valueb);
- if (liveTime > 0) {
- connection.expire(keyb, liveTime);
- }
- return 1L;
- }
- });
- }
- public String obj2Str(Object key){
- String keyStr = null;
- if(key instanceof Integer){
- keyStr = ((Integer)key).toString();
- }else if(key instanceof Long){
- keyStr = ((Long)key).toString();
- }else {
- keyStr = (String)key;
- }
- return keyStr;
- }
- /**
- * 描述 : <Object转byte[]>. <br>
- * <p>
- * <使用方法说明>
- * </p>
- *
- * @param obj
- * @return
- */
- private byte[] toByteArray(Object obj) {
- byte[] bytes = null;
- ByteArrayOutputStream bos = new ByteArrayOutputStream();
- try {
- ObjectOutputStream oos = new ObjectOutputStream(bos);
- oos.writeObject(obj);
- oos.flush();
- bytes = bos.toByteArray();
- oos.close();
- bos.close();
- } catch (IOException ex) {
- ex.printStackTrace();
- }
- return bytes;
- }
- /**
- * 描述 : <byte[]转Object>. <br>
- * <p>
- * <使用方法说明>
- * </p>
- *
- * @param bytes
- * @return
- */
- private Object toObject(byte[] bytes) {
- Object obj = null;
- try {
- ByteArrayInputStream bis = new ByteArrayInputStream(bytes);
- ObjectInputStream ois = new ObjectInputStream(bis);
- obj = ois.readObject();
- ois.close();
- bis.close();
- } catch (IOException ex) {
- ex.printStackTrace();
- } catch (ClassNotFoundException ex) {
- ex.printStackTrace();
- }
- return obj;
- }
- @Override
- public void evict(Object key) {
- final String keyf = obj2Str(key);
- redisTemplate.execute(new RedisCallback<Long>() {
- public Long doInRedis(RedisConnection connection)
- throws DataAccessException {
- return connection.del(keyf.getBytes());
- }
- });
- }
- @Override
- public void clear() {
- redisTemplate.execute(new RedisCallback<String>() {
- public String doInRedis(RedisConnection connection)
- throws DataAccessException {
- connection.flushDb();
- return "ok";
- }
- });
- }
- @Override
- public <T> T get(Object key, Class<T> type) {
- ValueWrapper wrapper = get(key);
- return wrapper == null ? null : (T) wrapper.get();
- }
- @Override
- public ValueWrapper putIfAbsent(Object key, Object value) {
- synchronized (key) {
- ValueWrapper wrapper = get(key);
- if (wrapper != null) {
- return wrapper;
- }
- put(key, value);
- return toWrapper(new Element(key, value));
- }
- }
- private ValueWrapper toWrapper(Element element) {
- return (element != null ? new SimpleValueWrapper(element.getObjectValue()) : null);
- }
- }
spring配置文件的相关配置如下:
- <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
- <property name="maxIdle" value="${redis.pool.maxIdle}" /> <!-- 最大能够保持idel状态的对象数 -->
- <property name="maxTotal" value="${redis.pool.maxTotal}" /> <!-- 最大分配的对象数 -->
- <property name="testOnBorrow" value="${redis.pool.testOnBorrow}" /> <!-- 当调用borrow Object方法时,是否进行有效性检查 -->
- </bean>
- <!-- jedisPool init -->
- <bean id="jedisPool" class="redis.clients.jedis.JedisPool">
- <constructor-arg index="0" ref="jedisPoolConfig" />
- <constructor-arg index="1" value="${redis.host}" type="String" />
- <constructor-arg index="2" value="${redis.port}" type="int" />
- </bean>
- <!-- jedis单机配置 -->
- <bean id="jedisConnFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory" >
- <property name="hostName" value="${redis.host}" />
- <property name="port" value="${redis.port1}" />
- <property name="timeout" value="${redis.timeout}" />
- <property name="poolConfig" ref="jedisPoolConfig" />
- </bean>
- <bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer" />
- <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
- p:connectionFactory-ref="jedisConnFactory" p:keySerializer-ref="stringRedisSerializer" />
- <!-- spring自己的缓存管理器 -->
- <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
- <property name="caches">
- <set>
- <bean class="com.lh.common.jedis.RdRedisCache" p:redis-template-ref="redisTemplate" p:name="sysCache"/>
- </set>
- </property>
- </bean>
- <!-- 启用缓存注解功能,这个是必须的,否则注解不会生效,另外,该注解一定要声明在spring主配置文件中才会生效 -->
- <cache:annotation-driven cache-manager="cacheManager" proxy-target-class="true" />
spring-data-redis时效设置的更多相关文章
- spring data redis RedisTemplate操作redis相关用法
http://blog.mkfree.com/posts/515835d1975a30cc561dc35d spring-data-redis API:http://docs.spring.io/sp ...
- Spring Data Redis—Pub/Sub(附Web项目源码)
一.发布和订阅机制 当一个客户端通过 PUBLISH 命令向订阅者发送信息的时候,我们称这个客户端为发布者(publisher). 而当一个客户端使用 SUBSCRIBE 或者 PSUBSCRIBE ...
- Spring Data Redis—Pub/Sub(附Web项目源码) (转)
一.发布和订阅机制 当一个客户端通过 PUBLISH 命令向订阅者发送信息的时候,我们称这个客户端为发布者(publisher). 而当一个客户端使用 SUBSCRIBE 或者 PSUBSCRIBE ...
- Spring Data Redis 详解及实战一文搞定
SDR - Spring Data Redis的简称. Spring Data Redis提供了从Spring应用程序轻松配置和访问Redis的功能.它提供了与商店互动的低级别和高级别抽象,使用户免受 ...
- Spring Data Redis 让 NoSQL 快如闪电(2)
[编者按]本文作者为 Xinyu Liu,文章的第一部分重点概述了 Redis 方方面面的特性.在第二部分,将介绍详细的用例.文章系国内 ITOM 管理平台 OneAPM 编译呈现. 把 Redis ...
- Spring Data Redis 让 NoSQL 快如闪电 (1)
[编者按]本文作者为 Xinyu Liu,详细介绍了 Redis 的特性,并辅之以丰富的用例.在本文的第一部分,将重点概述 Redis 的方方面面.文章系国内 ITOM 管理平台 OneAPM 编译呈 ...
- spring data redis使用1——连接的创建
spring data redis集成了几个Redis客户端框架,Jedis , JRedis (Deprecated since 1.7), SRP (Deprecated since 1.7) a ...
- Spring Data Redis学习
本文是从为知笔记上复制过来的,懒得调整格式了,为知笔记版本是带格式的,内容也比这里全.点这里 为知笔记版本 Spring Data Redis 学习 Version 1.8.4.Release 前言 ...
- Spring Data Redis 2.x 中 RedisConfiguration 类的新编写方法
在 Spring Data Redis 1.x 的时候,我们可能会在项目中编写这样一个RedisConfig类: @Configuration @EnableCaching public class ...
- Spring Boot使用Spring Data Redis操作Redis(单机/集群)
说明:Spring Boot简化了Spring Data Redis的引入,只要引入spring-boot-starter-data-redis之后会自动下载相应的Spring Data Redis和 ...
随机推荐
- [POJ2976] Dropping tests
传送门:>Here< 题意:给出长度相等的数组a和b,定义他们的和为$\dfrac{a_1+a_2+...+a_n}{b_1+b_2+...+b_n}$.现在可以舍弃k对元素(一对即$a[ ...
- windows环境pip安装时一直报错Could not fetch URL https://pypi.org/simple/xrld/: There was a problem confirming the ssl certificate: HTTPSConnectionPool(host='pypi.org', port=443): Max retries exceeded with url:
最近项目不忙了~~有开始专研的python大业,上来想用pip安装一个第三方的库,就一直报错: Could not fetch URL https://pypi.org/simple/xrld/: T ...
- WIN8.1下Prolific USB-to-Serial Comm Port驱动黄色感叹号问题
文章解决来源:http://blog.csdn.net/gsj0791/article/details/17664861 在做fpga口的uart回环测试时候,由于开发板上的是usb转uart,所以需 ...
- 正睿 2019 省选附加赛 Day10
A 核心就是一个公式 \[\sum_{i = 0}^{k} S(k, i) \tbinom{x}{i} i\] S是第二类斯特林数 递推公式 \(S_2(n,k)=S_2(n−1,k−1)+kS_2( ...
- HNOI2019
省选总结 day0 写了下平时不经常写的模板,像什么\(LCT\),圆方树,\(exlucas\)之类的,但是一个都没考. day1 提前十几分钟进了考场,可以提前动电脑,赶紧把\(vimrc\)打了 ...
- CS Academy Sliding Product Sum(组合数)
题意 有一个长为 \(N\) 的序列 \(A = [1, 2, 3, \dots, N]\) ,求所有长度 \(\le K\) 的子串权值积的和,对于 \(M\) 取模. \(N \le 10^{18 ...
- 自学Python4.8-生成器(方式二:生成器表达式)
自学Python之路-Python基础+模块+面向对象自学Python之路-Python网络编程自学Python之路-Python并发编程+数据库+前端自学Python之路-django 自学Pyth ...
- rt-thread之stm32系列BSP制作方法
@2019-01-24 [小记] bsp制作方法: 官网下载 rt-thread 源码,将路径 bsp/stm32/libraries/templates/ 下的模板文件,Copy至路径 bsp/st ...
- linux防火墙,高级策略策略实例详解(实例一)
双线服务器的控制问题: 要求:写出这个电信用户访问到双线web服务器时的IP变化过程(只写源IP,目标IP,和做SNAT还是DNAT等) 你觉得有没有问题? 实验环境: 精简一点可以使用下面的四台虚拟 ...
- terminate called without an active exception异常
在gcc4.4下,采用回调机制写了一个类似std::thread的线程类. 但是使用时却发生了核心已转移的错误. main函数调用的代码大致是 int main(int argc, char *arg ...