SpringBoot(七) SpringBoot中的緩存機制


隨着時間的積累,應用的使用用戶不斷增加,數據規模也越來越大,往往數據庫查詢操作會成為影響用戶使用體驗的瓶頸,此時使用緩存往往是解決這一問題非常好的手段之一。Spring 3開始提供了強大的基於注解的緩存支持,可以通過注解配置方式低侵入的給原有Spring應用增加緩存功能,提高數據訪問性能。

SpringBoot中的Cache緩存

1、基本概念

Spring從3.1開始定義了org.springframework.cache.Cache和org.springframework.cache.CacheManager接口來統一不同的緩存技術;同時支持JCache(JSR-107)注解。

Cache 緩存接口,定義緩存操作,實現有:RedisCache、EhCacheCache、ConcurrentMapCache等
CacheManager 緩存管理器,管理各種緩存(Cache)組件
@Cacheable 針對方法配置,根據方法的請求參數對其結果進行緩存
@CacheEvict 清空緩存
@CachePut 保證方法被調用,又希望結果被緩存 update,調用,將信息更新緩存
@EnableCaching 開啟基於注解的緩存
KeyGenerator 緩存數據時key生成的策略
serialize 緩存數據時value序列化策略

2、整合項目

1、新建一個SpringBoot1.5+web+mysql+mybatis+cache

2、編寫配置文件,連接Mysql

spring.datasource.driver-class-name=com.mysql.jdbc.Driver
spring.datasource.url=jdbc:mysql://192.168.1.125:3306/test01
spring.datasource.username=root
spring.datasource.password=root
mybatis.configuration.map-underscore-to-camel-case=true
server.port=9000

3、創建JaveBean實例

public class Employee {
    private Integer id;
    private String lastName;
    private String gender;
    private String email;
    private Integer dId;

    public Integer getId() {
        return id;
    }

    public void setId(Integer id) {
        this.id = id;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getGender() {
        return gender;
    }

    public void setGender(String gender) {
        this.gender = gender;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public Integer getdId() {
        return dId;
    }

    public void setdId(Integer dId) {
        this.dId = dId;
    }

    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", lastName='" + lastName + '\'' +
                ", gender='" + gender + '\'' +
                ", email='" + email + '\'' +
                ", dId=" + dId +
                '}';
    }
}

4、創建mapper接口映射數據庫,並訪問數據庫中的數據

import com.wdjr.cache.bean.Employee;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;
import org.apache.ibatis.annotations.Update;

@Mapper
public interface EmployeeMapper {

    @Select("SELECT * FROM employee WHERE id = #{id}")
    public Employee getEmpById(Integer id);
    @Update("UPDATE employee SET lastName=#{lastName},email=#{email},gender=#{gender},d_id=#{dId} WHERE id=#{id}")
    public void updateEmp(Employee employee);
}

 

5、在pom.xml中引入cache依賴

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

 

6、主程序添加注解MapperScan,並且使用@EnableCaching,開啟緩存功能

import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cache.annotation.EnableCaching;

@EnableCaching
@MapperScan("com.wdjr.cache.mapper")
@SpringBootApplication
public class Springboot01CacheApplication {

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

7、編寫service,來具體實現mapper中的方法,使用@Cacheable增加緩存

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

@Service
public class EmployeeService {

    @Autowired
    EmployeeMapper employeeMapper;

    /**
     * 將方法的運行結果進行緩存,以后要是再有相同的數據,直接從緩存中獲取,不用調用方法
     * CacheManager中管理多個Cache組件,對緩存的真正CRUD操作在Cache組件中,每個緩存組件都有自己的唯一名字;
     *
     * 屬性:
     *  CacheName/value:指定存儲緩存組件的名字
     *  key:緩存數據使用的key,可以使用它來指定。默認是使用方法參數的值,或方法的返回值
     *  編寫Spel表達式:#id 參數id的值, #a0/#p0 #root.args[0]
     *  keyGenerator:key的生成器,自己可以指定key的生成器的組件id
     *  key/keyGendertor二選一使用
     *
     *  cacheManager指定Cache管理器,或者cacheReslover指定獲取解析器
     *  condition:指定符合條件的情況下,才緩存;
     *  unless:否定緩存,unless指定的條件為true,方法的返回值就不會被緩存,可以獲取到結果進行判斷
     *  sync:是否使用異步模式,unless不支持
     *
     *
     */
    @Cacheable(cacheNames = {"emp"},key = "#id",condition = "#id>0",unless = "#result==null")
    public Employee getEmp(Integer id){
        System.out.println("查詢id= "+id+"的員工");
        return employeeMapper.getEmpById(id);
    }
}

8、編寫controller測試

@RestController
public class EmployeeController {
    @Autowired
    EmployeeService employeeService;

    @GetMapping("/emp/{id}")
    public Employee getEmp(@PathVariable("id")Integer id){
        return employeeService.getEmp(id);
    }
}

 

9、查看結果。具體結果就不截圖了,緩存功能開啟, 如果條件相同,只會查詢一次數據庫。

Cache注解

