1. 程式人生 > >springboot與快取—使用、原理、整合redis

springboot與快取—使用、原理、整合redis

      Java快取介面標準JSR-107:Java Caching定義了5個核心介面,分別是CachingProvider(快取提供者), CacheManager(快取管理器), Cache(快取元件), Entry (key-value對)和Expiry(條目有效期),他們的關係如下圖所示。但是該快取規範,整合難度較大。Spring從3.1開始定義了org.springframework.cache.Cache和org.springframework.cache.CacheManager介面來統一不同的快取技術;

1、介面、快取註解、key/value生成策略

Cache 快取介面,定義快取操作。實現:RedisCache、EhCache等(根據快取技術不同來實現Cache介面)
CacheManager 快取介面,快取管理器,管理各種快取(Cache)元件
@Cacheable 可快取的,標註方法,能夠根據方法的請求引數對其結果進行快取
@CacheEvict 清空快取
@CachePut 更新快取
@EnableCaching 開啟基於註解的快取
keyGenerator 快取資料時key生成策略
serialize 快取資料時value序列化策略

2、使用cache快取步驟(不使用快取時,每次訪問都會連線資料庫)

      a、引入依賴

<dependency>
	<groupId>org.springframework.boot</groupId>
	<artifactId>spring-boot-starter-cache</artifactId>
</dependency>

      b、開啟基於註解的快取 @EnableCaching

package com.example.cache.service;

import com.example.cache.bean.Employee;
import com.example.cache.mapper.EmployeeMapper;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.cache.annotation.*;
import org.springframework.stereotype.Service;

//@Service:將service載入到容器中
//CacheConfig:公共的快取配置;cacheNames = "emp":之後的value="emp"就可以不用寫
@CacheConfig(cacheNames = "emp")
@Service
public class EmployeeService {
    @Autowired
    EmployeeMapper employeeMapper;

    /**
     *  @Cacheable:將方法的執行結果進行儲存,以後再要相同的資料,直接從快取中獲取,不用呼叫方法
     * CacheManager管理多個Cache元件,對快取的真正CRUD操作在Cache元件中,每個快取元件有自己唯一的名字
     * 屬性:(快取資料時使用key-value的形式)
     *      cacheNames/value:指定快取元件的名字;將方法的返回結果放在哪個快取中,是陣列的方式,可以指定多個快取
     *      key:快取資料使用的key;可以用她來指定。預設是使用方法引數的值 鍵值對:1-方法的返回值
     *          引數id的值:#id相當於#root.args[0]
     *      keyGenerator:key的生成器;可以自己指定key的生成器元件id
     *          ——key和keyGenerator不可同時使用
     *      cacheManager:指定快取管理器(從哪個快取管理器中取);cacheResolver:指定快取解析器
     *      condition:指定符合條件的情況下才快取;condition="#id>0":id大於0的時候才快取
     *      unless:否定快取;當unless指定的true,方法的返回值就不會被快取;可以獲取到結果進行判斷
     *          unless="#result == null":結果為空時不快取
     *      sync:快取是否使用非同步模式
     * @param id
     * @return
     */
    @Cacheable(cacheNames = {"emp"})
    public Employee getEmp(Integer id){
        System.out.println("查詢"+id+"號員工");
        Employee emp = employeeMapper.getEmpById(id);
        return emp;
    }
    /**
     * @CachePut:既呼叫方法,又更新快取資料;
     * 修改了資料庫的某個資料,同時更新快取
     * 執行過程:
     *  1、先呼叫目標方法
     *  2、將目標方法的結果快取起來
     * 更新後重新查詢出的資料是更新前的資料:
     *  ——(key預設使用方法引數的值)查詢是的快取是@Cacheable快取的值,key是1;@CachePut更新後快取的值:key是傳入的employee物件
     *  ——所以要統一key:key="#employee.id";key="#result.id";
     */
    @CachePut(value = "emp",key="#employee.id")
    public Employee updateEmp(Employee employee){
        employeeMapper.updateEmp(employee);
        return employee;
    }
    /**
     *@CacheEvict快取清除
     * key:指定要清楚的資料
     * allEntries=true:指定清除這個快取中的所有資料
     * beforeInvocation = false;快取的清除是否在方法之前執行;false:在方法執行之後清除
     */
    @CacheEvict(value="emp",key="#id")
    public void deleteEmp(Integer id){
    }

    /**
     * 指定多個快取規則
     */
    @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) {
        return employeeMapper.getEmpByLastName(lastName);
    }
}

