1. 程式人生 > >Spring思維導圖,讓Spring不再難懂(cache篇)

Spring思維導圖,讓Spring不再難懂(cache篇)

關於快取

快取是實際工作中非常常用的一種提高效能的方法。而在java中,所謂快取,就是將程式或系統經常要呼叫的物件存在記憶體中,再次呼叫時可以快速從記憶體中獲取物件,不必再去建立新的重複的例項。這樣做可以減少系統開銷,提高系統效率。

在增刪改查中,資料庫查詢佔據了資料庫操作的80%以上,而非常頻繁的磁碟I/O讀取操作,會導致資料庫效能極度低下。而資料庫的重要性就不言而喻了:
  • 資料庫通常是企業應用系統最核心的部分
  • 資料庫儲存的資料量通常非常龐大
  • 資料庫查詢操作通常很頻繁,有時還很複雜
在系統架構的不同層級之間,為了加快訪問速度,都可以存在快取

spring cache特性與缺憾

現在市場上主流的快取框架有ehcache、redis、memcached。spring cache可以通過簡單的配置就可以搭配使用起來。其中使用註解方式是最簡單的。


Cache註解

從以上的註解中可以看出,雖然使用註解的確方便,但是缺少靈活的快取策略,

快取策略:
  • TTL(Time To Live ) 存活期,即從快取中建立時間點開始直到它到期的一個時間段(不管在這個時間段內有沒有訪問都將過期)
  • TTI(Time To Idle) 空閒期,即一個數據多久沒被訪問將從快取中移除的時間
專案中可能有很多快取的TTL不相同,這時候就需要編碼式使用編寫快取。

條件快取

