SpringMVC使用 redis 实现缓存

简介

SpringMVC 中也可以将缓存标签和 redis 结合起来使用,其实此时缓存没有起作用,只是通过缓存的那几个注解来操作 redis 而已;SpringMVC 中整合 redis 比较麻烦的是注意版本冲突的问题,如下是官网有关于版本的要求

https://docs.spring.io/spring-data/data-redis/docs/current/reference/html/#requirements

实现步骤

  1. 在 pom.xml 中添加如下依赖,尤其要注意各个依赖的版本,基本上都是 maven 官网上的最新版
<properties>
    <!-- java 版本 -->
    <java.version>1.8</java.version>
    <!-- spring 版本 -->
    <spring.version>5.1.2.RELEASE</spring.version>
    <!-- jstl 版本 -->
    <jstl.version>1.2</jstl.version>
    <!-- standard 版本 -->
    <standard.version>1.2.5</standard.version>
</properties>
<dependencies>
    <dependency>
        <groupId>org.eclipse.jetty</groupId>
        <artifactId>jetty-servlets</artifactId>
        <version>9.1.0.M0</version>
    </dependency>
    <!-- begin SpringMVC -->
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aop</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-aspects</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-beans</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-context-support</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-core</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-expression</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-instrument</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jdbc</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-jms</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-messaging</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-orm</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-oxm</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-test</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-tx</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-web</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-websocket</artifactId>
        <version>${spring.version}</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-webmvc-portlet</artifactId>
        <version>4.3.20.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>org.springframework</groupId>
        <artifactId>spring-instrument-tomcat</artifactId>
        <version>4.3.20.RELEASE</version>
    </dependency>
    <!-- end SpringMVC -->
    <!-- begin jstl -->
    <dependency>
        <groupId>javax.servlet</groupId>
        <artifactId>jstl</artifactId>
        <version>${jstl.version}</version>
    </dependency>
    <dependency>
        <groupId>org.apache.taglibs</groupId>
        <artifactId>taglibs-standard-impl</artifactId>
        <version>${standard.version}</version>
    </dependency>
    <!-- end jstl -->
    <!-- config redis data and client jar-->
    <dependency>
        <groupId>org.springframework.data</groupId>
        <artifactId>spring-data-redis</artifactId>
        <version>2.1.2.RELEASE</version>
    </dependency>
    <dependency>
        <groupId>redis.clients</groupId>
        <artifactId>jedis</artifactId>
        <version>2.9.0</version>
    </dependency>
    <!-- 用于处理将对象转化为json格式的字符串 -->
    <dependency>
        <groupId>com.fasterxml.jackson.core</groupId>
        <artifactId>jackson-databind</artifactId>
        <version>2.9.7</version>
    </dependency>
</dependencies>
  1. 新建 CacheConfig.class,实现 Cache 接口,添加如下属性和方法
@Component
public class CacheConfig implements Cache {
    private StringRedisTemplate redisTemplate;
    private String name;

    public CacheConfig() {}

    public CacheConfig(StringRedisTemplate redisTemplate, String name) {
        this.redisTemplate = redisTemplate;
        this.name = name;
    }

    public StringRedisTemplate getStringRedisTemplate() {
        return redisTemplate;
    }

    public void setStringRedisTemplate(StringRedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getName() {
        return this.name;
    }

    public void clear() {
        redisTemplate.execute(new RedisCallback < String > () {
            public String doInRedis(RedisConnection connection) throws DataAccessException {
                connection.flushDb();
                return "ok";
            }
        });
    }

    public void evict(Object object) {
        final String key = (String) object;
        redisTemplate.execute(new RedisCallback < Long > () {
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                return connection.del(key.getBytes());
            }
        });
    }

    public ValueWrapper get(Object obj) {
        final String key = (String) obj;
        Object object = null;
        object = redisTemplate.execute(new RedisCallback < Object > () {
            public Object doInRedis(RedisConnection connection) throws DataAccessException {
                byte[] keys = key.getBytes();
                byte[] value = connection.get(keys);
                if (value == null) {
                    return null;
                }
                return toObject(value);
            }
        });
        return (object != null ? new SimpleValueWrapper(object) : null);
    }

    public Object getNativeCache() {
        return this.redisTemplate;
    }

