学习笔记:cache 和spring cache 技术2--spring cache 的基本使用 、spring-Redis集成

1,353 阅读7分钟

title: 学习笔记:cache 和spring cache 技术 2 author: Eric liu tags: [] categories:

  • hexo

spring cache 的基本使用

http://jinnianshilongnian.iteye.com/blog/2001040

Spring支持Cache,是针对方法。主要的实现方式为:当我们在调用一个缓存方法时会把该方法参数和返回结果作为一个键值对存放在缓存中,等到下次利用同样的参数来调用该方法时将不再执行该方法,而是直接从缓存中获取结果进行返回。注解既可以放在方法上也可以放在类上,放到类上时,表示该类的所有方法都支持缓存。

原理:

和 spring 的事务管理类似,spring cache 的关键原理就是 spring AOP,通过 spring AOP,其实现了在方法调用前、调用后获取方法的入参和返回值,进而实现了缓存的逻辑。

注意:

当一个支持缓存的方法在对象内部被调用时是不会触发缓存功能的
@Cache*注解要加在 public 方法上

注解最好加在实现类而不是接口的方法上

因此需要注意的点为:

1、键如何选取
2、缓存的更新策略
主要会使用到的注解有两个@Cacheable 与@CacheEvict

1.基本使用

如1中所说,配置spring 配置文件,创建cacheManger,在下面配置cache片,在方法上标注@cacheable(value= cache片的名) ,参数为入参key,即可使用。
        <bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">  
            <property name="caches">  
                <set>  
                    <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">  
                        <property name="name" value="default"/>  
                    </bean>  
                    <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">  
                        <property name="name" value="accountCache"/>  
                    </bean>  
                </set>  
            </property>  
        </bean>   
    </beans>  