根據執行流程,如下@Cacheable將在執行方法之前( #result還拿不到返回值)判斷condition,如果返回true,則查快取;
程式碼
  1. @Cacheable(value = 
    "user", key = "#id", condition = "#id lt 10")    
  2. public User conditionFindById(final Long id)  
@Cacheable(value = "user", key = "#id", condition = "#id lt 10")  
public User conditionFindById(final Long id)

如下@CachePut將在執行完方法後(#result就能拿到返回值了)判斷condition,如果返回true,則放入快取
程式碼
  1. @CachePut(value = "user"
    , key = "#id", condition = "#result.username ne 'zhang'")    
  2. public User conditionSave(final User user)  
@CachePut(value = "user", key = "#id", condition = "#result.username ne 'zhang'")  
public User conditionSave(final User user)

如下@CachePut將在執行完方法後(#result就能拿到返回值了)判斷unless,如果返回false,則放入快取;(即跟condition相反)
程式碼
  1. @CachePut(value = "user", key = "#user.id", unless = "#result.username eq 'zhang'")    
  2. public User conditionSave2(final User user)  
@CachePut(value = "user", key = "#user.id", unless = "#result.username eq 'zhang'")  
public User conditionSave2(final User user)

如下@CacheEvict, beforeInvocation=false表示在方法執行之後呼叫(#result能拿到返回值了);且判斷condition,如果返回true,則移除快取;
程式碼
  1. @CacheEvict(value = "user", key = "#user.id", beforeInvocation = false, condition = "#result.username ne 'zhang'")   
  2. public User conditionDelete(final User user)  
@CacheEvict(value = "user", key = "#user.id", beforeInvocation = false, condition = "#result.username ne 'zhang'") 
public User conditionDelete(final User user)

小試牛刀,綜合運用:
程式碼
  1. @CachePut(value = "user", key = "#user.id")  
  2.    public User save(User user) {  
  3.        users.add(user);  
  4.        return user;  
  5.    }  
  6.    @CachePut(value = "user", key = "#user.id")  
  7.    public User update(User user) {  
  8.        users.remove(user);  
  9.        users.add(user);  
  10.        return user;  
  11.    }  
  12.    @CacheEvict(value = "user", key = "#user.id")  
  13.    public User delete(User user) {  
  14.        users.remove(user);  
  15.        return user;  
  16.    }  
  17.    @CacheEvict(value = "user", allEntries = true)  
  18.    public void deleteAll() {  
  19.        users.clear();  
  20.    }  
  21.    @Cacheable(value = "user", key = "#id")  
  22.    public User findById(final Long id) {  
  23.        System.out.println("cache miss, invoke find by id, id:" + id);  
  24.        for (User user : users) {  
  25.            if (user.getId().equals(id)) {  
  26.                return user;  
  27.            }  
  28.        }  
  29.        return null;  
  30.    }  
 @CachePut(value = "user", key = "#user.id")
    public User save(User user) {
        users.add(user);
        return user;
    }

    @CachePut(value = "user", key = "#user.id")
    public User update(User user) {
        users.remove(user);
        users.add(user);
        return user;
    }

    @CacheEvict(value = "user", key = "#user.id")
    public User delete(User user) {
        users.remove(user);
        return user;
    }

    @CacheEvict(value = "user", allEntries = true)
    public void deleteAll() {
        users.clear();
    }

    @Cacheable(value = "user", key = "#id")
    public User findById(final Long id) {
        System.out.println("cache miss, invoke find by id, id:" + id);
        for (User user : users) {
            if (user.getId().equals(id)) {
                return user;
            }
        }
        return null;
    }

配置ehcache與redis
spring cache整合ehcache,spring-ehcache.xml主要內容:
程式碼
  1. <dependency>  
  2.     <groupId>net.sf.ehcache</groupId>  
  3.     <artifactId>ehcache-core</artifactId>  
  4.     <version>${ehcache.version}</version>  
  5. </dependency>  
<dependency>
	<groupId>net.sf.ehcache</groupId>
	<artifactId>ehcache-core</artifactId>
	<version>${ehcache.version}</version>
</dependency>

程式碼
  1. <!-- Spring提供的基於的Ehcache實現的快取管理器 -->  
  2. <!-- 如果有多個ehcacheManager要在bean加上p:shared="true" -->  
  3. <bean id="ehcacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">  
  4.      <property name="configLocation" value="classpath:xml/ehcache.xml"/>  
  5. </bean>  
  6. <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">  
  7.      <property name="cacheManager" ref="ehcacheManager"/>  
  8.      <property name="transactionAware" value="true"/>  
  9. </bean>  
  10. <!-- cache註解,和spring-redis.xml中的只能使用一個 -->  
  11. <cache:annotation-driven cache-manager="cacheManager" proxy-target-class="true"/>  
<!-- Spring提供的基於的Ehcache實現的快取管理器 -->
    
<!-- 如果有多個ehcacheManager要在bean加上p:shared="true" -->
<bean id="ehcacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean">
     <property name="configLocation" value="classpath:xml/ehcache.xml"/>
</bean>
    
<bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
     <property name="cacheManager" ref="ehcacheManager"/>
     <property name="transactionAware" value="true"/>
</bean>
    
<!-- cache註解,和spring-redis.xml中的只能使用一個 -->
<cache:annotation-driven cache-manager="cacheManager" proxy-target-class="true"/>

spring cache整合redis,spring-redis.xml主要內容:
程式碼
  1. <dependency>  
  2.     <groupId>org.springframework.data</groupId>  
  3.     <artifactId>spring-data-redis</artifactId>  
  4.     <version>1.8.1.RELEASE</version>  
  5. </dependency>  
  6. <dependency>  
  7.     <groupId>org.apache.commons</groupId>  
  8.     <artifactId>commons-pool2</artifactId>  
  9.     <version>2.4.2</version>  
  10. </dependency>  
  11. <dependency>  
  12.     <groupId>redis.clients</groupId>  
  13.     <artifactId>jedis</artifactId>  
  14.     <version>2.9.0</version>  
  15. </dependency>  
<dependency>
	<groupId>org.springframework.data</groupId>
	<artifactId>spring-data-redis</artifactId>
	<version>1.8.1.RELEASE</version>
</dependency>
<dependency>
	<groupId>org.apache.commons</groupId>
	<artifactId>commons-pool2</artifactId>
	<version>2.4.2</version>
</dependency>
<dependency>
	<groupId>redis.clients</groupId>
	<artifactId>jedis</artifactId>
	<version>2.9.0</version>
</dependency>

程式碼
  1. <!-- 注意需要新增Spring Data Redis等jar包 -->  
  2. <description>redis配置</description>  
  3. <bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">  
  4.     <property name="maxIdle" value="${redis.pool.maxIdle}"/>  
  5.     <property name="maxTotal" value="${redis.pool.maxActive}"/>  
  6.     <property name="maxWaitMillis" value="${redis.pool.maxWait}"/>  
  7.     <property name="testOnBorrow" value="${redis.pool.testOnBorrow}"/>  
  8.     <property name="testOnReturn" value="${redis.pool.testOnReturn}"/>  
  9. </bean>  
  10. <!-- JedisConnectionFactory -->  
  11. <bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">  
  12.     <property name="hostName" value="${redis.master.ip}"/>  
  13.     <property name="port" value="${redis.master.port}"/>  
  14.     <property name="poolConfig" ref="jedisPoolConfig"/>  
  15. </bean>  
  16. <bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
  17.       p:connectionFactory-ref="jedisConnectionFactory">  
  18.     <property name="keySerializer">  
  19.         <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"></bean>  
  20.     </property>  
  21.     <property name="valueSerializer">  
  22.         <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>  
  23.     </property>  
  24.     <property name="hashKeySerializer">  
  25.         <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>  
  26.     </property>  
  27.     <property name="hashValueSerializer">  
  28.         <bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>  
  29.     </property>  
  30. </bean>  
  31. <!--spring cache-->  
  32. <bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager"
  33.       c:redisOperations-ref="redisTemplate">  
  34.     <!-- 預設快取10分鐘 -->  
  35.     <property name="defaultExpiration" value="600"/>  
  36.     <property name="usePrefix" value="true"/>  
  37.     <!-- cacheName 快取超時配置,半小時,一小時,一天 -->  
  38.     <property name="expires">  
  39.         <map key-type="java.lang.String" value-type="java.lang.Long">  
  40.             <entry key="halfHour" value="1800"/>  
  41.             <entry key="hour" value="3600"/>  
  42.             <entry key="oneDay" value="86400"/>  
  43.             <!-- shiro cache keys -->  
  44.             <entry key="authorizationCache" value="1800"/>  
  45.             <entry key="authenticationCache" value="1800"/>  
  46.             <entry key="activeSessionCache" value="1800"/>  
  47.         </map>  
  48.     </property>  
  49. </bean>  
  50. <!-- cache註解,和spring-ehcache.xml中的只能使用一個 -->  
  51. <cache:annotation-driven cache-manager="cacheManager" proxy-target-class="true"/>  
<!-- 注意需要新增Spring Data Redis等jar包 -->
<description>redis配置</description>

<bean id="jedisPoolConfig" class="redis.clients.jedis.JedisPoolConfig">
	<property name="maxIdle" value="${redis.pool.maxIdle}"/>
	<property name="maxTotal" value="${redis.pool.maxActive}"/>
	<property name="maxWaitMillis" value="${redis.pool.maxWait}"/>
	<property name="testOnBorrow" value="${redis.pool.testOnBorrow}"/>
	<property name="testOnReturn" value="${redis.pool.testOnReturn}"/>
</bean>

<!-- JedisConnectionFactory -->
<bean id="jedisConnectionFactory" class="org.springframework.data.redis.connection.jedis.JedisConnectionFactory">
	<property name="hostName" value="${redis.master.ip}"/>
	<property name="port" value="${redis.master.port}"/>
	<property name="poolConfig" ref="jedisPoolConfig"/>
</bean>

<bean id="redisTemplate" class="org.springframework.data.redis.core.RedisTemplate"
	  p:connectionFactory-ref="jedisConnectionFactory">
	<property name="keySerializer">
		<bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"></bean>
	</property>
	<property name="valueSerializer">
		<bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
	</property>
	<property name="hashKeySerializer">
		<bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
	</property>
	<property name="hashValueSerializer">
		<bean class="org.springframework.data.redis.serializer.JdkSerializationRedisSerializer"/>
	</property>
</bean>

<!--spring cache-->
<bean id="cacheManager" class="org.springframework.data.redis.cache.RedisCacheManager"
	  c:redisOperations-ref="redisTemplate">
	<!-- 預設快取10分鐘 -->
	<property name="defaultExpiration" value="600"/>
	<property name="usePrefix" value="true"/>
	<!-- cacheName 快取超時配置,半小時,一小時,一天 -->
	<property name="expires">
		<map key-type="java.lang.String" value-type="java.lang.Long">
			<entry key="halfHour" value="1800"/>
			<entry key="hour" value="3600"/>
			<entry key="oneDay" value="86400"/>
			<!-- shiro cache keys -->
			<entry key="authorizationCache" value="1800"/>
			<entry key="authenticationCache" value="1800"/>
			<entry key="activeSessionCache" value="1800"/>
		</map>
	</property>
</bean>
<!-- cache註解,和spring-ehcache.xml中的只能使用一個 -->
<cache:annotation-driven cache-manager="cacheManager" proxy-target-class="true"/>

專案中註解快取只能配置一個,所以可以通過以下引入哪個配置檔案來決定使用哪個快取。
當然,可以通過其他配置搭配使用兩個快取機制。比如ecache做一級快取,redis做二級快取。


更加詳細的使用與配置,可以參考專案中spring-shiro-training中有關spring cache的配置。

原文轉自:  http://www.iteye.com/news/32626