目錄: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 }