1. 程式人生 > >SpringBoot資料訪問-------------資料快取

SpringBoot資料訪問-------------資料快取

Spring快取支援

  Spring開始定義了org.springframework.cache.Cache和org.springframework.cache.CacheManager介面來統一不同的快取技術。Spring Cache的核心就是對某個方法進行快取,其實質就是快取該方法的返回結果,並把方法引數和結果用鍵值對的方式存放到快取中,當再次呼叫該方法使用相應的引數時,就會直接從快取裡面取出指定的結果進行返回。所以在使用Cache的時候我們要保證我們快取的方法對於相同的引數要有相同的返回結果。

  自定義快取

  定義實體類

public class Account {
    private int id;
    private String name;
    public Account(String name) {
        this.name = name;
    }
    public int getId() {
        return id;
    }
    public void setId(int id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
}

  定義一個快取管理器

import com.google.common.collect.Maps;
import java.util.Map;
public class CacheContext<T> {
    private Map<String, T> cache = Maps.newConcurrentMap();

    public T get(String key){
        return  cache.get(key);
    }
    public void addOrUpdateCache(String key,T value) {
        cache.put(key, value);
    }
    // 依據 key 來刪除快取中的一條記錄
    public void evictCache(String key) {
        if(cache.containsKey(key)) {
            cache.remove(key);
        }
    }
    // 清空快取中的全部記錄
    public void evictCache() {
        cache.clear();
    }
}

  定義服務類

import com.google.common.base.Optional;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
@Service
public class AccountService1 {

    private final Logger logger = LoggerFactory.getLogger(AccountService1.class);

    @Resource
    private CacheContext<Account> accountCacheContext;

    public Account getAccountByName(String accountName) {
        Account result = accountCacheContext.get(accountName);
        if (result != null) {
            logger.info("get from cache... {}", accountName);
            return result;
        }

        Optional<Account> accountOptional = getFromDB(accountName);
        if (!accountOptional.isPresent()) {
            throw new IllegalStateException(String.format("can not find account by account name : [%s]", accountName));
        }

        Account account = accountOptional.get();
        accountCacheContext.addOrUpdateCache(accountName, account);
        return account;
    }

    public void reload() {
        accountCacheContext.evictCache();
    }

    private Optional<Account> getFromDB(String accountName) {
        logger.info("real querying db... {}", accountName);
        //Todo query data from database
        return Optional.fromNullable(new Account(accountName));
    }

}

Spring Cache

  註解                                  描述   

  @Cacheable                   在方法執行前Spring先檢視快取中是否有資料,如果有資料則直接返回快取資料。反之呼叫方法並將方法返回值放入快取填充。

  @CachePut                    無論快取是否有資料,都會將方法的返回值放入快取。 

  @CacheEvict                  將一條或多條資料從快取中刪除。

  @Caching                     組合多個註解策略在一個方法上。

  Spring Cache例項

package com.kingdee.service;

import java.util.Optional;

import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Service;

import com.kingdee.domains.Account;

@Service
public class AccountService {

	@Cacheable(value="accountCache")
	public Account getAccountByName(String accountName) {
		System.out.println("real querying account... "+accountName);
        // 方法內部實現不考慮快取邏輯,直接實現業務
        Account accountOptional = getFromDB(accountName);
        return accountOptional;
    }
	@CacheEvict(value="accountCache",key="#account.getName()")
    public void updateAccount(Account account) {
        updateDB(account);
    }
	@CachePut(value="accountCache",key="#account.getName()")
	public Account updateAccount2(Account account){
		account.setName("bcd");
		System.out.println("========="+account.getName());
        return account;
	}
	private void updateDB(Account account) {
        System.out.println("real update db..."+account.getName());
    }
	private Account getFromDB(String accountName) {
        System.out.println("real querying db... "+accountName);
        Account account = new Account(accountName);
        return account;
    }
}

  Spring配置

<?xml version="1.0" encoding="UTF-8"?>
<beans
 xmlns="http://www.springframework.org/schema/beans"
 xmlns:context="http://www.springframework.org/schema/context"
 xmlns:p="http://www.springframework.org/schema/p"
 xmlns:util="http://www.springframework.org/schema/util"
 xmlns:mvc="http://www.springframework.org/schema/mvc"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xmlns:task="http://www.springframework.org/schema/task"
 xmlns:rabbit="http://www.springframework.org/schema/rabbit"
 xmlns:cache="http://www.springframework.org/schema/cache"
 xsi:schemaLocation="http://www.springframework.org/schema/beans 
                    http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
                    http://www.springframework.org/schema/context
                    http://www.springframework.org/schema/context/spring-context-3.0.xsd
                    http://www.springframework.org/schema/mvc
                    http://www.springframework.org/schema/mvc/spring-mvc-3.0.xsd
                    http://www.springframework.org/schema/util
                    http://www.springframework.org/schema/util/spring-util-3.0.xsd
                    http://www.springframework.org/schema/task
                    http://www.springframework.org/schema/task/spring-task-3.0.xsd
                    http://www.springframework.org/schema/rabbit
                    http://www.springframework.org/schema/rabbit/spring-rabbit-1.0.xsd
                    http://www.springframework.org/schema/cache
           			http://www.springframework.org/schema/cache/spring-cache.xsd">

<context:component-scan base-package="com.kingdee"></context:component-scan>
<context:annotation-config/>
<cache:annotation-driven/>
    <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>
                <bean class="org.springframework.cache.concurrent.ConcurrentMapCacheFactoryBean">
                    <property name="name" value="myCache"/>
                </bean>
            </set>
        </property>
    </bean>

</beans>

