`

spring cache使用(转)

 
阅读更多

 http://www.cnblogs.com/rollenholt/p/4202631.htm

基本原理

一句话介绍就是Spring AOP的动态代理技术。 如果读者对Spring AOP不熟悉的话,可以去看看官方文档

扩展性

直到现在,我们已经学会了如何使用开箱即用的 spring cache,这基本能够满足一般应用对缓存的需求。

但现实总是很复杂,当你的用户量上去或者性能跟不上,总需要进行扩展,这个时候你或许对其提供的内存缓存不满意了,因为其不支持高可用性,也不具备持久化数据能力,这个时候,你就需要自定义你的缓存方案了。

还好,spring 也想到了这一点。我们先不考虑如何持久化缓存,毕竟这种第三方的实现方案很多。

我们要考虑的是,怎么利用 spring 提供的扩展点实现我们自己的缓存,且在不改原来已有代码的情况下进行扩展。

首先,我们需要提供一个 CacheManager 接口的实现,这个接口告诉 spring 有哪些 cache 实例,spring 会根据 cache 的名字查找 cache 的实例。另外还需要自己实现 Cache 接口,Cache 接口负责实际的缓存逻辑,例如增加键值对、存储、查询和清空等。

利用 Cache 接口,我们可以对接任何第三方的缓存系统,例如 EHCacheOSCache,甚至一些内存数据库例如 memcache 或者 redis 等。下面我举一个简单的例子说明如何做。

import java.util.Collection; 

 import org.springframework.cache.support.AbstractCacheManager; 

 public class MyCacheManager extends AbstractCacheManager { 
   private Collection<? extends MyCache> caches; 
  
   /** 
   * Specify the collection of Cache instances to use for this CacheManager. 
   */ 
   public void setCaches(Collection<? extends MyCache> caches) { 
     this.caches = caches; 
   } 

   @Override 
   protected Collection<? extends MyCache> loadCaches() { 
     return this.caches; 
   } 

 }

上面的自定义的 CacheManager 实际继承了 spring 内置的 AbstractCacheManager,实际上仅仅管理 MyCache 类的实例。

下面是MyCache的定义:

import java.util.HashMap; 
 import java.util.Map; 

 import org.springframework.cache.Cache; 
 import org.springframework.cache.support.SimpleValueWrapper; 

 public class MyCache implements Cache { 
   private String name; 
   private Map<String,Account> store = new HashMap<String,Account>();; 
  
   public MyCache() { 
   } 
  
   public MyCache(String name) { 
     this.name = name; 
   } 
  
   @Override 
   public String getName() { 
     return name; 
   } 
  
   public void setName(String name) { 
     this.name = name; 
   } 

   @Override 
   public Object getNativeCache() { 
     return store; 
   } 

   @Override 
   public ValueWrapper get(Object key) { 
     ValueWrapper result = null; 
     Account thevalue = store.get(key); 
     if(thevalue!=null) { 
       thevalue.setPassword("from mycache:"+name); 
       result = new SimpleValueWrapper(thevalue); 
     } 
     return result; 
   } 

   @Override 
   public void put(Object key, Object value) { 
     Account thevalue = (Account)value; 
     store.put((String)key, thevalue); 
   } 

   @Override 
   public void evict(Object key) { 
   } 

   @Override 
   public void clear() { 
   } 
 }
 

上面的自定义缓存只实现了很简单的逻辑,但这是我们自己做的,也很令人激动是不是,主要看 get 和 put 方法,其中的 get 方法留了一个后门,即所有的从缓存查询返回的对象都将其 password 字段设置为一个特殊的值,这样我们等下就能演示“我们的缓存确实在起作用!”了。

这还不够,spring 还不知道我们写了这些东西,需要通过 spring*.xml 配置文件告诉它

 <cache:annotation-driven /> 

 <bean id="cacheManager" class="com.rollenholt.spring.cache.MyCacheManager">
     <property name="caches"> 
       <set> 
         <bean 
           class="com.rollenholt.spring.cache.MyCache"
           p:name="accountCache" /> 
       </set> 
     </property> 
   </bean> 
   

接下来我们来编写测试代码:

Account account = accountService.getAccountByName("someone"); 
logger.info("passwd={}", account.getPassword()); 
account = accountService.getAccountByName("someone"); 
logger.info("passwd={}", account.getPassword()); 

上面的测试代码主要是先调用 getAccountByName 进行一次查询,这会调用数据库查询,然后缓存到 mycache 中,然后我打印密码,应该是空的;下面我再次查询 someone 的账号,这个时候会从 mycache 中返回缓存的实例,记得上面的后门么?我们修改了密码,所以这个时候打印的密码应该是一个特殊的值

注意和限制

基于 proxy 的 spring aop 带来的内部调用问题

上面介绍过 spring cache 的原理,即它是基于动态生成的 proxy 代理机制来对方法的调用进行切面,这里关键点是对象的引用问题.

如果对象的方法是内部调用(即 this 引用)而不是外部引用,则会导致 proxy 失效,那么我们的切面就失效,也就是说上面定义的各种注释包括 @Cacheable、@CachePut 和 @CacheEvict 都会失效,我们来演示一下。

public Account getAccountByName2(String accountName) { 
   return this.getAccountByName(accountName); 
 } 

 @Cacheable(value="accountCache")// 使用了一个缓存名叫 accountCache 
 public Account getAccountByName(String accountName) { 
   // 方法内部实现不考虑缓存逻辑,直接实现业务
   return getFromDB(accountName); 
 }
 

上面我们定义了一个新的方法 getAccountByName2,其自身调用了 getAccountByName 方法,这个时候,发生的是内部调用(this),所以没有走 proxy,导致 spring cache 失效

要避免这个问题,就是要避免对缓存方法的内部调用,或者避免使用基于 proxy 的 AOP 模式,可以使用基于 aspectJ 的 AOP 模式来解决这个问题。

@CacheEvict 的可靠性问题

我们看到,@CacheEvict 注释有一个属性 beforeInvocation,缺省为 false,即缺省情况下,都是在实际的方法执行完成后,才对缓存进行清空操作。期间如果执行方法出现异常,则会导致缓存清空不被执行。我们演示一下

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

我们的测试代码如下:

   accountService.getAccountByName("someone"); 
   accountService.getAccountByName("someone"); 
   try { 
     accountService.reload(); 
   } catch (Exception e) { 
    //...
   } 
   accountService.getAccountByName("someone"); 
   

注意上面的代码,我们在 reload 的时候抛出了运行期异常,这会导致清空缓存失败。上面的测试代码先查询了两次,然后 reload,然后再查询一次,结果应该是只有第一次查询走了数据库,其他两次查询都从缓存,第三次也走缓存因为 reload 失败了。

那么我们如何避免这个问题呢?我们可以用 @CacheEvict 注释提供的 beforeInvocation 属性,将其设置为 true,这样,在方法执行前我们的缓存就被清空了。可以确保缓存被清空。

非 public 方法问题

和内部调用问题类似,非 public 方法如果想实现基于注释的缓存,必须采用基于 AspectJ 的 AOP 机制

Dummy CacheManager 的配置和作用

有的时候,我们在代码迁移、调试或者部署的时候,恰好没有 cache 容器,比如 memcache 还不具备条件,h2db 还没有装好等,如果这个时候你想调试代码,岂不是要疯掉?这里有一个办法,在不具备缓存条件的时候,在不改代码的情况下,禁用缓存。

方法就是修改 spring*.xml 配置文件,设置一个找不到缓存就不做任何操作的标志位,如下

   <cache:annotation-driven /> 
 
   <bean id="simpleCacheManager" class="org.springframework.cache.support.SimpleCacheManager"> 
     <property name="caches"> 
       <set> 
         <bean 
           class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean"
           p:name="default" /> 
       </set> 
     </property> 
   </bean> 

   <bean id="cacheManager" class="org.springframework.cache.support.CompositeCacheManager">
     <property name="cacheManagers"> 
       <list> 
         <ref bean="simpleCacheManager" /> 
       </list> 
     </property> 
     <property name="fallbackToNoOpCache" value="true" /> 
   </bean> 

注意以前的 cacheManager 变为了 simpleCacheManager,且没有配置 accountCache 实例,后面的 cacheManager 的实例是一个 CompositeCacheManager,他利用了前面的 simpleCacheManager 进行查询,如果查询不到,则根据标志位 fallbackToNoOpCache 来判断是否不做任何缓存操作。

使用 guava cache

<bean id="cacheManager" class="org.springframework.cache.guava.GuavaCacheManager">
    <property name="cacheSpecification" value="concurrencyLevel=4,expireAfterAccess=100s,expireAfterWrite=100s" />
    <property name="cacheNames">
        <list>
            <value>dictTableCache</value>
        </list>
    </property>
</bean>
分享到:
评论

相关推荐

    springboot 使用spring cache缓存 和 使用fastjson配置redis系列化

    在本篇文档中,我们将探讨如何使用Spring Cache来缓存数据,并结合Fastjson配置Redis序列化,确保数据正确存储和读取。 首先,我们需要在`pom.xml`中添加必要的依赖。Spring Boot的`spring-boot-starter-cache`模块...

    spring-cache(通过key值更新缓存)

    Spring Cache是一个抽象层,它允许开发者在不关注具体缓存实现的情况下,轻松地在应用程序中添加缓存功能。本篇文章将详细探讨如何通过key值更新Spring Cache中的指定缓存,以及相关的缓存管理策略。 首先,让我们...

    springcache+redis springboot maven

    在这个项目中,"springcache+redis"的整合意味着我们要利用Spring Cache的特性,将缓存存储在Redis中,以提升应用的性能。 首先,Spring Cache提供了`@Cacheable`、`@CacheEvict`和`@Caching`等注解,允许我们在...

    SSM与memcached整合项目Spring Cache

    4. **启用Spring Cache**:在Spring配置文件中,使用`@EnableCaching`注解启用缓存功能。 5. **使用注解**:在业务逻辑的方法上使用`@Cacheable`、`@CacheEvict`和`@CachePut`注解,指定缓存的名称和键生成策略。...

    Redis整合SpringCache实例

    **Redis整合SpringCache实例** 在现代的Web应用中,数据缓存是提高系统性能的关键技术之一。本示例主要探讨如何将开源的内存数据结构存储系统Redis与Spring Cache框架结合,实现高效的分布式缓存解决方案。Redis以...

    SpringCache与redis集成,优雅的缓存解决方案.docx

    使用SpringCache与Redis集成的优雅缓存解决方案,可以大大减少编写模板代码的工作量,提高应用程序的开发效率。同时,它还可以提高应用程序的性能,减少数据库压力。 SpringCache与Redis集成的优雅缓存解决方案是一...

    springboot使用springCache和不使用springCache的对比.zip

    本项目通过对比使用和不使用Spring Cache注解来探讨其作用和效果。 1. **@Cacheable**: 这个注解用于方法上,表示该方法的结果可以被缓存。当调用一个标记为@Cacheable的方法时,Spring会检查缓存中是否存在相同...

    redis与springcache集成

    Redis是一款高性能的键值对数据存储系统,常用于构建分布式缓存系统,而Spring Cache是Spring框架提供的一个抽象层,允许我们使用不同的缓存技术,如Redis, EhCache等。在Spring Boot应用中,集成Redis作为缓存机制...

    Spring cache

    Spring Cache 是 Spring 3.1 版本引入的一项重要特性,它不是一种具体的缓存实现(如 EHCache 或 OSCache),而是一种缓存使用的抽象层。通过在现有的业务代码上添加特定的注解,可以轻松地为方法调用添加缓存支持。...

    spring cache + redis 主从

    此外,还需要掌握如何将Spring Cache与Redis整合,以便在应用中高效使用缓存机制。 一、Redis的主从配置 1. 准备工作: - 操作系统要求:Ubuntu 16.04。 - Redis版本:选择适合的稳定版本,例如redis-4.0.9.tar....

    springCache

    通过 Spring Cache,开发者可以方便地在方法级别启用或禁用缓存,简化了缓存的管理和使用。 首先,让我们深入了解 Spring Cache 的核心概念和配置: 1. **注解驱动**:Spring Cache 提供了 `@Cacheable`、`@...

    springboot 使用spring cache缓存 和 缓存数据落地到redis

    【Spring Boot 使用 Spring Cache 缓存与数据落地到 Redis】\n\n在Spring Boot应用中,Spring Cache是一个强大的工具,可以极大地提升应用的性能,通过缓存非计算性或者昂贵的计算结果。Spring Cache抽象了缓存管理...

    JAVA编程之spring cache本机缓存应用

    1、SpringCache是Spring提供的一个缓存框架,在Spring3.1版本开始支持将缓存添加到现有的spring应用程序中,在4.1开始,缓存已支持JSR-107注释和更多自定义的选项 2、Spring Cache利用了AOP,实现了基于注解的缓存...

    spring cache

    spring cacke spring cacke

    Spring Cache使用RedisCache案例解析

    Spring Cache使用RedisCache案例解析 Spring Cache是Spring框架中的一种缓存机制,主要用于优化应用程序的性能和响应速度。通过使用缓存,可以减少对数据库的访问次数,降低系统的负载,提高系统的整体性能。在...

    spring-cache.xsd+spring-encache.xsd

    标题中的"spring-cache.xsd+spring-encache.xsd"提到了两个XML Schema定义文件,它们是Spring框架中用于缓存管理的配置规范。Spring框架是一个广泛应用的Java企业级应用开发框架,它提供了多种功能,包括但不限于...

    spring-cache-mongodb:基于MongoDB的Spring Cache实现

    5. **MongoDB集成**:在Spring Cache中使用MongoDB作为缓存存储,需要配置MongoDB的连接信息,并使用Spring Data MongoDB提供的库来与数据库交互。这通常涉及到`MongoTemplate`或`MongoRepository`的使用。 6. **...

    Spring Cache的基本使用与实现原理详解

    2. **开启Spring的缓存功能**:在配置文件中使用`&lt;cache:annotation-driven /&gt;`或通过`@EnableCaching`注解开启缓存支持。 3. **配置缓存后端**:配置CacheManager,指定缓存的存储实现,例如使用Ehcache、Guava或...

    redis-cluster和spring集成,基于cache注解

    综上所述,"redis-cluster和spring集成,基于cache注解" 的项目是一个使用 Spring Cache 集成 Redis 集群的实例,旨在通过注解的方式简化缓存管理,提高应用性能。开发者可以通过导入提供的项目,快速了解和实践这一...

Global site tag (gtag.js) - Google Analytics