1. 程式人生 > >SpringBoot之資料快取Cache操作

SpringBoot之資料快取Cache操作

一、前言
快取要解決的問題:一個程式的瓶頸在於資料庫,我們也知道記憶體的速度是大大快於硬碟的速度的。當我們需要重複地獲取相同的資料的時候,我們一次又一次的請求資料庫或者遠端服務,導致大量的時間耗費在資料庫查詢或者遠端方法呼叫上,導致程式效能的惡化,這便是資料快取要解決的問題。

類似的快取技術有:Redis、EhCache、Guava等,現在一般常用的為Redis。

Spring 3.1 引入了激動人心的基於註釋(annotation)的快取(cache)技術,它本質上不是一個具體的快取實現方案(例如EHCache 或者 OSCache),而是一個對快取使用的抽象,通過在既有程式碼中新增少量它定義的各種 annotation,即能夠達到快取方法的返回物件的效果。
Spring 的快取技術還具備相當的靈活性,不僅能夠使用 SpEL(Spring Expression Language)來定義快取的 key 和各種 condition,還提供開箱即用的快取臨時儲存方案,也支援和主流的專業快取例如 EHCache 整合。
其特點總結如下:
1. 通過少量的配置 annotation 註釋即可使得既有程式碼支援快取
2. 支援開箱即用 Out-Of-The-Box,即不用安裝和部署額外第三方元件即可使用快取
3. 支援 Spring Express Language,能使用物件的任何屬性或者方法來定義快取的 key 和 condition
4. 支援 AspectJ,並通過其實現任何方法的快取支援
5. 支援自定義 key 和自定義快取管理者,具有相當的靈活性和擴充套件性

二、Spring快取支援
Spring定義了org.springframework.cache.CacheManager和org.springframework.cache.Cache介面用來統一不同的快取的技術。其中,CacheManager是Spring提供的各種快取技術抽象介面,Cache介面包含快取的各種操作(增加、刪除、獲得快取,我們一般不會直接和此介面打交道)。

1..Spring支援的CacheManager實現如下圖:

在我們使用任意一個實現的CacheManager的時候,需註冊實現的CacheManager的Bean,例如:

@Bean
public EhCacheCacheManager cacheManager(CacheManager ehCacheCacheManager) {
    return new EhCacheCacheManager(ehCacheCacheManager);
}

2.聲名式快取註解 
Spring提供了4個註解來宣告快取規則(又是使用註解式的AOP的一個生動例子)。這四個註解如表8-6 
所示。

@Cacheable、@CachePut、@CacheEvit都有value屬性,指定的是要使用的快取名稱;key屬性指定的是 
資料在快取中的儲存的鍵。

3.開啟聲名式快取支援 
開啟聲名式快取支援十分簡單,只需在配置類上使用@EnableCaching註解即可,例如:

@Configuration
@EnableCaching
public class AppConfig {
}

四:本例 
將以Spring Boot預設的ConcurrentMapCacheManager作為快取技術,演示@Cacheable、CachePut、@CacheEvit,最後使用EhCache、Guava來替換快取技術。 
1.pom.xml,主要是需要加入jpa和mysql。

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-jpa</artifactId>
        </dependency>
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
            <scope>runtime</scope>
        </dependency>

2.application.yml,進行mysql配置和jpa配置,注意這裡的show-sql: true

server:
  port: 5000

spring:
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/test?useUnicode=true&characterEncoding=utf8&characterSetResults=utf8
    username: root
    password: ******

  jpa:
    hibernate:
      ddl-auto: update
    show-sql: true

3.以userinfo為例進行配置

//UserInfo 
@Entity
public class UserInfo {

    @Id
    @GeneratedValue
    private Long id;
    private String name;
    private int age;
    private String sex;
    private String address;
    省略get/set...
}

//UserInfoDao
public interface UserInfoDao extends JpaRepository<UserInfo, Long> {
    //
}

//UserInfoService
public interface UserInfoService {
    UserInfo save(UserInfo userInfo);
    void remove(Long id);
    UserInfo findOne(UserInfo userInfo);
}

//UserInfoServiceImpl
@Service
public class UserInfoServiceImpl implements UserInfoService  {
    private static Logger log = LoggerFactory.getLogger(UserInfoServiceImpl.class);
    @Autowired
    UserInfoDao userInfoDao;

    @Override
    @CachePut(value = "userinfocache",key="#userInfo.id") //@CachePut快取新增的或更新的資料到快取,其中快取名稱為people,資料的key是person的id。
    public UserInfo save(UserInfo userInfo) {
        UserInfo u = userInfoDao.save(userInfo);
        log.info("為userinfo的id(也是cache的key):" + u.getId() + "資料做了快取");
        return u;
    }

    @Override
    @CacheEvict(value = "userinfocache") //@CacheEvict從快取people中刪除key為id的資料。
    public void remove(Long id) {
        log.info("刪除了userinfo的id(也是cache的key):" + id + "資料快取");
        userInfoDao.delete(id);
    }

    @Override
    @Cacheable(value = "userinfocache",key="#userInfo.id") //@Cacheable快取key為person的id資料到快取people中。
    public UserInfo findOne(UserInfo userInfo) {
        UserInfo u = userInfoDao.findOne(userInfo.getId());
        log.info("為userinfo的id(也是cache的key):" + u.getId() + "資料做了快取");
        return u;
    }
}

//UserCacheController
@RestController
@RequestMapping("/userinfo")
public class UserCacheController {

    @Autowired
    UserInfoService userInfoService;

    @RequestMapping("/put")
    public UserInfo put(UserInfo userInfo){
        return userInfoService.save(userInfo);
    }

    @RequestMapping("/remove")
    public String remove(Long id){
        userInfoService.remove(id);
        return "success";
    }

    @RequestMapping("/cache")
    public UserInfo findone(UserInfo userInfo){
        return userInfoService.findOne(userInfo);
    }
}

//別忘了,啟動類中加上@EnableCaching
@SpringBootApplication
@EnableCaching
public class SpringbootcacheApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootcacheApplication.class, args);
    }
}