3、整合redis進行快取;開發中常使用快取中介軟體:redis、memcahed、ehcache;

     引入redis啟動器

<dependency>
   	<groupId>org.springframework.boot</groupId>
   	<artifactId>spring-boot-starter-data-redis</artifactId>
</dependency>

    配置redis資訊

spring.redis.host=127.0.0.1
spring.redis.password=

     使用redis

@RunWith(SpringRunner.class)
@SpringBootTest
public class SpringbootCacheApplicationTests implements Serializable {
    @Autowired
	EmployeeMapper employeeMapper;
    @Autowired
	StringRedisTemplate stringRedisTemplate;//key-value都是操作字串
	@Autowired
	RedisTemplate redisTemplate;//key-value都是物件
    //@Autowired
	//RedisTemplate<Object, Employee> empRedisTemplate;
    
    /**
	 * Redis常見的五大資料型別
	 * 	String(字串)、List(列表)、Set(集合)、Hash(雜湊)、ZSet(有序集合)
	 * 	stringRedisTemplate.opsForValue()[操作String(字串)]
	 * 	stringRedisTemplate.opsForList()[操作List(列表)]
	 * 	stringRedisTemplate.opsForSet()[操作Set(集合)]
	 * 	stringRedisTemplate.opsForHash()[操作Hash(雜湊)]
	 * 	stringRedisTemplate.opsForZSet()[操作ZSet(有序集合)]
	 */
	@Test
	public  void test01(){
		//stringRedisTemplate.opsForValue().append("msg","hello");
		String msg = stringRedisTemplate.opsForValue().get("msg");
		System.out.println(msg);
	}

    //測試儲存物件(需要序列化)
	@Test
	public  void test02(){
		Employee empById = employeeMapper.getEmpById(1);
		//預設如果儲存物件,使用jdk序列化機制,序列化後的資料儲存到redis
		redisTemplate.opsForValue().set("emp-01",empById);
		//將資料以json的方式儲存:1、自己將物件轉化為json;2、redisTemplate預設的序列化規則:改變預設的序列化規則
		//empRedisTemplate.opsForValue().set("emp-01",empById);
	}
}

        自定義序列化器,改變預設的序列化規則;使其儲存的資料為json格式,而不是序列化格式。

package com.example.cache.config;

import com.example.cache.bean.Employee;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;

import java.net.UnknownHostException;
import java.util.LinkedHashSet;
import java.util.List;

@Configuration
public class MyRedisConfig {
    //RedisTemplate<Object, Employee>:序列化Employee
    @Bean
    public RedisTemplate<Object, Employee> empRedisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
        RedisTemplate<Object, Employee> template = new RedisTemplate();
        template.setConnectionFactory(redisConnectionFactory);
        //序列化器
        Jackson2JsonRedisSerializer<Employee> ser = new Jackson2JsonRedisSerializer<Employee>(Employee.class);
        template.setDefaultSerializer(ser);
        return template;
    }
}

     原理:預設使用快取管理器CacheManager來建立Cache快取元件,來給快取中存取資料。加入redis後,開啟了RedisCacheConfiguration。RedisCacheManager幫我們建立RedisCache來作為快取元件。

4、快取執行流程  

     @Cacheable:

    a、方法執行之前,先去查詢Cache(快取元件),按照cacheNames指定的名字獲取;(CacheManager先獲取相應的快取),如果沒有Cache元件,第一次獲取快取會自動建立

     b、去Cache中查詢快取的內容,使用一個key,預設就是方法的引數;

          key是按照某種策略生成的;預設是使用keyGenerator生成的;預設使用SimpleKeyGenerator生成key

          SimpleKeyGenerator生成key的預設策略:

           如果沒有引數:key=new SimpleKey();如果有一個引數,key=引數的值;如果有多個引數:key= new SimpleKey(params);

      c、沒有查到快取就呼叫目標方法;

      d、將目標方法返回的結果放到快取中

     @Cacheable標註的方法執行之前來檢查快取中有沒有這個資料,預設按照引數的值作為key去查詢快取,如果沒有就執行方法,並將結果放入快取。以後再來呼叫,就可以使用快取中的資料;

5、快取原理

  a、自動配置類CacheAutoConfiguration給容器中匯入元件

   b、各快取的配置類

   c、預設生效的配置類:SimpleCacheConfiguration

   d、給容器中註冊了一個CacheManager:ConcurrentManager;可以獲取和建立ConcurrentMapCache型別的快取元件,他的作用是將資料儲存在ConcurrentMap中。