  測試

package com.kingdee;

import org.springframework.context.support.ClassPathXmlApplicationContext;

import com.kingdee.domains.Account;
import com.kingdee.domains.User;
import com.kingdee.service.AccountService;
import com.kingdee.service.UserService;

public class CacheTest {

	public static void main(String[] args){
		ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext("application-beans.xml");
        AccountService accountService = context.getBean("accountService", AccountService.class);
        Account account = accountService.getAccountByName("abc");
        Account account2 = accountService.getAccountByName("abc");
        UserService userService = context.getBean("userService", UserService.class);
        User user = userService.findName("abc");
        System.out.println("==========="+user.getName());
        User user2 = userService.findName("abc");
        System.out.println("==========="+user2.getName());
	}
}

  註解詳解

  快取@Cacheable

  @Cacheable註解會先查詢是否已經有快取,有會使用快取,沒有則會執行方法並快取。

@Cacheable(value = "emp" ,key = "targetClass + methodName +#p0")
    public List<NewJob> queryAll(User uid) {
        return newJobDao.findAllByUid(uid);
    }

  此處的value是必需的,它指定了你的快取存放在哪塊名稱空間。

  此處的key是使用的spEL表示式,參考上章。這裡有一個小坑,如果你把methodName換成method執行會報錯,觀察它們的返回型別,原因在於methodNameStringmethohMethod

  原始碼分析

String[] cacheNames() default {}; //和value註解差不多,二選一
String keyGenerator() default ""; //key的生成器。key/keyGenerator二選一使用
String cacheManager() default ""; //指定快取管理器
String cacheResolver() default ""; //或者指定獲取解析器
String condition() default ""; //條件符合則快取
String unless() default ""; //條件符合則不快取
boolean sync() default false; //是否使用非同步模式

  配置@CacheConfig

  當我們需要快取的地方越來越多,你可以使用@CacheConfig(cacheNames = {"myCache"})註解來統一指定value的值,這時可省略value,如果你在你的方法依舊寫上了value,那麼依然以方法的value值為準。

@CacheConfig(cacheNames = {"myCache"})
public class BotRelationServiceImpl implements BotRelationService {
    @Override
    @Cacheable(key = "targetClass + methodName +#p0")//此處沒寫value
    public List<BotRelation> findAllLimit(int num) {
        return botRelationRepository.findAllLimit(num);
    }
    .....
}

  原始碼分析

String keyGenerator() default "";  //key的生成器。key/keyGenerator二選一使用
String cacheManager() default "";  //指定快取管理器
String cacheResolver() default ""; //或者指定獲取解析器

  更新@CachePut

  @CachePut註解的作用 主要針對方法配置,能夠根據方法的請求引數對其結果進行快取,和 @Cacheable 不同的是,它每次都會觸發真實方法的呼叫 。簡單來說就是使用者更新快取資料。但需要注意的是該註解的value 和 key 必須與要更新的快取相同,也就是與@Cacheable 相同。示例:

@CachePut(value = "emp", key = "targetClass + #p0")
    public NewJob updata(NewJob job) {
        NewJob newJob = newJobDao.findAllById(job.getId());
        newJob.updata(job);
        return job;
    }

  原始碼分析

String[] cacheNames() default {}; //與value二選一
String keyGenerator() default "";  //key的生成器。key/keyGenerator二選一使用
String cacheManager() default "";  //指定快取管理器
String cacheResolver() default ""; //或者指定獲取解析器
String condition() default ""; //條件符合則快取
String unless() default ""; //條件符合則不快取

  清除@CacheEvict

  @CachEvict 的作用 主要針對方法配置,能夠根據一定的條件對快取進行清空 。

@CacheEvict(value="emp",key="#id")
    public void delect(int id) {
        newJobDao.deleteAllById(id);
    }

  原始碼分析

String[] cacheNames() default {}; //與value二選一
String keyGenerator() default "";  //key的生成器。key/keyGenerator二選一使用
String cacheManager() default "";  //指定快取管理器
String cacheResolver() default ""; //或者指定獲取解析器
String condition() default ""; //條件符合則清空

SpringBoot例項

  在Springboot執行時開啟快取支援

package com.kingdee;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.context.annotation.ImportResource;

@SpringBootApplication
@EnableCaching
public class App {

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