Spring Boot中除了對經常使用的關係型數據庫提供了優秀的自動化支持以外,對於不少NoSQL數據庫同樣提供了自動化配置的支持,包括:Redis, MongoDB, 等。java
Redis簡單介紹redis
Redis是Redis是Remote DIctionary Server的縮寫,是目前業界使用最普遍的內存數據存儲。相比memcached,Redis支持更豐富的數據結構(Memcached徹底基於內存,而Redis具備持久化保存特性,Redis能夠將數據寫入到磁盤中(以字節(0101這樣的二進制數據)的形式寫入的),例如hashes, lists, sets等,同時支持數據持久化。除此以外,Redis還提供一些類數據庫的特性,好比事務,HA,主從庫。能夠說Redis兼具了緩存系統和數據庫的一些特性,所以有着豐富的應用場景。spring
Spring boot集成Redis數據庫
添加依賴json
Spring Boot提供的數據訪問框架Spring Data Redis基於Jedis。能夠經過引入spring-boot-starter-redis來配置依賴關係。數組
<!-- 添加Spring-boot-starter-redis依賴 --> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-redis</artifactId> </dependency>
對Redis進行配置,修改配置文件 application.properties緩存
# REDIS (RedisProperties) # Redis數據庫索引(默認爲0) spring.redis.database=0 # Redis服務器地址 spring.redis.host=localhost # Redis服務器鏈接端口 spring.redis.port=6379 # Redis服務器鏈接密碼(默認爲空) spring.redis.password=qpc_redis # 鏈接池最大鏈接數(使用負值表示沒有限制) spring.redis.pool.max-active=8 # 鏈接池最大阻塞等待時間(使用負值表示沒有限制) spring.redis.pool.max-wait=-1 # 鏈接池中的最大空閒鏈接 spring.redis.pool.max-idle=8 # 鏈接池中的最小空閒鏈接 spring.redis.pool.min-idle=0 # 鏈接超時時間(毫秒) spring.redis.timeout=0
其中spring.redis.database的配置一般使用0便可,Redis在配置的時候能夠設置數據庫數量,默認爲16,能夠理解爲數據庫的schema.安全
使用Redis服務器
使用自動配置的StringRedisTemplate對象進行Redis讀寫操做。數據結構
@RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(Application.class) public class ApplicationTest { private static final Logger LOG = Logger.getLogger(RedisApplicationTest.class); @Autowired private StringRedisTemplate stringRedisTemplate; //@Autowired //private RedisTemplate<Serializable, Object> redisTemplate; //@Autowired //private RedisService redisService; @Test public void testStringWithRedis(){ stringRedisTemplate.opsForValue().set("name", "guanguan"); String val = stringRedisTemplate.opsForValue().get("name"); Assert.assertEquals("guanguan", val); } }
固然,根據StringRedisTemplate對象命名咱們能夠知道該對象支持String類型,可是在實際的應用中,咱們可能須要存入Object對象。那該怎麼存儲呢。聰明的你,確定馬上想到了,直接把對象轉成json格式字符串,不就能夠存儲了嘛。這裏我使用jackson依賴轉換成json數據。
首先添加jackson依賴
<!-- java json解析依賴 --> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-annotations</artifactId> <version>2.9.3</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-core</artifactId> <version>2.9.3</version> </dependency> <dependency> <groupId>com.fasterxml.jackson.core</groupId> <artifactId>jackson-databind</artifactId> <version>2.9.3</version> </dependency>
實現json轉換工具類
public class JsonUtil { private static ObjectMapper objectMapper = new ObjectMapper(); public static String convertObj2String(Object object) { String s = null; try { s = objectMapper.writeValueAsString(object); } catch (JsonProcessingException e) { e.printStackTrace(); } return s; } public static <T> T convertString2Obj(String s, Class<T> clazz) { T t = null; try { t = objectMapper.readValue(s, clazz); } catch (IOException e) { e.printStackTrace(); } return t; } }
咱們知道,RedisTemplate 是 redis 模塊的核心類,是對 redis 操做的較高抽象具備豐富的特性。他關注的是序列化和鏈接管理,線程安全,提供了以下操做接口:
HashOperations
HyperLogLogOperations
ListOperations
SetOperations
ValueOperations
ZSetOperations
那咱們就實現一個通用的RedisService類完成Redis的讀寫操做
@Service public class RedisService { @Autowired private StringRedisTemplate redisTemplate; /** * 一週有多少秒 */ private static final long WEEK_SECONDS = 7 * 24 * 60 * 60; /** * 將 key,value 存放到redis數據庫中,默認設置過時時間爲一週 * * @param key * @param value */ public void set(String key, Object value) { redisTemplate.opsForValue().set(key, JsonUtil.convertObj2String(value), WEEK_SECONDS, TimeUnit.SECONDS); } /** * 將 key,value 存放到redis數據庫中,設置過時時間單位是秒 * * @param key * @param value * @param expireTime */ public void set(String key, Object value, long expireTime) { redisTemplate.opsForValue().set(key, JsonUtil.convertObj2String(value), expireTime, TimeUnit.SECONDS); } /** * 判斷 key 是否在 redis 數據庫中 * * @param key * @return */ public boolean exists(final String key) { return redisTemplate.hasKey(key); } /** * 獲取與 key 對應的對象 * @param key * @param clazz 目標對象類型 * @param <T> * @return */ public <T> T get(String key, Class<T> clazz) { String s = get(key); if (s == null) { return null; } return JsonUtil.convertString2Obj(s, clazz); } /** * 獲取 key 對應的字符串 * @param key * @return */ public String get(String key) { return redisTemplate.opsForValue().get(key); } /** * 刪除 key 對應的 value * @param key */ public void delete(String key) { redisTemplate.delete(key); } }
新建一個User對象
public class User implements Serializable{ /** * */ private static final long serialVersionUID = 3456232569272497427L; private int id; private String name; private int age; public User() { } public User(int id, String name, int age) { super(); this.id = id; this.name = name; this.age = age; } public int getId() { return id; } public void setId(int id) { this.id = id; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "User [id=" + id + ", name=" + name + ", age=" + age + "]"; } }
新建測試類
@RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(Application.class) public class ApplicationTest { private static final Logger LOG = Logger.getLogger(ApplicationTest.class); @Autowired private RedisService redisService; @Test public void testRedisService(){ User user3 = new User(2,"xiaoxiaoping",16); redisService.set("user3", user3, 1000*60l); User userV3 = redisService.get("user3",User.class); LOG.info("userV3====="+userV3.toString()); } }
測試結果
經過使用StringRedisTemplate對象徹底實現了對Object對象的存儲.經過redis-cli.exe能夠查看到咱們存儲的Object對象是json格式字符串,可是當某個對象很大時,這個json字符串會很冗長,那咱們有沒有其餘方式實現呢。若是有使用過spring-data-redis的開發者必定熟悉RedisTemplate<K, V>接口,StringRedisTemplate就至關於RedisTemplate<String, String>的實現。沒有使用過,能夠先看下StringRedisTemplate類源碼。
public class StringRedisTemplate extends RedisTemplate<String, String> { /** * Constructs a new <code>StringRedisTemplate</code> instance. {@link #setConnectionFactory(RedisConnectionFactory)} * and {@link #afterPropertiesSet()} still need to be called. */ public StringRedisTemplate() { RedisSerializer<String> stringSerializer = new StringRedisSerializer(); setKeySerializer(stringSerializer); setValueSerializer(stringSerializer); setHashKeySerializer(stringSerializer); setHashValueSerializer(stringSerializer); } /** * Constructs a new <code>StringRedisTemplate</code> instance ready to be used. * * @param connectionFactory connection factory for creating new connections */ public StringRedisTemplate(RedisConnectionFactory connectionFactory) { this(); setConnectionFactory(connectionFactory); afterPropertiesSet(); } protected RedisConnection preProcessConnection(RedisConnection connection, boolean existingConnection) { return new DefaultStringRedisConnection(connection); } }
從源碼分析,咱們能夠看出StringRedisTemplate實現RedisTemplate<K, V>接口,那咱們徹底能夠模仿寫一個RedisTemplate<Serializable, Object>模板類。可是Spring boot不支直接使用,因此根據源碼,咱們須要實現一個RedisSerializer<T>未來對傳入對象進行序列化和反序列化。這個實現類ObjectRedisSerializer能夠參考StringRedisSerializer類。另外,根據源碼,能夠發現,Redis默認的序列化方式爲JdkSerializationRedisSerializer ,利用JDK的序列化和反序列化,持久化就是以字節(0101這樣的二進制數據)的形式寫入的。
Redis存儲對象實現以下
添加ObjectRedisSerializer實現類,須要實現RedisSerializer<T>接口。
/** * 實現Redis對象的序列化接口 * 參考:JdkSerializationRedisSerializer源碼 * */ public class ObjectRedisSerializer implements RedisSerializer<Object>{ private static final Logger LOG = Logger.getLogger(ObjectRedisSerializer.class); /** * 定義序列化和發序列化轉化類 */ private Converter<Object, byte[]> serializer = new SerializingConverter(); private Converter<byte[], Object> deserializer = new DeserializingConverter(); /** * 定義轉換空字節數組 */ private static final byte[] EMPTY_ARRAY = new byte[0]; @Override public byte[] serialize(Object obj) throws SerializationException { byte[] byteArray = null; if (null == obj) { LOG.warn("Redis待序列化的對象爲空."); byteArray = EMPTY_ARRAY; } else { try { byteArray = serializer.convert(obj); } catch (Exception e) { LOG.error("Redis序列化對象失敗,異常:"+e.getMessage()); byteArray = EMPTY_ARRAY; } } return byteArray; } @Override public Object deserialize(byte[] datas) throws SerializationException { Object obj = null; if(isNullOrEmpty(datas)){ LOG.warn("Redis待反序列化的對象爲空."); }else{ try { obj = deserializer.convert(datas); } catch (Exception e) { LOG.error("Redis反序列化對象失敗,異常:"+e.getMessage()); } } return obj; } private boolean isNullOrEmpty(byte[] datas){ return (null == datas)|| (datas.length == 0); } }
建立RedisConfig配置類,將RedisTemplate的setValueSerializer設置成ObjectRedisSerializer轉換類。
@Configuration public class RedisConfig { // /** // * 鏈接 redis 須要 RedisConnection 和 RedisConnectionFactory, // * RedisConnection 是經過 RedisConnectionFactory 進行建立 // * RedisConnection 提供較低級的數據操做 (byte arrays) // */ // @Bean // RedisConnectionFactory initJedisConnectionFactory(){ // //在這裏設置redis鏈接對象配置 // return new JedisConnectionFactory(); // } /** * 配置RedisTemplate實例 * @param factory * @return */ @Bean public RedisTemplate<Serializable, Object> redisTemplate(RedisConnectionFactory connectionFactory) { RedisTemplate<Serializable, Object> template = new RedisTemplate<Serializable, Object>(); template.setConnectionFactory(connectionFactory); template.afterPropertiesSet(); template.setKeySerializer(new StringRedisSerializer()); template.setValueSerializer(new ObjectRedisSerializer()); return template; } }
須要注意幾點:
在添加RedisConfig配置時,由於鏈接redis須要RedisConnection和RedisConnectionFactory,RedisConnection是經過RedisConnectionFactory進行建立若注入JedisConnnectionFactory,若是咱們Redis設置了密碼,在從新注入RedisConnectionFactory(如上註釋代碼),就會報錯以下:
org.springframework.test.context.junit4.statements.RunAfterTestClassCallbacks.evaluate(RunAfterTestClassCallbacks.java:70) at org.junit.runners.ParentRunner.run(ParentRunner.java:363) at org.springframework.test.context.junit4.SpringJUnit4ClassRunner.run(SpringJUnit4ClassRunner.java:193) at org.eclipse.jdt.internal.junit4.runner.JUnit4TestReference.run(JUnit4TestReference.java:86) at org.eclipse.jdt.internal.junit.runner.TestExecution.run(TestExecution.java:38) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:459) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:678) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:382) at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.main(RemoteTestRunner.java:192) Caused by: redis.clients.jedis.exceptions.JedisDataException: NOAUTH Authentication required. at redis.clients.jedis.Protocol.processError(Protocol.java:117) at redis.clients.jedis.Protocol.process(Protocol.java:151) at redis.clients.jedis.Protocol.read(Protocol.java:205) at redis.clients.jedis.Connection.readProtocolWithCheckingBroken(Connection.java:297) at redis.clients.jedis.Connection.getStatusCodeReply(Connection.java:196) at redis.clients.jedis.BinaryJedis.set(BinaryJedis.java:126) at org.springframework.data.redis.connection.jedis.JedisConnection.set(JedisConnection.java:1136) ... 36 more
根據StringRedisTemplate源碼,在注入RedisTemplate<Serializable, Object>直接使用默認的鏈接對象便可。設置以下代碼:
template.setConnectionFactory(connectionFactory);
template.afterPropertiesSet();
或者咱們注入RedisConnectionFactory設置鏈接屬性應該也是能夠的,有興趣能夠嘗試下。
建立測試類
@RunWith(SpringJUnit4ClassRunner.class) @SpringApplicationConfiguration(Application.class) public class ApplicationTest { private static final Logger LOG = Logger.getLogger(ApplicationTest.class); @Autowired private RedisTemplate<Serializable, Object> redisTemplate; @Test public void testObjectWithRedis(){ User user1 = new User(1,"guanguan",18); redisTemplate.opsForValue().set("user1", user1); User userV1 = (User)redisTemplate.opsForValue().get("user1"); LOG.info("userV1====="+userV1.toString()); User user2 = new User(2,"xiaoyan",16); redisTemplate.opsForValue().set("user2", user2); User userV2 = (User)redisTemplate.opsForValue().get("user2"); LOG.info("user2====="+userV2.toString()); User user3 = new User(3,"xiaoxiaoping",18); redisTemplate.opsForValue().set("user3", user3); User userV3 = (User)redisTemplate.opsForValue().get("user3"); LOG.info("userV3====="+userV3.toString()); } }
測試結果:
能夠看出,是以字節方式存儲的。