目录:java
1.项目集成redis客户端jedisweb
引入Jedis pomredis
2.redis链接池构建及调试json
1)JedisPoolConfig源码解析浏览器
2)JedisPool源码解析缓存
3)JedisPool回收资源tomcat
4)封装redisPoolcookie
1 public class RedisPool { 2 //声明成static的缘由:保证jedis链接池在tomcat启动时就加载出来 3 //jedis链接池 4 private static JedisPool pool; 5 //与redis链接池链接的最大链接数 6 private static Integer maxTotal = Integer.parseInt(PropertiesUtil.getProperty("redis.max.total", "20")); 7 //在这个链接池中最多有多少个状态为idle的jedis实例,jedis链接池里就是jedis的实例,idle就是空闲的jedis实例 8 //在jedis链接池中最大的idle状态(空闲的)的jedis实例的个数 9 private static Integer maxIdle = Integer.parseInt(PropertiesUtil.getProperty("redis.max.idle", "10")); 10 //在jedis链接池中最小的idle状态(空闲的)的jedis实例的个数 11 private static Integer minIdle = Integer.parseInt(PropertiesUtil.getProperty("redis.min.idle", "2")); 12 13 //在borrow一个jedis实例的时候,是否要进行验证操做,若是赋值为true,则获得的jedis实例确定是可用的 14 private static Boolean testOnBorrow = Boolean.parseBoolean(PropertiesUtil.getProperty("redis.test.borrow", "true")); 15 //在return一个jedis实例的时候,是否要进行验证操做,若是赋值为true,则返回jedis链接池的jedis实例确定是可用的 16 private static Boolean testOnReturn = Boolean.parseBoolean(PropertiesUtil.getProperty("redis.test.return", "true")); 17 18 private static String redisIp = PropertiesUtil.getProperty("redis.ip"); 19 private static Integer redisPort = Integer.parseInt(PropertiesUtil.getProperty("redis.port")); 20 21 //初始化链接池,只会调用一次 22 private static void initPool() { 23 JedisPoolConfig config = new JedisPoolConfig(); 24 25 config.setMaxTotal(maxTotal); 26 config.setMaxIdle(maxIdle); 27 config.setMinIdle(minIdle); 28 29 config.setTestOnBorrow(testOnBorrow); 30 config.setTestOnReturn(testOnReturn); 31 32 //链接池耗尽的时候,是否阻塞,false会抛出异常,true阻塞直到超时,会抛出超时异常,默认为true 33 config.setBlockWhenExhausted(true); 34 35 //这里超时时间是2s 36 pool = new JedisPool(config, redisIp, redisPort, 1000*2); 37 38 } 39 40 static { 41 initPool(); 42 } 43 44 //从链接池中拿取一个实例 45 public static Jedis getJedis() { 46 return pool.getResource(); 47 } 48 49 //将正常实例放回jedis链接池 50 public static void returnResource(Jedis jedis) { 51 pool.returnResource(jedis); 52 } 53 54 //将破损实例放回jedis链接池 55 public static void returnBrokenResource(Jedis jedis) { 56 pool.returnResource(jedis); 57 } 58 59 //测试是否与redis-server正常链接上 60 // public static void main(String[] args) { 61 // Jedis jedis = pool.getResource(); 62 // jedis.set("kkk", "ddd"); 63 // returnResource(jedis); 64 // pool.destroy(); 65 // System.out.println("end"); 66 // } 67 68 }
3.jedis API封装及调试session
封装RedisPoolUtilapp
1 @Slf4j 2 public class RedisPoolUtil { 3 4 //从新设置有效期 5 //参数只有key和有效期,由于只须要根据key设置有效期便可 6 public static Long expire(String key, int exTime) { 7 Jedis jedis = null; 8 Long result = null; 9 try { 10 jedis = RedisPool.getJedis(); 11 //设置有效期 12 result = jedis.expire(key, exTime); 13 } catch (Exception e) { 14 log.error("setex key:{} error", key, e); 15 RedisPool.returnBrokenResource(jedis); 16 return result; 17 } 18 RedisPool.returnResource(jedis); 19 return result; 20 } 21 22 //exTime单位是s,设置session有效时间 23 //当用户初次登陆的时候,须要设置有限期,存在redis session中 24 //后续若是用户再次请求登陆,则只须要调用expire,从新设置有效期便可 25 public static String setEx(String key, String value, int exTime) { 26 Jedis jedis = null; 27 String result = null; 28 try { 29 jedis = RedisPool.getJedis(); 30 result = jedis.setex(key, exTime, value); 31 } catch (Exception e) { 32 log.error("setex key:{} value:{} error", key, value, e); 33 RedisPool.returnBrokenResource(jedis); 34 return result; 35 } 36 RedisPool.returnResource(jedis); 37 return result; 38 } 39 40 public static String set(String key, String value) { 41 Jedis jedis = null; 42 //jedis返回的结果 43 String result = null; 44 try { 45 jedis = RedisPool.getJedis(); 46 //设置key-value 47 result = jedis.set(key, value); 48 } catch (Exception e) { 49 log.error("set key:{} value:{} error", key, value, e); 50 RedisPool.returnBrokenResource(jedis); 51 return result; 52 } 53 RedisPool.returnResource(jedis); 54 return result; 55 } 56 57 public static String get(String key) { 58 Jedis jedis = null; 59 String result = null; 60 try { 61 jedis = RedisPool.getJedis(); 62 //根据key获取value值 63 result = jedis.get(key); 64 } catch (Exception e) { 65 log.error("set key:{} error", key, e); 66 RedisPool.returnBrokenResource(jedis); 67 return result; 68 } 69 RedisPool.returnResource(jedis); 70 return result; 71 } 72 73 public static Long del(String key) { 74 Jedis jedis = null; 75 Long result = null; 76 try { 77 jedis = RedisPool.getJedis(); 78 //根据key删除key-value 79 result = jedis.del(key); 80 } catch (Exception e) { 81 log.error("set key:{} error", key, e); 82 RedisPool.returnBrokenResource(jedis); 83 return result; 84 } 85 RedisPool.returnResource(jedis); 86 return result; 87 } 88 89 //单步调试上面的各个方法 90 // public static void main(String[] args) { 91 // Jedis jedis = RedisPool.getJedis(); 92 // RedisPoolUtil.set("keyTest", "value"); 93 // String value = RedisPoolUtil.get("keyTest"); 94 // RedisPoolUtil.setEx("keyex", "valueex", 60*10); 95 // RedisPoolUtil.expire("keyTest", 60*20); 96 // RedisPoolUtil.del("keyTest"); 97 // System.out.println("end"); 98 // } 99 }
4.json封装jsonUtil及调试
1)封装及调试
2)多泛型(一个对象里面有多个泛型,例如map里面的key-value,value里面是一个set或list,里面又是一个泛型)序列化和反序列化
1 public class JsonUtil { 2 private static ObjectMapper objectMapper = new ObjectMapper(); 3 static { 4 //序列化 5 //对象的全部字段所有列入 6 objectMapper.setSerializationInclusion(Inclusion.ALWAYS); 7 //取消默认转换timestamps形式 8 objectMapper.configure(SerializationConfig.Feature.WRITE_DATE_KEYS_AS_TIMESTAMPS, false); 9 //忽略空Bean转json的错误 10 objectMapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false); 11 //全部的日期格式都统一为如下的样式,即yyyy-MM-dd HH:mm:ss 12 objectMapper.setDateFormat(new SimpleDateFormat(DateTimeUtil.STANDARD_FORMAT)); 13 14 //反序列化 15 //忽略在json字符串中存在,但在java对象中不存在对应属性的状况,防止错误 16 objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false); 17 } 18 19 //对象转json字符串 20 public static <T> String obj2String(T obj) { 21 if(obj == null) { 22 return null; 23 } 24 try { 25 return obj instanceof String ? (String)obj : objectMapper.writeValueAsString(obj); 26 } catch (Exception e) { 27 log.warn("Parse object to String error", e); 28 return null; 29 } 30 } 31 32 //返回格式化好的json字符串 33 public static <T> String obj2StringPretty(T obj) { 34 if(obj == null) { 35 return null; 36 } 37 try { 38 return obj instanceof String ? (String)obj : objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj); 39 } catch (Exception e) { 40 log.warn("Parse Object to String error", e); 41 return null; 42 } 43 } 44 45 //json字符串转对象 46 //只能针对一个对象的状况,若是是list,就不适用 47 public static <T> T string2Obj(String str, Class<T> clazz) { 48 if(StringUtils.isEmpty(str) || clazz == null) { 49 return null; 50 } 51 try { 52 return clazz.equals(String.class) ? (T)str : objectMapper.readValue(str, clazz); 53 } catch (Exception e) { 54 log.warn("Parse String to Object error", e); 55 return null; 56 } 57 } 58 59 //能够针对list的状况,只要第二个参数传入new TypeReference<List<User>>就能够 60 public static <T> T string2Obj(String str, TypeReference<T> typeReference) { 61 if(StringUtils.isEmpty(str) || typeReference == null) { 62 return null; 63 } 64 try { 65 return (T)(typeReference.getType().equals(String.class) ? str : objectMapper.readValue(str, typeReference)); 66 } catch (Exception e) { 67 log.warn("Parse String to Object error", e); 68 return null; 69 } 70 } 71 72 //能够针对List的状况,只要传入List.class和User.class就能够了 73 public static <T> T string2Obj(String str,Class<?> collectionClass,Class<?>... elementClasses){ 74 JavaType javaType = objectMapper.getTypeFactory().constructParametricType(collectionClass,elementClasses); 75 try { 76 return objectMapper.readValue(str,javaType); 77 } catch (Exception e) { 78 log.warn("Parse String to Object error",e); 79 return null; 80 } 81 } 82 83 /* public static void main(String[] args) { 84 User u1 = new User(); 85 u1.setId(1); 86 u1.setEmail("kkk@163.com"); 87 User u2 = new User(); 88 u2.setId(2); 89 u2.setEmail("iii@163.com"); 90 91 String user1Json = JsonUtil.obj2String(u1); 92 String user1JsonPretty = JsonUtil.obj2StringPretty(u1); 93 94 log.info("user1Json:{}", user1Json); 95 log.info("user1JsonPretty:{}", user1JsonPretty); 96 97 User user = JsonUtil.string2Obj(user1Json, User.class); 98 99 List<User> userList = Lists.newArrayList(); 100 userList.add(u1); 101 userList.add(u2); 102 String userListStr = JsonUtil.obj2StringPretty(userList); 103 104 log.info("------------------"); 105 log.info(userListStr); 106 107 // List<User> userListObj = JsonUtil.string2Obj(userListStr, List.class); 108 // List<User> userListObj = JsonUtil.string2Obj(userListStr, new TypeReference<List<User>>() { 109 // }); 110 List<User> userListObj = JsonUtil.string2Obj(userListStr, List.class, User.class); 111 System.out.println("end"); 112 }*/ 113 }
5.json ObjectMapper源码解析
1)Inclusion.ALWAYS
2)Inclusion.NON_NULL
3)Inclusion.NON_DEFAULT
4)Inclusion.NON_EMPTY
5)SerializationConfig.Feature.WRITE_DATES_AS_TIMESTAMPSInclusion.NON_NULL
6)SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS
7)DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES
8)ObjectMapper DateFormat
6.cookie封装及使用
1)写cookie
2)读cookie
3)删cookie
4)domain
5)path
6)maxAge
7)httponly
1 public class CookieUtil { 2 //将这个cookie放在一级域名下,则二级域名www.mall.com、user.mall.com等均可以访问到这个cookie,而同级域名是访问不到的 3 private final static String COOKIE_DOMAIN = ".mall.com"; 4 //这个名字会由服务端种到客户端的浏览器上, 5 private final static String COOKIE_NAME = "mall_login_token"; 6 7 //获取cookie 8 public static String readLoginToken(HttpServletRequest request) { 9 Cookie[] cks = request.getCookies(); 10 if(cks != null) { 11 for(Cookie ck : cks) { 12 log.info("cookieName:{}, cookieValue:{}", ck.getName(), ck.getValue()); 13 if(StringUtils.equals(ck.getName(), COOKIE_NAME)) { 14 log.info("return cookie_name:{}, cookie_value:{}", ck.getName(), ck.getValue()); 15 return ck.getValue(); 16 } 17 } 18 } 19 return null; 20 } 21 22 //X:domain=".mall.com",a,b,c,d,e都能拿到这个cookie 23 //a,b不能看到彼此的cookie 24 //a:A.mall.com cookie:domain = A.mall.com;path = "/" 25 //b:B.mall.com cookie:domain = B.mall.com;path = "/" 26 //c,d能共享a的cookie,由于domain相同;c,d也能共享e的cookie,由于domain和path 27 //c,d不能看到彼此的cookie,也不能看到b的cookie 28 //c:A.mall.com/test/cc cookie:domain = A.mall.com;path = "/test/cc" 29 //d:A.mall.com/test/dd cookie:domain = A.mall.com;path = "/test/dd" 30 //e:A.mall.com/test cookie:domain = A.mall.com;path = "/test" 31 //登陆时,写入cookie,这个token就是sessionId 32 public static void writeLoginToken(HttpServletResponse response, String token) { 33 Cookie ck = new Cookie(COOKIE_NAME, token); 34 ck.setDomain(COOKIE_DOMAIN); 35 //"/"表明设置在根目录, 36 ck.setPath("/"); 37 //禁止经过脚本访问cookie,能够防止脚本攻击泄露信息 38 ck.setHttpOnly(true); 39 //若是是-1,表明永久,单位是s;若是不设置这个变量,则cookie不会写入硬盘,而只是写在内存,值在当前页面有效 40 ck.setMaxAge(60 * 60 * 24 * 365); 41 log.info("write cookie_name:{}, cookie_value:{}", ck.getName(), ck.getValue()); 42 response.addCookie(ck); 43 } 44 45 //注销时删除cookie 46 public static void delLoginToken(HttpServletRequest request, HttpServletResponse response) { 47 Cookie[] cks = request.getCookies(); 48 if(cks != null) { 49 for(Cookie ck : cks) { 50 if(StringUtils.equals(ck.getName(), COOKIE_NAME)) { 51 ck.setDomain(COOKIE_DOMAIN); 52 ck.setPath("/"); 53 //设置成0,表明删除此cookie 54 ck.setMaxAge(0); 55 log.info("del cookie_name:{}, cookie_value:{}", ck.getName(), ck.getValue()); 56 response.addCookie(ck); 57 return; 58 } 59 } 60 } 61 } 62 }
7.SessionExpireFilter重置session有效期
因为用户在第一次登陆的时候,会设置session有效期,为了session不过时,在用户每一次请求页面数据的时候,就重置一下session有效期。使用过滤器实现。
Filter类:
1 public class SessionExpireFilter implements Filter { 2 3 @Override 4 public void init(FilterConfig filterConfig) throws ServletException { 5 6 } 7 8 @Override 9 public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { 10 HttpServletRequest httpServletRequest = (HttpServletRequest)servletRequest; 11 String loginToken = CookieUtil.readLoginToken(httpServletRequest); 12 if(StringUtils.isNotEmpty(loginToken)) { 13 //判断loginToken是否为空或者"" 14 //若是不为空的话,符合条件,继续拿user信息 15 String userJsonStr = RedisShardedPoolUtil.get(loginToken); 16 User user = JsonUtil.string2Obj(userJsonStr, User.class); 17 if(user != null) { 18 //若是user不为空,则重置session的时间,即调用expire命令 19 RedisShardedPoolUtil.expire(loginToken, Const.RedisCacheExtime.REDIS_SESSION_EXTIME); 20 } 21 } 22 filterChain.doFilter(servletRequest, servletResponse); 23 } 24 25 @Override 26 public void destroy() { 27 28 } 29 }
web.xml配置过滤器:
1 <filter> 2 <filter-name>sessionExpireFilter</filter-name> 3 <filter-class>com.mall.controller.common.SessionExpireFilter</filter-class> 4 </filter> 5 <filter-mapping> 6 <filter-name>sessionExpireFilter</filter-name> 7 <url-pattern>*.do</url-pattern> 8 </filter-mapping>
8.用户session相关模块重构
9.Guava cache迁移redis缓存
1 public class JsonUtil { 2 private static ObjectMapper objectMapper = new ObjectMapper(); 3 static { 4 //序列化 5 //对象的全部字段所有列入 6 objectMapper.setSerializationInclusion(Inclusion.ALWAYS); 7 //取消默认转换timestamps形式 8 objectMapper.configure(SerializationConfig.Feature.WRITE_DATE_KEYS_AS_TIMESTAMPS, false); 9 //忽略空Bean转json的错误 10 objectMapper.configure(SerializationConfig.Feature.FAIL_ON_EMPTY_BEANS, false); 11 //全部的日期格式都统一为如下的样式,即yyyy-MM-dd HH:mm:ss 12 objectMapper.setDateFormat(new SimpleDateFormat(DateTimeUtil.STANDARD_FORMAT)); 13 14 //反序列化 15 //忽略在json字符串中存在,但在java对象中不存在对应属性的状况,防止错误 16 objectMapper.configure(DeserializationConfig.Feature.FAIL_ON_UNKNOWN_PROPERTIES, false); 17 } 18 19 //对象转json字符串 20 public static <T> String obj2String(T obj) { 21 if(obj == null) { 22 return null; 23 } 24 try { 25 return obj instanceof String ? (String)obj : objectMapper.writeValueAsString(obj); 26 } catch (Exception e) { 27 log.warn("Parse object to String error", e); 28 return null; 29 } 30 } 31 32 //返回格式化好的json字符串 33 public static <T> String obj2StringPretty(T obj) { 34 if(obj == null) { 35 return null; 36 } 37 try { 38 return obj instanceof String ? (String)obj : objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(obj); 39 } catch (Exception e) { 40 log.warn("Parse Object to String error", e); 41 return null; 42 } 43 } 44 45 //json字符串转对象 46 //只能针对一个对象的状况,若是是list,就不适用 47 public static <T> T string2Obj(String str, Class<T> clazz) { 48 if(StringUtils.isEmpty(str) || clazz == null) { 49 return null; 50 } 51 try { 52 return clazz.equals(String.class) ? (T)str : objectMapper.readValue(str, clazz); 53 } catch (Exception e) { 54 log.warn("Parse String to Object error", e); 55 return null; 56 } 57 } 58 59 //能够针对list的状况,只要第二个参数传入new TypeReference<List<User>>就能够 60 public static <T> T string2Obj(String str, TypeReference<T> typeReference) { 61 if(StringUtils.isEmpty(str) || typeReference == null) { 62 return null; 63 } 64 try { 65 return (T)(typeReference.getType().equals(String.class) ? str : objectMapper.readValue(str, typeReference)); 66 } catch (Exception e) { 67 log.warn("Parse String to Object error", e); 68 return null; 69 } 70 } 71 72 //能够针对List的状况,只要传入List.class和User.class就能够了 73 public static <T> T string2Obj(String str,Class<?> collectionClass,Class<?>... elementClasses){ 74 JavaType javaType = objectMapper.getTypeFactory().constructParametricType(collectionClass,elementClasses); 75 try { 76 return objectMapper.readValue(str,javaType); 77 } catch (Exception e) { 78 log.warn("Parse String to Object error",e); 79 return null; 80 } 81 } 82 83 /* public static void main(String[] args) { 84 User u1 = new User(); 85 u1.setId(1); 86 u1.setEmail("kkk@163.com"); 87 User u2 = new User(); 88 u2.setId(2); 89 u2.setEmail("iii@163.com"); 90 91 String user1Json = JsonUtil.obj2String(u1); 92 String user1JsonPretty = JsonUtil.obj2StringPretty(u1); 93 94 log.info("user1Json:{}", user1Json); 95 log.info("user1JsonPretty:{}", user1JsonPretty); 96 97 User user = JsonUtil.string2Obj(user1Json, User.class); 98 99 List<User> userList = Lists.newArrayList(); 100 userList.add(u1); 101 userList.add(u2); 102 String userListStr = JsonUtil.obj2StringPretty(userList); 103 104 log.info("------------------"); 105 log.info(userListStr); 106 107 // List<User> userListObj = JsonUtil.string2Obj(userListStr, List.class); 108 // List<User> userListObj = JsonUtil.string2Obj(userListStr, new TypeReference<List<User>>() { 109 // }); 110 List<User> userListObj = JsonUtil.string2Obj(userListStr, List.class, User.class); 111 System.out.println("end"); 112 }*/ 113 }