  • @CacheConfig:主要用於配置該類中會用到的一些共用的緩存配置。在這里@CacheConfig(cacheNames = "users"):配置了該數據訪問對象中返回的內容將存儲於名為users的緩存對象中,我們也可以不使用該注解,直接通過@Cacheable自己配置緩存集的名字來定義。
    •   該注解是整合了Cacheable /Cacheput / CacheEvict。設置在類上,該類的方法上面的cache注解均不用設置name。
  • @Cacheable:配置了findByName函數的返回值將被加入緩存。同時在查詢時,會先從緩存中獲取,若不存在才再發起對數據庫的訪問。該注解主要有下面幾個參數:
    • valuecacheNames:兩個等同的參數(cacheNames為Spring 4新增,作為value的別名),用於指定緩存存儲的集合名。由於Spring 4中新增了@CacheConfig,因此在Spring 3中原本必須有的value屬性,也成為非必需項了
    • key:緩存對象存儲在Map集合中的key值,非必需,缺省按照函數的所有參數組合作為key值,若自己配置需使用SpEL表達式,比如:@Cacheable(key = "#p0"):使用函數第一個參數作為緩存的key值,更多關於SpEL表達式的詳細內容可參考官方文檔
    • condition:緩存對象的條件,非必需,也需使用SpEL表達式,只有滿足表達式條件的內容才會被緩存,比如:@Cacheable(key = "#p0", condition = "#p0.length() < 3"),表示只有當第一個參數的長度小於3的時候才會被緩存,若做此配置上面的AAA用戶就不會被緩存,讀者可自行實驗嘗試。
    • unless:另外一個緩存條件參數,非必需,需使用SpEL表達式。它不同於condition參數的地方在於它的判斷時機,該條件是在函數被調用之后才做判斷的,所以它可以通過對result進行判斷。
    • keyGenerator:用於指定key生成器,非必需。若需要指定一個自定義的key生成器,我們需要去實現org.springframework.cache.interceptor.KeyGenerator接口,並使用該參數來指定。需要注意的是:該參數與key是互斥的
    • cacheManager:用於指定使用哪個緩存管理器,非必需。只有當有多個時才需要使用
    • cacheResolver:用於指定使用那個緩存解析器,非必需。需通過org.springframework.cache.interceptor.CacheResolver接口來實現自己的緩存解析器,並用該參數指定。

除了上面的兩個注解外,還有一些其他的注解。

  • @Cacheput:既調用方法,又更新緩存數據。也就是修改數據庫的某個數據,同時更新緩存。步驟:先運行方法,再將目標結果緩存起來。
    • 如果需要使用該注解,不僅需要加入注解,還需要設置注解的key的值需要和查詢出來的key一致,否則不會改變cache緩存中的數據。
  • @CacheEvict:清除緩存。使用key刪除緩存數據。
    • 特有的兩個屬性:
      • allEntries = true,代表不論清除哪個key,都重新刷新緩存
      • beforeInvocation=true.方法執行前,清空緩存,無論發不發生異常,都會清除緩存。false則是方法執行后,情況緩存。默認是false,如果程序異常,就不會清除緩存。

使用Redis做集中式緩存

默認的緩存是在內存中定義HashMap,在實際的開發生產中,經常使用Redis作為緩存中間件,而不使用cache。

Redis 是一個開源(BSD許可)的,內存中的數據結構存儲系統,它可以用作數據庫、緩存和消息中間件。也是以key-value的形式進行存儲數據的一款非關系型數據庫。

步驟:

1、安裝Redis:將安裝redis在虛擬機中(docker)。  推薦去docker中國中去下載。

#拉取redis鏡像
docker pull redis
#啟動redis,並對外發開,使外部可以連接到虛擬機中的rdeis
 docker run -d -p 6379:6379 --name redis01 bfcb1f6df2db

 安裝后可以使用 Redis Desktop Manager管理工具去管理redis。

2、Redis的Tempate

Redis的常用五大數據類型

String【字符串】、List【列表】、Set【集合】、Hash【散列】、ZSet【有序集合】

分為兩種一種是StringRedisTemplate,另一種是RedisTemplate

根據不同的數據類型,大致的操作也分為這5種,以StringRedisTemplate為例

tringRedisTemplate.opsForValue()  --String
stringRedisTemplate.opsForList()  --List
stringRedisTemplate.opsForSet()  --Set
stringRedisTemplate.opsForHash()  --Hash
stringRedisTemplate.opsForZset()  -Zset

  a.導入Redis依賴

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

 

 b.修改配置文件

spring.redis.host=192.168.1.125

 

   3.添加測試類

    @Autowired
    StringRedisTemplate stringRedisTemplate;//操作字符串【常用】

    @Autowired
    RedisTemplate redisTemplate;//操作k-v都是對象    
    @Test
    public void test01(){
//        stringRedisTemplate.opsForValue().append("msg", "hello");
        String msg = stringRedisTemplate.opsForValue().get("msg");
        System.out.println(msg);
    } 

我們需要做的配置和測試到這里就已經完成了。

並且Spring Boot會在偵測到存在Redis的依賴並且Redis的配置是可用的情況下,還可以使用RedisCacheManager初始化CacheManager。

3、保存對象

    • 如果要直接保存對象,需要使用JDK序列化機制,現將對象序列化后再近些保存Redis中。
    • 將對象轉換成JSON格式,存儲到Redis中。
      • 第一種方式:自己將對象轉換成JSON。(fastJson)
      • 第二種方式:新建一個Redis的配置類MyRedisConfig。(redisTemplate中默認改變的序列化規則)
@Configuration
public class MyRedisConfig {
    @Bean
    public RedisTemplate<Object, Employee> empRedisTemplate(
            RedisConnectionFactory redisConnectionFactory)
            throws UnknownHostException {
        RedisTemplate<Object, Employee> template = new RedisTemplate<Object, Employee>();
        template.setConnectionFactory(redisConnectionFactory);
        Jackson2JsonRedisSerializer<Employee> jsonRedisSerializer = new Jackson2JsonRedisSerializer<Employee>(Employee.class);
        template.setDefaultSerializer(jsonRedisSerializer);
        return template;
    }

 


免責聲明!

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



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