spring 配置 cache 缓存使用 ConcurrentMap EhCache Redis

spring cache 总结

    作用:  保证数据时效性的前提下, 提高查询效率; 减少查询数据库的次数, 减轻数据库压力;

    原理 基于Proxy/AspectJ动态代理技术的AOP思想(面向切面编程), 在不改变目标方法的前提下在其切面进行缓存的增删改查(在服务方法执行前查询缓存, 在服务方法执行后添加缓存, 或者清除缓存);

    用法: 

        1. spring cache实现有基于XML/注解实现AOP;

        2. CacheManager负责对缓存的增删改查, CacheManager的缓存的介质可配置, 如: ConcurrentMap/EhCache/Redis等;

spring cache 实例

1. 导入jar包
<!-- redis架包 -->
<dependency>
	<groupId>org.springframework.data</groupId>
	<artifactId>spring-data-redis</artifactId>
	<version>1.6.2.RELEASE</version>
</dependency>
<dependency>
	<groupId>redis.clients</groupId>
	<artifactId>jedis</artifactId>
	<version>2.9.0</version>
</dependency>

<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-core -->
<dependency>
	<groupId>com.fasterxml.jackson.core</groupId>
	<artifactId>jackson-core</artifactId>
	<version>2.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-databind -->
<dependency>
	<groupId>com.fasterxml.jackson.core</groupId>
	<artifactId>jackson-databind</artifactId>
	<version>2.9.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/com.fasterxml.jackson.core/jackson-annotations -->
<dependency>
	<groupId>com.fasterxml.jackson.core</groupId>
	<artifactId>jackson-annotations</artifactId>
	<version>2.9.2</version>
</dependency>

<!-- https://mvnrepository.com/artifact/commons-logging/commons-logging -->
<dependency>
	<groupId>commons-logging</groupId>
	<artifactId>commons-logging</artifactId>
	<version>1.2</version>
</dependency>
<!-- https://mvnrepository.com/artifact/log4j/log4j -->
<dependency>
	<groupId>log4j</groupId>
	<artifactId>log4j</artifactId>
	<version>1.2.17</version>
</dependency>
2. 配置spring-cache.xml
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:cache="http://www.springframework.org/schema/cache"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans.xsd
  http://www.springframework.org/schema/cache
  http://www.springframework.org/schema/cache/spring-cache.xsd">

    <!-- 开启缓存注解: 使用 @Cacheable, @CachePut, @CacheEvict, @Caching, @CacheConfig 等注解引入缓存 -->
    <cache:annotation-driven/>

    <!-- generic cache manager -->
    <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
        <property name="caches">
            <set>
                <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="default"/>
                <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean" p:name="accountCache"/>
            </set>
        </property>
    </bean>
	
    <!-- 注入实体类 -->
    <bean id="accountServiceBean" class="com.csdn.learn.springcache.AccountService"/>
   
</beans>
3. 使用及测试
package com.csdn.learn.springcache;

import com.csdn.learn.annocache.Account;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;

/**
 * 使用@Cacheable, @CachePut, @CacheEvict, @CacheConfig spring通过AOP对方法进行缓存管理
 * @author liudong
 * @date 2018-04-27
 */
public class AccountService {

    /**
     * 使用了一个缓存名叫 accountCache
     * key condition ,前面的 # 号代表这是一个 SpEL 表达式
     * @param userName
     * @return
     */
    @Cacheable(value = "accountCache", key = "#userName", condition = "#userName.length() <= 4")
    public Account getAccountByName(String userName) {
        // 方法内部实现不考虑缓存逻辑,直接实现业务
        System.out.println("real querying db..." + acctName + "+" + password);
        return new Account(acctName);
    }

    /**
     * 更新 accountCache 缓存
     * @param account dd
     */
    @CachePut(value = "accountCache", key = "#account.getName()")
    public void updateAccount(Account account) {
        System.out.println("real update db..." + account.getName());
    }

    /**
     * reload 清空 accountCache 缓存
     */
    @CacheEvict(value = "accountCache", allEntries = true)
    public void reload() {
    }

}
package com.csdn.learn.annocache;

import java.io.Serializable;

/**
 * 首先定义一个实体类:账号类,具备基本的 id 和 name 属性,且具备 getter 和 setter 方法
 * @author liudong
 * @date 2018-04-27
 */
public class Account implements Serializable {
    private static final long serialVersionUID = 1L;

    private int id;
    private String name;
    private String password;

    public Account() {
        super();
    }