    public void put(Object key, Object value) {
        final String keyf = (String) key;
        final Object valuef = value;
        final long liveTime = 86400;

        redisTemplate.execute(new RedisCallback < Long > () {
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                byte[] keyb = keyf.getBytes();
                byte[] valueb = toByteArray(valuef);
                connection.set(keyb, valueb);
                if (liveTime > 0) {
                    connection.expire(keyb, liveTime);
                }
                return 1 L;
            }
        });
    }

    public ValueWrapper putIfAbsent(Object key, Object value) {
        return null;
    }

    public < T > T get(Object object, Class < T > clz) {
        return null;
    }

    public < T > T get(Object object, Callable < T > callable) {
        return null;
    }

    private Object toObject(byte[] bytes) {
        ByteArrayInputStream bis = null;
        ObjectInputStream ois = null;
        Object obj = null;
        try {
            bis = new ByteArrayInputStream(bytes);
            ois = new ObjectInputStream(bis);
            obj = ois.readObject();
        } catch (IOException e) {
            e.printStackTrace();
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        } finally {
            closeIO(ois, bis);
        }
        return obj;
    }

    private byte[] toByteArray(Object obj) {
        ByteArrayOutputStream bos = null;
        ObjectOutputStream oos = null;
        byte[] bytes = null;
        try {
            bos = new ByteArrayOutputStream();
            oos = new ObjectOutputStream(bos);
            oos.writeObject(obj);
            oos.flush();
            bytes = bos.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
        } finally {
            closeIO(oos, bos);
        }
        return bytes;
    }

    public void closeIO(Closeable...ios) {
        try {
            for (Closeable io: ios) {
                if (null != io) {
                    io.close();
                }@Configuration
@EnableWebMvc
@EnableCaching
@ComponentScan(basePackages = "com.ibm.redis")
public class SpringConfig extends WebMvcConfigurationSupport {@
    Resource
    private AppSetting appSetting;

    @Bean
    public CacheManager cacheManager(StringRedisTemplate redisTemplate) {
        SimpleCacheManager cacheManager = new SimpleCacheManager();
        cacheManager
        // 注意此处设置的 cache name 后面将会用到
            .setCaches(Arrays.asList(new ConcurrentMapCache("myCache"), new CacheConfig(redisTemplate, "redis")));
        return cacheManager;
    }

    @Bean
    public KeyGenerator keyGenerator() {
        return new MyKeyGenerator();
    }
    // Jedis 连接池配置(对于缓存+redis配置来说, 该 bean 不是必需的, 只有单独使用 jedis 才用到)
    @Bean
    public GenericObjectPoolConfig poolConfig() {
        GenericObjectPoolConfig poolConfig = new GenericObjectPoolConfig();
        poolConfig.setMaxWaitMillis(appSetting.getMaxWaitMillis());
        poolConfig.setMaxTotal(appSetting.getMaxActive());
        poolConfig.setMaxIdle(appSetting.getMaxIdle());
        poolConfig.setMinIdle(appSetting.getMinIdle());
        return poolConfig;
    }

    // 创建 Jedis 连接池(对于缓存+redis配置来说, 该 bean 不是必需的, 只有单独使用 jedis 才用到)
    @Bean
    public JedisPool jedisPool(GenericObjectPoolConfig poolConfig) {
        JedisPool jedisPool = new JedisPool(poolConfig, appSetting.getHost(), appSetting.getPort(), 60, appSetting.getPass());
        return jedisPool;
    }

    // 创建 JedisConnectionFactory
    @Bean
    public JedisConnectionFactory redisConnectionFactory() {
        RedisStandaloneConfiguration redisStandaloneConfiguration = new RedisStandaloneConfiguration();
        redisStandaloneConfiguration.setHostName(appSetting.getHost());
        redisStandaloneConfiguration.setPassword(RedisPassword.of(appSetting.getPass()));
        redisStandaloneConfiguration.setPort(appSetting.getPort());
        return new JedisConnectionFactory(redisStandaloneConfiguration);
    }

    // 创建 StringRedisTemplate
    @Bean
    public StringRedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        StringRedisTemplate redisTemplate = new StringRedisTemplate(redisConnectionFactory);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setValueSerializer(new JdkSerializationRedisSerializer());
        return redisTemplate;
    }
}
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }
}
  1. 新建 SpringConfig.class 为 spring 的配置类,添加如下配置
在这里插入代码片

注意:上面的主键生成器 Bean 的实现如下

// 缓存键的生成策略
public class MyKeyGenerator implements KeyGenerator {
    public Object generate(Object target, Method method, Object ... params) {
        return target.getClass().getName() + method.getName();
    }
}
  1. Appsetting.class 类主要用于从 redis.properties 中绑定数据

Appsetting.class

@PropertySource(value="classpath:redis.properties")
@Component
public class AppSetting {
    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.password}")
    private String pass;
    @Value("${spring.redis.port}")
    private int port;
    @Value("${spring.redis.timeout}")
    private int timeout;
    @Value("${spring.redis.pool.max-active}")
    private int maxActive;
    @Value("${spring.redis.pool.max-idle}")
    private int maxIdle;
    @Value("${spring.redis.pool.min-idle}")
    private int minIdle;
    @Value("${spring.redis.pool.max-wait}")
    private long maxWaitMillis;
    ... ... 省略 getter 和 setter 方法
}

redis.properties

# REDIS
#redis jedis配置
# Redis数据库索引(默认为0)
spring.redis.database=0
# Redis服务器地址 (默认为127.0.0.1)
spring.redis.host=127.0.0.1
# Redis服务器连接端口
spring.redis.port=6379
# Redis服务器连接密码(默认为空)
spring.redis.password=dufu9137
# 连接池最大连接数(使用负值表示没有限制)
spring.redis.pool.max-active=200
# 连接池最大阻塞等待时间(使用负值表示没有限制)
spring.redis.pool.max-wait=-1
# 连接池中的最大空闲连接
spring.redis.pool.max-idle=8
# 连接池中的最小空闲连接
spring.redis.pool.min-idle=0
# 连接超时时间(毫秒)
spring.redis.timeout=0
#spring-session 使用
spring.session.store-type=none
  1. 在 RedisService.class 中使用
@Service
// 此处的 cache name 和第 3 步中声明 CacheManager 时添加的 name 要对应上
@CacheConfig(cacheNames = "redis", keyGenerator = "keyGenerator")
public class RedisService {
    @Resource
    private CacheManager cacheManager;

    @Cacheable
    public String get1() {
        System.out.println(new Date() + "--> No value from cache");
        return "Ramos";
    }

    @Cacheable
    public User get2() {
        System.out.println(new Date() + "--> No value from cache");
        return new User(1, "dufu");
    }
}

运行效果

  1. 操作字符串
    在这里插入图片描述

查看 redis 客户端,发现已经有数据存入到库中
在这里插入图片描述
2. 操作对象
在这里插入图片描述
查看 redis 客户端,发现已经有数据存入到库中
在这里插入图片描述
此时,如果我们反复刷新页面,RedisService.class 中的 get1() 和 get2() 方法也只会打印出一次:No value from cache,说明缓存(redis)已经生效

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值