SpringBoot與緩存


幾個重要的概念 & 緩存注解

Cache

緩存接口,定義緩存操作。實現有:RedisCacheEhCacheCacheConcurrentMapCache

CacheManager

緩存管理器,管理各種緩存(Cache)組件

@Cacheable

主要針對方法配置,能夠根據方法的請求參數對其結果進行緩存

@CacheEvict

清空緩存

@CachePut

保證方法調用,又能將結果緩存,可以用於刷新緩存

@EnableCaching

開啟基於注解的緩存

keyGenerator

緩存數據時key生成策略

serialize

緩存數據時value序列化的策略

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

使用緩存

首先引入cache組件

@Cacheable

將方法的運行結果進行緩存,以后要相同的數據,直接從緩存中獲取,不用調用方法

  • CacheManager管理多個Cache組件的,每一個緩存組件有自己唯一的名字
  • 幾個屬性:
    • cacheNames/value:指定緩存組件的名字 cacheNames = {"emp"} 
    • key:緩存數據使用的key,默認使用的是方法的參數、還可以使用SpEL來編寫
    • keyGeneratorkey的生成器,生成緩存數據的keykeyGeneratorkey只能二選一
    • cacheManager:指定緩存管理器,或者cacheResolver獲取解析器
    • condition:指定符合條件的情況下才緩存。例:condition="#id>0",id大於0才緩存 
    • unless:否定緩存,當unless指定的條件為true,方法的返回值就不會被緩存,可以用#result獲取方法的返回值。例: unless = "#result == null" 
    • sync: 是否使用異步模式

key的取值

名字

描述

示例

methodName

當前被調用的方法名

#root.methodName

method

當前被調用的方法

#root.method.name

target

當前被調用的目標對象

#root.target

targetClass

當前被調用的目標對象類

#root.targetClass

args

當前被調用的方法的參數列表

#root.args[0]

caches

當前方法調用使用的緩存列表(如@Cacheable(value={“cache1”, “cache2”})),則有兩個cache

#root.caches[0].name

argument name

方法參數的名字,可以直接 #參數名,也可以使用 #p0#a0 的形式,0代表參數的索引

#iban#a0, #p0

result