    public String getPassword() {
        return password;
    }
    public void setPassword(String password) {
        this.password = password;
    }
    public Account(String name) {
        this.name = name;
    }
    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;
    }

    @Override
    public String toString() {
        return "Account{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", password='" + password + '\'' +
                '}';
    }
}
package com.csdn.learn.rediscacheanno;

import com.csdn.learn.annocache.Account;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;
import org.springframework.data.redis.core.RedisTemplate;

public class Main {

    public static void main(String[] args) {
        // 加载 spring 配置文件
        ApplicationContext context = new ClassPathXmlApplicationContext("spring-cache.xml");

        AccountService s = (AccountService) context.getBean("accountServiceBean");
		
        // 第一次查询,应该走数据库
        System.out.print("first query...");
        s.getAccountByName("somebody");
        // 第二次查询,应该不查数据库,直接返回缓存的值
        System.out.print("second query...");
        s.getAccountByName("somebody");
        System.out.println();

	// TODO 自行测试@CachePut及@CacheEvict
		
	// 测试redisTemplate
	RedisTemplate redisTemplate = (RedisTemplate) context.getBean("redisTemplate");
	// redis中存取字符串
        redisTemplate.opsForValue().set("redis2", "ceshiRedis222");
        System.out.println(redisTemplate.opsForValue().get("redis2"));
	// redis中存取实体类
        redisTemplate.opsForValue().set("account", new Account("ceshi 123"));
        System.out.println(redisTemplate.opsForValue().get("account"));
    }
}

内存式普通缓存使用大功告成, 由于spring整合Redis的做缓存时配置最难搞定, 所以仅介绍配置

方式一. spring整合Redis的xml配置
#============================#
#==== Redis settings ====#
#============================#
#redis 服务器 IP
redis.host=127.0.0.1
#redis 服务器端口
redis.port=6379
#redis 密码
redis.pass=
#redis 支持16个数据库(相当于不同用户)可以使不同的应用程序数据彼此分开同时又存储在相同的实例上
redis.dbIndex=1
#redis 缓存数据过期时间单位秒
redis.expiration=3000
#控制一个 pool 最多有多少个状态为 idle 的jedis实例
redis.maxIdle=300
#控制一个 pool 可分配多少个jedis实例
redis.maxActive=600
#当borrow一个jedis实例时,最大的等待时间,如果超过等待时间,则直接抛出JedisConnectionException;
redis.maxWait=1000
#在borrow一个jedis实例时,是否提前进行alidate操作;如果为true,则得到的jedis实例均是可用的;
redis.testOnBorrow=true
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:context="http://www.springframework.org/schema/context"
       xmlns:cache="http://www.springframework.org/schema/cache"
       xmlns:p="http://www.springframework.org/schema/p"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans.xsd
  http://www.springframework.org/schema/context
  http://www.springframework.org/schema/context/spring-context.xsd
  http://www.springframework.org/schema/cache
  http://www.springframework.org/schema/cache/spring-cache.xsd">

    <!-- **************************************************redis********************************************************** -->
    <!-- 配置文件加载 -->
    <context:property-placeholder location="classpath:*.properties"/>
    <!-- 配置 JedisPoolConfig 实例 -->
    <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <property name="maxIdle" value="${redis.maxIdle}"/>
        <property name="maxTotal" value="${redis.maxActive}"/>
        <property name="maxWaitMillis" value="${redis.maxWait}"/>
        <property name="testOnBorrow" value="${redis.testOnBorrow}"/>
    </bean>

    <!-- 配置JedisConnectionFactory -->
    <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
        <property name="hostName" value="${redis.host}"/>
        <property name="port" value="${redis.port}"/>
        <property name="password" value="${redis.pass}"/>
        <property name="database" value="${redis.dbIndex}"/>
        <property name="poolConfig" ref="poolConfig"/>
    </bean>

    <!-- 配置RedisTemplate -->
    <!-- redis 序列化策略 ,通常情况下key值采用String序列化策略, -->
    <!-- 如果不指定序列化策略,StringRedisTemplate的key和value都将采用String序列化策略; -->
    <!-- 但是RedisTemplate的key和value都将采用JDK序列化 这样就会出现采用不同template保存的数据不能用同一个template删除的问题 -->
    <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
          p:connectionFactory-ref="jedisConnectionFactory"
          p:keySerializer-ref="stringRedisSerializer"
          p:hashKeySerializer-ref="stringRedisSerializer"
          p:valueSerializer-ref="genericJackson2JsonRedisSerializer"
          p:hashValueSerializer-ref="genericJackson2JsonRedisSerializer"
    />
    <!--JDK序列化,速度快但占用空间较大,对象必须实现java.io.Serializable接口 -->
    <bean id="jdkSerializationRedisSerializer" class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer" />
    <bean id="stringRedisSerializer" class="org.springframework.data.redis.serializer.StringRedisSerializer" />
    <!-- JSON序列化,空间占用小,无需指明但对象类型
            SerializationException:
                Could not read JSON: Cannot construct instance of `com.learn.cache.model.Account` (although at least one Creator exists):
                cannot deserialize from Object value (no delegate- or property-based Creator)
            解决办法: 给实体类com.learn.cache.model.Account添加默认的构造方法 public Account(){}
    -->
    <bean id="genericJackson2JsonRedisSerializer" class="org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer" />
    <!-- JSON序列化,空间占用小,需指明对象类型,不知道怎么在xml配置中注入参数类型为Class<?>的值,可在代码中指定 -->
    <!-- <bean id="jackson2JsonRedisSerializer" class="org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer"/> -->

    <!-- 配置RedisCacheManager -->
    <bean id="redisCacheManager" class="org.springframework.data.redis.cache.RedisCacheManager">
        <constructor-arg name="redisOperations" ref="redisTemplate"/>
        <property name="defaultExpiration" value="${redis.expiration}"/>
    </bean>

    <!-- 开启缓存注解: 使用 @Cacheable, @CachePut, @CacheEvict, @Caching, @CacheConfig 等注解引入缓存 -->
    <cache:annotation-driven/>
    <!-- 开启注解扫描 -->
    <!--<context:annotation-config />-->
    <context:component-scan base-package="com.learn"/>
    <bean id="testServiceBean" class="com.learn.cache.service.TestService"/>
    <bean id="accountServiceBean" class="com.learn.cache.service.AccountService"/>