(2)基本注解(https://www.cnblogs.com/imyijie/p/6518547.html)

三个主要的注解 Cacheable (最常用的注解,用于标注需要缓存方法):

@Cacheable

执行后Spring Cache将缓存其返回结果
主要的属性有三个:value、key和condition
value用来指定Cache的名称

@Cacheable("cache1")//Cache是发生在cache1上的
   public User find(Integer id) {}
   @Cacheable({"cache1", "cache2"})//Cache是发生在cache1和cache2上的
   public User find(Integer id) {}

key属性自定义key

key属性是用来指定Spring缓存方法的返回结果时对应的key的。该属性支持SpringEL表达式。当我们没有指定该属性时,Spring将使用默认策略生成key。
下面分析自定义的策略

使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”。
@Cacheable(value="users", key="#id")
 @Cacheable(value="users", key="#p0")
@Cacheable(value="users", key="#user.id")
   public User find(User user) {
      return null;
   }
   @Cacheable(value="users", key="#p0.id")
   public User find(User user) {
      return null;
   }
@Cacheable(value = "HOTEL", key = "#p0+'_'+#p1")
   public User find(String a,String b) {
   }

默认键值生成策略:

1 如果方法没有参数,则使用0作为key。 2 如果只有一个参数的话则使用该参数作为key。 3 如果参数多余一个的话则使用所有参数的hashCode作为key。

condition属性指定发生的条件
将满足条件的结果缓存
@Cacheable(value={"users"}, key="#user.id", condition="#user.id%2==0")

可以自己指定

默认KeyGenerator

@CacheEvict

是用来标注在需要清除缓存元素的方法或类上的。当标记在一个类上时表示其中所有的方法的执行都会触发缓存的清除操作。

@CacheEvict(value="accountCache",key="#account.getName()")// 清空accountCache 缓存 
public void updateAccount(Account account) {
     updateDB(account);
}
 
@CacheEvict(value="accountCache",allEntries=true)// 清空accountCache 缓存
public void reload() {
     reloadAll()
}
 
@Cacheable(value="accountCache",condition="#userName.length() <=4")// 缓存名叫 accountCache
public Account getAccountByName(String userName) {
 // 方法内部实现不考虑缓存逻辑,直接实现业务
 return getFromDB(userName);
}

key表示需要清除的是哪个key,如未指定则会使用默认策略生成的key;condition表示清除操作发生的条件。

allEntries:删除命名空间下的所有缓存,默认false,key = "#name" 删除指定id的缓存

通过@schedule的方式每10秒清空缓存

beforeInvocation属性

清除操作默认是在对应方法成功执行之后触发的,即方法如果因为抛出异常而未能成功返回时也不会触发清除操作。使用beforeInvocation可以改变触发清除操作的时间,当我们指定该属性值为true时,Spring会在调用该方法之前清除缓存中的指定元素。

@CachePut

CachePut(用于仅存放缓存)

区别:cachePut 是放入缓存的,每次都执行,如果有condition执行后判断condition是否放入缓存

    @CachePut(value = "models", key = "#name")
    public TestModel saveModel(String name, String address) {
        return new TestModel(name, address);
    }
condition/unless

其他:


(1)keyGenerator: 实现 org.springframework.cache.interceptor.KeyGenerator 接口的类bean,用于统一自定义生成key
(2)cacheManager: 用于选择使用哪个cacheManager
(3)cacheResolver: 实现 org.springframework.cache.interceptor.CacheResolver 接口的类bean,用于自定义如何处理缓存
(4)sync: 是否同步  从相同key加载值 的方法,缓存消失后多线程访问同一个key的方法,只有一个线程实际调用,其他线程等待缓存结果

区别:

@CachePut也可以声明一个方法支持缓存功能。与@Cacheable不同的是使用@CachePut标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中。

@Caching

有时候我们可能组合多个Cache注解使用;比如用户新增成功后,我们要添加id-->user;username--->user;email--->user的缓存;此时就需要@Caching组合多个注解标签了。

    @Caching(  
            put = {  
                    @CachePut(value = "user", key = "#user.id"),  
                    @CachePut(value = "user", key = "#user.username"),  
                    @CachePut(value = "user", key = "#user.email")  
            }  
    )  
    public User save(User user) {  
二:
   @Caching(cacheable = @Cacheable("users"), evict = { @CacheEvict("cache2"),
         @CacheEvict(value = "cache3", allEntries = true) })
   public User find(Integer id) {
      returnnull;
   }

使用配置:

(1)cacheManager, 默认使用concurrentHashMap做缓存

<!--启用缓存注解功能-->
<cache:annotation-driven/>
<!--     指定key的生成
<cache:annotation-driven key-generator="XXXXXXXXX"/>
-->
<!--spring自己的基于java.util.concurrent.ConcurrentHashMap实现的缓存管理器(该功能是从Spring3.1开始提供)-->
<bean id="cacheManager" class="org.springframework.cache.support.SimpleCacheManager">
   <property name="caches">
     <set>
       <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
          <property name="name" value="user"/>
       </bean>
       <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
          <property name="name" value="另一个缓存的名称"/>
       </bean>
    </set>
 </property>
</bean>

(2)也可以用guava cache 做缓存

<cache:annotation-driven />
<bean id="cacheManager" class="org.springframework.cache.guava.GuavaCacheManager">
    <property name="cacheSpecification" value="concurrencyLevel=4,expireAfterAccess=3s,expireAfterWrite=100s" />
    <property name="cacheNames">
        <list>
            <value>default</value>
        </list>
    </property>
</bean>

配置说明:

<bean id="cacheManager" class="org.springframework.cache.support.CompositeCacheManager">
        <property name="cacheManagers">
            <array>
                <!-- 优先看看是不是本地缓存-->
                <ref bean="guavaCacheManager"/>
                <ref bean="sedisCacheManager"/>
            </array>
        </property>
</bean>

调用cacheManager.getCache(cacheName) 时,会先从第一个cacheManager中查找有没有cacheName的cache,如果没有接着查找第二个,如果最后找不到

<bean id="guavaCacheManager" class="com.xxxx.guava.GuavaCacheManager">
    <property name="caches">
      <list>
        <bean class="com.xxxx.cache.guava.GuavaCacheFactoryBean" name="w-index">
         <property name="spec" value="maximumSize=500,expireAfterWrite=1h,recordStats"/>
         <property name="allowNullValues" value="false"/>
 	    </bean>
     </list>
   </property>
</bean>

除了GuavaCacheManager之外,其他Cache都支持Spring事务的,即如果事务回滚了,Cache的数据也会移除掉。

如 ,ehCacheManager

<bean id="ehcacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">  
		<property name="configLocation" value="classpath:ehcache.xml"/>  
 </bean>  
  <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">  
        <property name="cacheManager" ref="ehcacheManager"/>  
        <property name="transactionAware" value="true"/>  
 </bean>  

这样注入configLocation,会自动根据路径从classpath下找,比编码方式简单

缺点:

1、他的cache功能是基于aop的,所以当内部调用方法的时候就不会调用cache方法。

http://www.iteye.com/topic/1123669 spring cache 实现二级缓存

核心问题

(1)失效时间

spring cache 注解本身不提供,需要在缓存manager 上配置,如guava Cache 可以配置

<bean id="guavaCacheManager" class="com.xxxxx.dapp.guava.GuavaCacheManager">
    <property name="caches">
        <list>
            <bean class="com.xxxxx.vc.cache.guava.GuavaCacheFactoryBean" name="w-index">
                <property name="spec" value="maximumSize=500,expireAfterWrite=1h,recordStats"/>
                <property name="allowNullValues" value="false"/>
            </bean>
        </list>
    </property>
</bean>

本地缓存和分布式缓存:

项目中 配置了本地缓存和分布式缓存,优先查看本地缓存

(2)缓存失效前主动刷新缓存

一般缓存失效后,会有一些请求会打到后端的数据库上,这段时间的访问性能肯定是比有缓存的情况要差很多。所以期望在缓存即将过期的某一时间点后台主动去更新缓存以确保前端请求的缓存命中率
    Srping 4.3提供了一个sync参数。是当缓存失效后,为了避免多个请求打到数据库,系统做了一个并发控制优化,同时只有一个线程会去数据库取数据其它线程会被阻塞。