方法執行后的返回值(僅當前方法執行之后的判斷有效,如’unless’, ‘cache put’的表達式 ‘cache evict’的表達式beforeInvocation=false

#result

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

 

例:

①、首先在主程序類上標注上@EnableCaching注解

@EnableCaching
@SpringBootApplication
public class SpringBootCacheApplication {

    public static void main(String[] args) {
        SpringApplication.run(SpringBootCacheApplication.class, args);
    }
}

②、在要緩存的方法上標注即可

@Cacheable(cacheNames = {"emp"})
public Employee getEmp(Integer id){
    System.out.println("查詢" + id + "員工號");
    Employee emp = employeeMapper.getEmployeeById(id);
    return emp;
}

@CachePut

這個注解用於更新緩存

示例:

/**
 * @CachePut: 既調用方法,又更新緩存
 * 主要用於修改了數據庫的某個數據,同時更新緩存
 * 運行時機:先調用目標方法,再將目標方法的結果緩存起來
 * 與@Cacheable不同,@Cacheable是先去緩存里面查看是否有值,再執行方法,因而這里在獲取id的時候@CachePut可以通過#result獲取而@Cacleable不行
 * 注意:這里的key一定要填要更新的緩存的key,不填就會默認使用第一個參數作為key,很可能就達不到更新緩存的目的而是新創建了一個緩存
 */
@CachePut(value = "emp", key = "#result.id")
public Employee updateEmp(Employee employee){
    System.out.println("更新cache");
    employeeMapper.updateEmp(employee);
    return employee;
}

@CacheEvict

該注解用於刪除一個緩存

示例:

/**
 * allEntries:刪除所有的緩存   allEntries = true
 * beforeInvocation:緩存的清除是否在方法之前執行,默認是在方法之后執行
 * 其他參數與@Cacheable大致一樣
 * @param id
 */
@CacheEvict(value = "emp", key = "#id", allEntries = true)
public void deleteEmp(Integer id){
    System.out.println("刪除" + id);
}

@Caching

該注解用於存放多個緩存注解,有時候對於一個方法,想放置多個緩存,既想緩存又想更新時使用

示例:

/**
 * 可以放多個注解
 * @param lastName
 * @return
 */
@Caching(
        cacheable = {
                @Cacheable(value = "emp", key = "#lastName")
        },
        put = {
                @CachePut(value = "emp", key = "#result.id"),
                @CachePut(value = "emp", key = "#result.email")
        }
)
public Employee getEmpByLastName(String lastName){
    System.out.println("查詢");
    return employeeMapper.getEmpByLastName(lastName);
}

@CacheConfig

有的時候覺得每次在使用緩存注解的時候都要指定緩存的名字,或者指定緩存的cacheManager之類的,覺得很麻煩。那么就可以在類上使用@CacheConfig統一的配置緩存的名字

@CacheConfig(value = "emp")
@Service
public class EmployeeService {

自定義key的生成策略

對於key,我們可以讓它自動生成,生成的策略可以有我們自己制定,之需要在配置類中將我們的定制規則加入到容器中即可

@Bean
public KeyGenerator keyGenerator(){
    return new KeyGenerator(){
        @Override
        public Object generate(Object o, Method method, Object... objects) {
            return method.getName() + "[" + Arrays.asList(objects) + "]";
        }
    };
}

使用Redis做為緩存

SpringBoot默認使用的是ConcurrentMapCacheManager==ConcurrentMapCache,將數據保存在ConcurrentMap<String, Cache> cacheMap里面

當然我們還可以使用其他的中間件作為緩存

 

開發中使用的緩存中間件:redismemcachedehcache

引入redis的starter

<!-- 引入redis的starter -->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

配置redis

來到配置文件application.yaml配置redis的主機地址

spring:
  redis:
    host: 172.17.119.176

使用redis

springbootstarter提供了兩個Template操作redis

StringRedisTemplateRedisTemplate,其中StringRedisTemplatekeyvalue都是字符串。redisTemplatekeyvalue都是Object

/**
 * String(字符串)、List(列表)、Set(集合)、Hash(散列)、ZSet(有序集合)、HyperLogLog
 * stringRedisTemplate.opsForValue()  操作String(字符串)
 * stringRedisTemplate.opsForList()   操作List(列表)
 * stringRedisTemplate.opsForSet()    操作Set(集合)
 * stringRedisTemplate.opsForHash()   操作Hash(散列)
 * stringRedisTemplate.opsForZSet()   操作ZSet(有序集合)
 * stringRedisTemplate.opsForHyperLogLog()  操作HyperLogLog
 */
@Test
public void test01(){
    stringRedisTemplate.opsForValue().append("redis", "hello");
    stringRedisTemplate.opsForList();
    stringRedisTemplate.opsForSet();
    stringRedisTemplate.opsForHash();
    stringRedisTemplate.opsForZSet();
    stringRedisTemplate.opsForHyperLogLog();
}

儲存對象

有的時候需要將對象存進redis(例如一個JavaBean對象),但是如果對象不是可Serializable的,因此需要讓JavaBean對象實現Serializable接口

public class Employee implements Serializable {

序列化機制

如果只是讓JavaBean實現Serializable接口也是可以存儲的,但是並不好看,那么能不能將JavaBean弄成Json的樣式放進redis呢。直接的方式就是自己轉換,但是未免有點麻煩,那就只能修改RedisTemplate的序列化機制了,在配置類中配置上序列化的方法即可

@Bean
public RedisTemplate<Object, Employee> empredisTemplate(
        RedisConnectionFactory redisConnectionFactory)
        throws UnknownHostException {
    RedisTemplate<Object, Employee> redisTemplate = new RedisTemplate<>();
    redisTemplate.setConnectionFactory(redisConnectionFactory);
    Jackson2JsonRedisSerializer<Employee> ser = new Jackson2JsonRedisSerializer<Employee>(Employee.class);
    redisTemplate.setDefaultSerializer(ser);
    return redisTemplate;
}

自定義CacheManager

對於自定義CacheManager,SpringBoot 1.x跟SpringBoot 2.x有所不同

SpringBoot 1.x

@Bean
public CacheManager cacheManager(RedisTemplate redisTemplate) {
    RedisCacheManager redisCacheManager = new RedisCacheManager(redisTemplate);
    //默認超時時間,單位秒
    redisCacheManager.setDefaultExpiration(60);
    //緩存超時時間Map,key為cacheName,value為超時,單位是秒
    Map<String, Long> expiresMap = new HashMap<>();
    //緩存用戶信息的cacheName和超時時間
    expiresMap.put("user", 1800L);
    //緩存產品信息的cacheName和超時時間
    expiresMap.put("product", 600L);
    redisCacheManager.setExpires(expiresMap);
    return redisCacheManager;
}

SpringBoot 2.x

@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
    RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig()
            .entryTtl(Duration.ofHours(1)); // 設置緩存有效期一小時
    return RedisCacheManager
            .builder(RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory))
            .cacheDefaults(redisCacheConfiguration).build();
}

編碼的方式使用緩存

前面都是用的注解的方法將注解標注在方法上去緩存方法的結果,但是如果在方法的執行中有一個數據我們想緩存那應該怎么辦呢?既然前面提到過CacheManager是管理Cache的,那我們就可以直接使用CacheManager去緩存了

@Autowired
RedisCacheManager redisCacheManager;

public void test(){
    Cache emp = redisCacheManager.getCache("emp");
    emp.put("111", "222");
}

  

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM