Spring整合Redis用作緩存-註解方式

1.前言

  redis是一個key-value存儲系統。和Memcached類似,它支持存儲的value類型相對更多,包括string(字符串)、list(鏈表)、set(集合)、zset(sorted set –有序集合)和hash(哈希類型)。這些數據類型都支持push/pop、add/remove及取交集並集和差集及更豐富的操作,而且這些操作都是原子性的。在此基礎上,redis支持各種不同方式的排序。與memcached一樣,爲了保證效率,數據都是緩存在內存中。區別的是redis會週期性的把更新的數據寫入磁盤或者把修改操作寫入追加的記錄文件,並且在此基礎上實現了master-slave(主從)同步。(以上來自百度百科)
  之前寫過spring整合Ehcache用作緩存的,這裏區別一下。使用redis的時候,需要獨立安裝redis。屬於獨立運行的程序。所以在寫單元測試的時候,第二次運行,照樣可以獲取到緩存中的值。但是ehcache是和java程序綁定一起的,有着相同的生命週期,所以第一次單元測試存入緩存,第二次運行是取不到的。
整合之前需要自行安裝Redis。  
  

2.maven配置

<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
  <modelVersion>4.0.0</modelVersion>
  <groupId>com.test</groupId>
  <artifactId>spring-redis</artifactId>
   <packaging>war</packaging>
  <version>0.0.1-SNAPSHOT</version>
    <properties>
        <spring.version>4.3.5.RELEASE</spring.version>
    </properties>

     <dependencies>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-core</artifactId>
            <version>${spring.version}</version>
        </dependency>
        <dependency>  
            <groupId>org.springframework</groupId>  
            <artifactId>spring-jdbc</artifactId>  
            <version>${spring.version}</version>  
        </dependency> 

        <dependency>
            <groupId>org.springframework.data</groupId>
            <artifactId>spring-data-redis</artifactId>
            <version>1.8.0.RELEASE</version>
        </dependency>
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
            <version>2.9.0</version>
        </dependency>

        <dependency>
            <groupId>org.slf4j</groupId>
            <artifactId>slf4j-log4j12</artifactId>
            <version>1.7.18</version>
        </dependency>

        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.17</version>
        </dependency>

        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-test</artifactId>
            <version>${spring.version}</version>
            <scope>test</scope>
        </dependency>
         <!-- mybatis核心包 -->   
        <dependency>
            <groupId>org.mybatis</groupId>
            <artifactId>mybatis</artifactId>
            <version>3.2.8</version>
        </dependency> 
        <!-- mybatis/spring包 -->  
        <dependency>  
            <groupId>org.mybatis</groupId>  
            <artifactId>mybatis-spring</artifactId>  
            <version>1.2.2</version>  
        </dependency>  

        <!-- 導入Mysql數據庫鏈接jar包 -->  
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <version>5.1.27</version>
            <type>jar</type>
            <scope>compile</scope>
        </dependency>

        <!-- 導入dbcp的jar包,用來在applicationContext.xml中配置數據庫 -->  
        <dependency>  
            <groupId>commons-dbcp</groupId>  
            <artifactId>commons-dbcp</artifactId>  
            <version>1.2.2</version>  
        </dependency>

        <dependency>
            <groupId>junit</groupId>
            <artifactId>junit</artifactId>
            <version>4.12</version>
            <scope>test</scope>
        </dependency>

     </dependencies>

  <build/>
</project>

3.配置緩存

package com.test.config;

import java.lang.reflect.Method;

import org.apache.log4j.Logger;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.connection.jedis.JedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;

@Configuration
@EnableCaching
public class CacheConfig{

      private static final Logger log = Logger.getLogger(CacheConfig.class);

      @Bean
      public JedisConnectionFactory redisConnectionFactory() {
        JedisConnectionFactory redisConnectionFactory = new JedisConnectionFactory();
        redisConnectionFactory.setHostName("127.0.0.1");
        redisConnectionFactory.setPort(6379);
        return redisConnectionFactory;
      }

      @Bean
      public RedisTemplate<String, String> redisTemplate(RedisConnectionFactory cf) {
        RedisTemplate<String, String> redisTemplate = new RedisTemplate<String, String>();
        redisTemplate.setConnectionFactory(cf);
        return redisTemplate;
      }

      @Bean
      public CacheManager cacheManager(RedisTemplate redisTemplate) {
        RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
        //這裏可以設置一個默認的過期時間
        cacheManager.setDefaultExpiration(300);
        return cacheManager;
      }

      @Bean
      public KeyGenerator customKeyGenerator() {
        return new KeyGenerator() {
          @Override
          public Object generate(Object o, Method method, Object... params) {
            StringBuilder sb = new StringBuilder();
            sb.append(o.getClass().getName());
            sb.append(method.getName());
            for (Object obj : params) {
              sb.append(obj.toString());
            }
            return sb.toString();
          }
        };
      }
}

以上配置中,JedisConnectionFactory的時候只配了地址和端口號,因爲我是本地測試,且沒有設置密碼。如果是遠程地址,可以直接修改上面的地址或端口號,還可以添加密碼。設置cacheManager的時候設置了一個緩存默認的過期時間,單位是秒。
customKeyGenerator()這個方法可以不寫。是自定義生key的,默認也可以。上面的生成方式只是比默認的生成方式多了一個類名。除了KeyGenerator,其他的bean都是必須配置的。這裏還省略了JedisPoolConfig的配置,可以設置最大連接數、最大等待毫秒數、逐出連接的最小空閒時間等。有需要的可以直接配置一個JedisPoolConfig。

