本次使用動態代理的初衷是學習Redis,使用Java操作Redis時用到Jedis的JedisPool,而後對Jedis的方法進一步封裝完善成為一個工具類。因為直接使用Jedis對象時,為了保證性能,總會需要手動的獲取到連接池中的連接,使用完成後還需要手動的釋放連接,都是完全重覆的操作。所以想要使用 ...
本次使用動態代理的初衷是學習Redis,使用Java操作Redis時用到Jedis的JedisPool,而後對Jedis的方法進一步封裝完善成為一個工具類。
因為直接使用Jedis對象時,為了保證性能,總會需要手動的獲取到連接池中的連接,使用完成後還需要手動的釋放連接,都是完全重覆的操作。
所以想要使用一些方法來抽取出這些重覆操作,初時想到的是使用模板方法或是動態代理,但考慮到到模板方法會使用到大量的類,似乎與主旨不符,遂使用動態代理進行加強。
實現動態代理可使用JDK對應的方法,或是CGlib。由於工具類本身不會去定義介面,所以選擇CGlib。本身是在SpringBoot項目中進行,所以沒有額外導入CGlib的包(spring的面向切麵就使用到了CGlib與JDK自帶的動態 代理)。
具體的思路就是使用動態代理來增強工具類對象,使得工具類在調用方法前後對應的進行獲取連接及釋放連接操作。在獲得到增強工具類後只需要進行相關操作即可。
使用了Maven來管理項目,代碼所需的依賴如下
1 <!-- Jedis --> 2 <dependency> 3 <groupId>com.redislabs</groupId> 4 <artifactId>jredisearch-jedis</artifactId> 5 <version>3.0.0-20180508</version> 6 </dependency>
此外還需要自己電腦安裝Redis,或有對應的伺服器
代碼如下
Redis連接池代碼
1 package com; 2 3 import org.apache.logging.log4j.Logger; 4 5 import redis.clients.jedis.Jedis; 6 import redis.clients.jedis.JedisPool; 7 import redis.clients.jedis.JedisPoolConfig; 8 /** 9 * Jedis連接池 10 * 11 * @author zaizouGGG 12 * 13 */ 14 public class RedisPool { 15 16 private Logger logger = LogUtil.get(JedisUtil.class); 17 private static SysConfigUtil sysConfigUtil = SysConfigUtil.getSysConfigUtil("redis.properties"); 18 19 //聲明成static的原因:保證jedis連接池在tomcat啟動時就載入出來 20 //jedis連接池 21 private static JedisPool pool; 22 //與redis連接池連接的最大連接數 23 private static Integer maxTotal = sysConfigUtil.getInt("redis.maxTotal"); 24 //在jedis連接池中最大的idle狀態(空閑的)的jedis實例的個數 25 private static Integer maxIdle = sysConfigUtil.getInt("redis.maxIdle"); 26 //在jedis連接池中最小的idle狀態(空閑的)的jedis實例的個數 27 private static Integer minIdle = sysConfigUtil.getInt("redis.minIdle"); 28 //在borrow一個jedis實例的時候,是否要進行驗證操作,如果賦值為true,則得到的jedis實例肯定是可用的 29 private static Boolean testOnBorrow = sysConfigUtil.getBoolean("redis.testOnBorrow"); 30 //在return一個jedis實例的時候,是否要進行驗證操作,如果賦值為true,則返回jedis連接池的jedis實例肯定是可用的 31 private static Boolean testOnReturn = sysConfigUtil.getBoolean("redis.testOnReturn"); 32 private static String redisIp = sysConfigUtil.getString("redis.host"); 33 private static Integer redisPort = sysConfigUtil.getInt("redis.port"); 34 private static String password = sysConfigUtil.getString("redis.password"); 35 36 //初始化連接池,只會調用一次 37 private static void initPool() { 38 JedisPoolConfig config = new JedisPoolConfig(); 39 40 config.setMaxTotal(maxTotal); 41 config.setMaxIdle(maxIdle); 42 config.setMinIdle(minIdle); 43 44 config.setTestOnBorrow(testOnBorrow); 45 config.setTestOnReturn(testOnReturn); 46 47 //連接池耗盡的時候,是否阻塞,false會拋出異常,true阻塞直到超時,會拋出超時異常,預設為true 48 config.setBlockWhenExhausted(true); 49 50 //這裡超時時間是2s 51 if (password != null && !"".equals(password)) { 52 // redis 設置了密碼 53 pool = new JedisPool(config, redisIp, redisPort, 1000*2, password); 54 } else { 55 // redis 未設置密碼 56 pool = new JedisPool(config, redisIp, redisPort, 1000*2); 57 }; 58 59 60 } 61 62 static { 63 initPool(); 64 } 65 66 //從連接池中拿取一個實例 67 public static Jedis getJedis() { 68 return pool.getResource(); 69 } 70 71 //返還Jedis 72 public static void returnJedis(Jedis jedis) { 73 jedis.close(); 74 } 75 76 //被使用的連接數 77 public static int getNumActive() { 78 return pool.getNumActive(); 79 } 80 81 //被阻塞的連接數? 82 public static int getNumWaiters() { 83 return pool.getNumWaiters(); 84 } 85 86 //空閑的連接數 87 public static int getNumIdle() { 88 return pool.getNumIdle(); 89 } 90 }
讀取.properties配置文件的工具類
用於讀取配置在.properties中的參數
1 package com; 2 3 import java.io.BufferedInputStream; 4 import java.io.FileInputStream; 5 import java.io.IOException; 6 import java.io.InputStream; 7 import java.util.Properties; 8 9 import org.apache.logging.log4j.Logger; 10 11 /** 12 * 獲取自定義配置文件參數的共用方法 13 * 14 * @author zaizouGGG
15 * 16 */ 17 public class SysConfigUtil { 18 19 private static Logger logger = LogUtil.get(SysConfigUtil.class); 20 private Properties properties = null; 21 22 public SysConfigUtil() { 23 24 } 25 26 public SysConfigUtil(Properties p) { 27 properties = p; 28 } 29 30 31 public static SysConfigUtil getSysConfigUtil(String url) { 32 try(InputStream in = new BufferedInputStream(new FileInputStream(url));) { 33 Properties p = new Properties(); 34 p.load(in); 35 //return p.getProperty("jdbc.type"); 36 return new SysConfigUtil(p); 37 } catch (IOException e) { 38 // TODO Auto-generated catch block 39 logger.error("Jedis工具類初始化失敗,讀取配置文件 "+url+" 出錯"); 40 e.printStackTrace(); 41 return null; 42 } 43 44 } 45 46 public String getString(String key) { 47 return properties.getProperty(key); 48 } 49 50 public int getInt(String key) { 51 int result = Integer.parseInt(properties.getProperty(key)); 52 return result; 53 } 54 55 public boolean getBoolean(String key) { 56 boolean result = Boolean.parseBoolean(properties.getProperty(key)); 57 return result; 58 } 59 60 public long getLong(String key) { 61 long result = Long.parseLong(properties.getProperty(key)); 62 return result; 63 } 64 }
Redis配置文件
此處的配置是我從網上找的,普通的使用沒有問題,要應用請慎重
redis.host=localhost
redis.port=6379
redis.password=root
#在指定時刻通過pool能夠獲取到的最大的連接的jedis個數
redis.maxTotal=8
#最大能夠保持idle的數量,控制一個pool最多有多少個狀態為idle的jedis實例
redis.maxIdle=8
#最小能夠保持idle的數量,控制一個pool最多有多少個狀態為idle的jedis實例
redis.minIdle=2
#在borrow一個jedis實例的時候,是否要進行驗證操作,如果賦值為true,則得到的jedis實例肯定是可用的
redis.testOnBorrow=true
#在return一個jedis實例的時候,是否要進行驗證操作,如果賦值為true,則返回jedis連接池的jedis實例肯定是可用的
redis.testOnReturn=false
#當連接池內的連接耗盡時,getBlockWhenExhausted為true時,連接會阻塞,超過了阻塞的時間(設定的maxWaitMillis,單位毫秒)時會報錯
redis.maxWaitMillis=3000
#在borrow一個jedis實例時,是否提前進行validate操作;如果為true,則得到的jedis實例均是可用的;預設是false
redis.testOnBorrow=false
Jedis的工具類
1 package com; 2 3 import java.io.IOException; 4 import java.util.List; 5 import java.util.Map; 6 import java.util.Set; 7 8 import org.apache.logging.log4j.Logger; 9 10 import redis.clients.jedis.Jedis; 11 import redis.clients.jedis.JedisPool; 12 import redis.clients.jedis.JedisPoolConfig; 13 import redis.clients.jedis.ListPosition; 14 15 /** 16 * Jedis 通用工具類 17 * @author zaizouGGG 18 * 19 */ 20 public class JedisUtil { 21 22 private Logger logger = LogUtil.get(JedisUtil.class); 23 private Jedis jedis = null; 24 25 public Jedis getJedis() { 26 return jedis; 27 } 28 29 public void setJedis(Jedis jedis) { 30 this.jedis = jedis; 31 } 32 33 /** 34 * 獲取指定key的值,如果key不存在返回null,如果該Key存儲的不是字元串,會拋出一個錯誤 35 * 36 * @param key 37 * @return 38 */ 39 public String get(String key) { 40 String value = jedis.get(key); 41 return value; 42 } 43 44 /** 45 * 設置key的值為value 46 * 47 * @param key 48 * @param value 49 * @return 50 */ 51 public String set(String key, String value) { 52 String result = jedis.set(key, value); 53 return result; 54 } 55 56 /** 57 * 刪除指定的key,也可以傳入一個包含key的數組 58 * 59 * @param keys 60 * @return 61 */ 62 public Long del(String... keys) { 63 64 return jedis.del(keys); 65 } 66 67 /** 68 * 通過key向指定的value值追加值 69 * 70 * @param key 71 * @param str 72 * @return 73 */ 74 public Long append(String key, String str) { 75 76 return jedis.append(key, str); 77 } 78 79 /** 80 * 判斷key是否存在 81 * 82 * @param key 83 * @return 84 */ 85 public Boolean exists(String key) { 86 87 return jedis.exists(key); 88 } 89 90 /** 91 * 設置key value,如果key已經存在則返回0 92 * 93 * @param key 94 * @param value 95 * @return 96 */ 97 public Long setnx(String key, String value) { 98 99 return jedis.setnx(key, value); 100 } 101 102 /** 103 * 設置key value並指定這個鍵值的有效期 104 * 105 * @param key 106 * @param seconds 107 * @param value 108 * @return 109 */ 110 public String setex(String key, String value, int seconds) { 111 String result = jedis.setex(key, seconds, value); 112 return result; 113 } 114 115 /** 116 * 通過key 和offset 從指定的位置開始將原先value替換 117 * 118 * @param key 119 * @param offset 120 * @param str 121 * @return 122 */ 123 public Long setrange(String key, int offset, String str) { 124 125 return jedis.setrange(key, offset, str); 126 } 127 128 /** 129 * 通過批量的key獲取批量的value 130 * 131 * @param keys 132 * @return 133 */ 134 public List<String> mget(String... keys) { 135 136 return jedis.mget(keys); 137 } 138 139 /** 140 * 批量的設置key:value,也可以一個 141 * 142 * @param keysValues 143 * @return 144 */ 145 public String mset(String... keysValues) { 146 147 return jedis.mset(keysValues); 148 } 149 150 /** 151 * 批量的設置key:value,可以一個,如果key已經存在則會失敗,操作會回滾 152 * 153 * @param keysValues 154 * @return 155 */ 156 public Long msetnx(String... keysValues) { 157 158 return jedis.msetnx(keysValues); 159 } 160 161 /** 162 * 設置key的值,並返回一個舊值 163 * 164 * @param key 165 * @param value 166 * @return 167 */ 168 public String getSet(String key, String value) { 169 170 return jedis.getSet(key, value); 171 } 172 173 /** 174 * 通過下標 和key 獲取指定下標位置的 value 175 * 176 * @param key 177 * @param startOffset 178 * @param endOffset 179 * @return 180 */ 181 public String getrange(String key, int startOffset, int endOffset) { 182 183 return jedis.getrange(key, startOffset, endOffset); 184 } 185 186 /** 187 * 通過key 對value進行加值+1操作,當value不是int類型時會返回錯誤,當key不存在是則value為1 188 * 189 * @param key 190 * @return 191 */ 192 public Long incr(String key) { 193 194 return jedis.incr(key); 195 } 196 197 /** 198 * 通過key給指定的value加值,如果key不存在,則這是value為該值 199 * 200 * @param key 201 * @param integer 202 * @return 203 */ 204 public Long incrBy(String key, long integer) { 205 206 return jedis.incrBy(key, integer); 207 } 208 209 /** 210 * 對key的值做減減操作,如果key不存在,則設置key為-1 211 * 212 * @param key 213 * @return 214 */ 215 public Long decr(String key) { 216 217 return jedis.decr(key); 218 } 219 220 /** 221 * 減去指定的值 222 * 223 * @param key 224 * @param integer 225 * @return 226 */ 227 public Long decrBy(String key, long integer) { 228 229 return jedis.decrBy(key, integer); 230 } 231 232 /** 233 * 通過key獲取value值的長度 234 * 235 * @param key 236 * @return 237 */ 238 public Long strLen(String key) { 239 240 return jedis.strlen(key); 241 } 242 243 /** 244 * 通過key給field設置指定的值,如果key不存在則先創建,如果field已經存在,返回0 245 * 246 * @param key 247 * @param field 248 * @param value 249 * @return 250 */ 251 public Long hsetnx(String key, String field, String value) { 252 253 return jedis.hsetnx(key, field, value); 254 } 255 256 /** 257 * 通過key給field設置指定的值,如果key不存在,則先創建 258 * 259 * @param key 260 * @param field 261 * @param value 262 * @return 263 */ 264 public Long hset(String key, String field, String value) { 265 266 return jedis.hset(key, field, value); 267 } 268 269 /** 270 * 通過key同時設置 hash的多個field 271 * 272 * @param key 273 * @param hash 274 * @return 275 */ 276 public String hmset(String key, Map<String, String> hash) { 277 278 return jedis.hmset(key, hash); 279 } 280 281 /** 282 * 通過key 和 field 獲取指定的 value 283 * 284 * @param key 285 * @param failed 286 * @return 287 */ 288 public String hget(String key, String failed) { 289 290 return jedis.hget(key, failed); 291 } 292 293 /** 294 * 設置key的超時時間為seconds 295 * 296 * @param key 297 * @param seconds 298 * @return 299 */ 300 public Long expire(String key, int seconds) { 301 302 return jedis.expire(key, seconds); 303 } 304 305 /** 306 * 通過key 和 fields 獲取指定的value 如果沒有對應的value則返回null 307 * 308 * @param key 309 * @param fields 可以是 一個String 也可以是 String數組 310 * @return 311 */ 312 public List<String> hmget(String key, String... fields) { 313 314 return jedis.hmget(key, fields); 315 } 316 317 /** 318 * 通過key給指定的field的value加上給定的值 319 * 320 * @param key 321 * @param field 322 * @param value 323 * @return 324 */ 325 public Long hincrby(String key, String field, Long value) { 326 327 return jedis.hincrBy(key, field, value); 328 } 329 330 /** 331 * 通過key和field判斷是否有指定的value存在 332 * 333 * @param key 334 * @param field 335 * @return 336 */ 337 public Boolean hexists(String key, String field) { 338 339 return jedis.hexists(key, field); 340 } 341 342 /**