</beans>
方式二. spring整合Redis的纯注解配置
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"
       xmlns:context="http://www.springframework.org/schema/context"
       xsi:schemaLocation="http://www.springframework.org/schema/beans
                        http://www.springframework.org/schema/beans/spring-beans-4.2.xsd
                        http://www.springframework.org/schema/context
                        http://www.springframework.org/schema/context/spring-context-4.2.xsd">

    <!-- 配置文件加载 -->
    <context:property-placeholder location="classpath:*.properties"/>
    <!-- redis连接池 -->
    <bean id="poolConfig" class="redis.clients.jedis.JedisPoolConfig">
        <property name="maxIdle" value="${redis.maxIdle}"/>
        <property name="maxWaitMillis" value="${redis.maxWait}"/>
        <property name="testOnBorrow" value="${redis.testOnBorrow}"/>
    </bean>
    <!-- 连接工厂 -->
    <bean id="redisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory"
          p:host-name="${redis.host}" p:port="${redis.port}" p:password="${redis.pass}" p:database="2" p:pool-config-ref="poolConfig"/>

    <!-- 开启注解扫描 -->
    <!--<context:annotation-config />-->
    <context:component-scan base-package="com.csdn.learn" />
    <bean id="accountServiceBean" class="com.csdn.learn.rediscacheanno.AccountService"/>
</beans>
package com.csdn.learn.rediscacheanno;

import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.annotation.EnableCaching;
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.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.StringRedisSerializer;

@Configuration
@EnableCaching
public class RedisConfig extends CachingConfigurerSupport {

    @Bean
    public CacheManager cacheManager(RedisTemplate redisTemplate) {
        RedisCacheManager cacheManager = new RedisCacheManager(redisTemplate);
//        cacheManager.setDefaultExpiration(3600);//默认缓存一小时
        return cacheManager;
    }

    /**
     * redisTemplate 序列化使用的jdkSerializeable, 存储二进制字节码, 所以自定义序列化类
     * @param redisConnectionFactory
     * @return
     */
    @Bean
    public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate<Object, Object> redisTemplate = new RedisTemplate<>();
        redisTemplate.setConnectionFactory(redisConnectionFactory);

        // 使用Jackson2JsonRedisSerialize 替换默认序列化
        Jackson2JsonRedisSerializer jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);

        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        objectMapper.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);

        jackson2JsonRedisSerializer.setObjectMapper(objectMapper);

        // 设置value的序列化规则和 key的序列化规则
        redisTemplate.setValueSerializer(jackson2JsonRedisSerializer);
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }
}
最后, SpringBoot整合Redis做缓存Cache技术配置非常简单, 可以试着玩一玩


猜你喜欢

转载自blog.csdn.net/hkk666123/article/details/80177284