4.service 實現

一.接口:

package com.test.service;

import com.test.model.User;

public interface UserService {
    public User findUserById(int id);
    public User addUser(User user);
    public User updateUser(User user);
    public int countUser();

    public String getTimestamp(String param);
}

二.接口實現類

package com.test.service.impl;

import javax.annotation.Resource;

import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import com.test.dao.UserDao;
import com.test.model.User;
import com.test.service.UserService;

@Service
public class UserServiceImpl implements UserService{

    @Resource
    private UserDao userDao;

    @Cacheable(cacheNames="user")
    @Override
    public User findUserById(int id) {
        return userDao.findById(id);
    }

    @CachePut(cacheNames="user",key="#result.id")
    @CacheEvict(cacheNames="countUser" ,allEntries = true)
    @Override
    public User addUser(User user) {
        userDao.add(user);
        return user;
    }

    @Override
    @CachePut(cacheNames="user",key="#user.id")
    public User updateUser(User user) {
        userDao.update(user);
        return user;
    }

    @Override
    @Cacheable(cacheNames="countUser")
    public int countUser() {
        return userDao.count();
    }

    @Override
    @Cacheable(cacheNames="time")
    public String getTimestamp(String param) {
        Long timestamp = System.currentTimeMillis();
        return timestamp.toString();
    }

}

以上代碼中,
findUserById()上有註解@Cacheable(cacheNames=”user”),表示將返回值存入緩存user。該註解沒有指定key,因爲findUserById()方法中只有一個參數,所以該方法的默認key就是id。
addUser()方法有兩個註解,@CachePut(cacheNames=”user”,key=”#result.id”)表示,執行前不會去檢查緩存中是否存在之前執行過的結果,而是每次都會執行該方法,並將執行結果以鍵值對的形式存入指定的緩存中。可以是添加用戶之後直接加入緩存,並且指定key是返回值的id。則下次使用該id去查詢user的時候可以直接從緩存中獲取。
而addUser()方法中的@CacheEvict(cacheNames=”countUser” ,allEntries = true)註解表示刪除countUser中的緩存,allEntries = true表示清除countUser中所有的元素。對應於添加用戶之後,將用戶數量的緩存刪除。以免造成查詢數據出錯。
updateUser() 中的@CachePut(cacheNames=”user”,key=”#user.id”)將更新後的user直接存入緩存。key是用戶的id。所以根據id查詢該用戶時,直接從緩存中獲取。
countUser() ,@Cacheable(cacheNames=”countUser”)將查詢出來的數量存入緩存countUser。所以上面addUser方法中@CacheEvict註解的cacheNames也是countUser。

5.測試代碼

package com.service;

import javax.annotation.Resource;

import org.junit.Test;

import com.base.SpringTestCase;
import com.test.model.User;
import com.test.service.UserService;

public class TestService extends SpringTestCase {

    @Resource
    private UserService userService;

    @Test
    public void addUser(){
        User user = new User();
        user.setName("王五");
        userService.addUser(user);
    }

    @Test
    public void testQuery() throws InterruptedException{
        System.out.println("第一次調用"+userService.findUserById(1));
        System.out.println("第二次調用"+userService.findUserById(1));

    }

    @Test
    public void testUpdate(){
        User user = userService.findUserById(1);
        System.out.println( "第一次調用"+user);
        user.setName(user.getName()+"1");
        System.out.println("更新");
        userService.updateUser(user);
        System.out.println("第二次調用"+userService.findUserById(1));
    }

    @Test
    public void testCount(){
        System.out.println("第一次查詢數量:"+userService.countUser());
        addUser();
        System.out.println("第二次查詢數量:"+userService.countUser());
    }

    @Test  
    public void getTimestampTest() throws InterruptedException{  
        System.out.println("第一次調用:" + userService.getTimestamp("param"));
        Thread.sleep(2000);
        System.out.println("2秒之後調用:" + userService.getTimestamp("param"));
        Thread.sleep(10000);
        System.out.println("12秒之後調用:" + userService.getTimestamp("param"));

    } 

}

添加用戶後,測試testQuery()指定一個id,運行之後,第一次查詢了數據庫,第二次查詢沒有經過數據庫,直接從緩存中獲取。
image
注意:這裏第一次查詢的時候訪問了數據庫,因爲我上面設置了緩存失效時間是300秒,所以在300秒內再次運行,這兩次的結果都不會經過數據庫。

testUpdate()測試更新user信息後第二次查詢是更新後的內容。因爲在update方法上加了@CachePut(cacheNames=”user”,key=”#user.id”)測試結果如下:
image

testCount()測試添加用戶後,再查詢時,數據從數據庫中讀取。執行結果圖太大就不貼了。

測試getTimestampTest()方法時需要將上面cacheManager.setDefaultExpiration(10);設置成10秒,測試緩存失效時間。結果第一次 和第二次調用的時間是一樣的。12秒後查詢的時間和上面查詢的不同。

還有其他的配置就不展示了,直接把項目傳上去,大家可自行下載。
http://download.csdn.net/detail/poorcoder_/9768548
轉載請註明出處:http://blog.csdn.net/poorcoder_/article/details/59541710

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章