SpringBoot+缓存

1Spring缓存抽象

Spring从3.1开始定义了org.springframework.cache.Cache和org.springframework.cache.CacheManager接口来统一不同的缓存技术;
并支持使用JCache(JSR-107)注解简化我们开发;

Cache接口为缓存的组件规范定义,包含缓存的各种操作集合;
Cache接口下Spring提供了各种xxxCache的实现;如RedisCache,EhCacheCache , ConcurrentMapCache等;

每次调用需要缓存功能的方法时,Spring会检查检查指定参数的指定的目标方法是否已经被调用过;如果有就直接从缓存中获取方法调用后的结果,如果没有就调用方法并缓存结果后返回给用户。下次调用直接从缓存中获取。
 

使用Spring缓存抽象时我们需要关注以下两点;
1、确定方法需要被缓存以及他们的缓存策略
2、从缓存中读取之前缓存存储的数据

 

2几个重要概念&缓存注解

 

3@Cacheable/@CachePut/@CacheEvict 主要的参数

 

4Cache SpEL available metadata

 

5缓存使用

 原理:
   1、自动配置类;CacheAutoConfiguration
   2、缓存的配置类
   org.springframework.boot.autoconfigure.cache.GenericCacheConfiguration
   org.springframework.boot.autoconfigure.cache.JCacheCacheConfiguration
   org.springframework.boot.autoconfigure.cache.EhCacheCacheConfiguration
   org.springframework.boot.autoconfigure.cache.HazelcastCacheConfiguration
   org.springframework.boot.autoconfigure.cache.InfinispanCacheConfiguration
   org.springframework.boot.autoconfigure.cache.CouchbaseCacheConfiguration
   org.springframework.boot.autoconfigure.cache.RedisCacheConfiguration
   org.springframework.boot.autoconfigure.cache.CaffeineCacheConfiguration
   org.springframework.boot.autoconfigure.cache.GuavaCacheConfiguration
   org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration【默认】
   org.springframework.boot.autoconfigure.cache.NoOpCacheConfiguration
   3、哪个配置类默认生效:SimpleCacheConfiguration;

   4、给容器中注册了一个CacheManager:ConcurrentMapCacheManager
   5、可以获取和创建ConcurrentMapCache类型的缓存组件;他的作用将数据保存在ConcurrentMap中;

   运行流程:(跟踪这个类ConcurrentMapCacheManager)
   @Cacheable:
   1、方法运行之前,先去查询Cache(缓存组件),按照cacheNames指定的名字获取;
      (CacheManager先获取相应的缓存),第一次获取缓存如果没有Cache组件会自动创建。
   2、去Cache中查找缓存的内容,使用一个key,默认就是方法的参数;
      key是按照某种策略生成的;默认是使用keyGenerator生成的,默认使用SimpleKeyGenerator生成key;
          SimpleKeyGenerator生成key的默认策略;
                  如果没有参数;key=new SimpleKey();
                  如果有一个参数:key=参数的值
                  如果有多个参数:key=new SimpleKey(params);
   3、没有查到缓存就调用目标方法;
   4、将目标方法返回的结果,放进缓存中

   @Cacheable标注的方法执行之前先来检查缓存中有没有这个数据,默认按照参数的值作为key去查询缓存,
   如果没有就运行方法并将结果放入缓存;以后再来调用就可以直接使用缓存中的数据;

   核心:
      1)、使用CacheManager【ConcurrentMapCacheManager】按照名字得到Cache【ConcurrentMapCache】组件
      2)、key使用keyGenerator生成的,默认是SimpleKeyGenerator

 

6 ConcurrentMapCache缓存

默认使用的是ConcurrentMapCacheManager==ConcurrentMapCache;将数据保存在 ConcurrentMap<Object, Object>

 

代码结构

基本代码

 

package com.feizhou.cache.bean;

import java.io.Serializable;


public class Employee implements Serializable {
	
	private Integer id;
	private String lastName;
	private String email;
	private Integer gender; //性别 1男  0女
	private Integer dId;
	
	
	public Employee() {
		super();
	}

	
	public Employee(Integer id, String lastName, String email, Integer gender, Integer dId) {
		super();
		this.id = id;
		this.lastName = lastName;
		this.email = email;
		this.gender = gender;
		this.dId = 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 getEmail() {
		return email;
	}
	public void setEmail(String email) {
		this.email = email;
	}
	public Integer getGender() {
		return gender;
	}
	public void setGender(Integer gender) {
		this.gender = gender;
	}
	public Integer getdId() {
		return dId;
	}
	public void setdId(Integer dId) {
		this.dId = dId;
	}
	@Override
	public String toString() {
		return "Employee [id=" + id + ", lastName=" + lastName + ", email=" + email + ", gender=" + gender + ", dId="
				+ dId + "]";
	}
	
	

}
-----------------------

package com.feizhou.cache.controller;

import com.feizhou.cache.bean.Employee;
import com.feizhou.cache.service.EmployeeService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class EmployeeController {

    @Autowired
    EmployeeService employeeService;

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


}
---------------------------------
package com.feizhou.cache.mapper;

import com.feizhou.cache.bean.Employee;
import org.apache.ibatis.annotations.*;

@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);

    @Delete("DELETE FROM employee WHERE id=#{id}")
    public void deleteEmpById(Integer id);

    @Insert("INSERT INTO employee(lastName,email,gender,d_id) VALUES(#{lastName},#{email},#{gender},#{dId})")
    public void insertEmployee(Employee employee);

    @Select("SELECT * FROM employee WHERE lastName = #{lastName}")
    Employee getEmpByLastName(String lastName);
}


---------------------------------


package com.feizhou.cache;

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

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

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

-----------------application.properties----------------

spring.datasource.url=jdbc:mysql://localhost:3306/spring_cache
spring.datasource.username=root
spring.datasource.password=root

# 开启驼峰命名匹配规则
mybatis.configuration.map-underscore-to-camel-case=true
# 打开缓存日志
logging.level.com.feizhou.cache.mapper=debug

debug=true


---------------------------------


---------------------------------

测试代码Cacheable

package com.feizhou.cache.service;

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

@Service
public class EmployeeService {

    @Autowired
    EmployeeMapper employeeMapper;



    /**
     * 将方法的运行结果进行缓存;以后再要相同的数据,直接从缓存中获取,不用调用方法;
     * CacheManager管理多个Cache组件的,对缓存的真正CRUD操作在Cache组件中,每一个缓存组件有自己唯一一个名字;
     *

     *
     * 原理:
     *
     *   几个属性:
     *      cacheNames/value:指定缓存组件的名字;将方法的返回结果放在哪个缓存中,是数组的方式,可以指定多个缓存;
     *      案例: @Cacheable(cacheNames={"emp","emp2"},key ="#id")
     *
     *
     *      key:缓存数据使用的key;可以用它来指定。默认是使用方法参数的值  1-方法的返回值
     *              编写SpEL; #id;参数id的值   #a0  #p0  #root.args[0]
     *       案例: @Cacheable(cacheNames={"emp","emp2"},key ="#id")
     *
     *
     *
     *      keyGenerator:key的生成器;可以自己指定key的生成器的组件id, key/keyGenerator:二选一使用;
     *       案例: @Cacheable(value = {"emp"},keyGenerator = "myKeyGenerator")
     *
     *
     *      cacheManager:指定缓存管理器;或者cacheResolver指定获取解析器
     *
     *
     *
     *      condition:指定符合条件的情况下才缓存;
     *      案例:  @Cacheable(cacheNames="emp",key ="#id" ,condition = "#id>0")
     *
     *
     *
     *      unless:否定缓存;当unless指定的条件为true,方法的返回值就不会被缓存;可以获取到结果进行判断
     *      案例:  @Cacheable(cacheNames="emp",key ="#id" , unless ="#id == null")
     *                 #id==null,就不缓存
     *
     *
     *     sync:是否使用异步模式
     * @param id
     * @return
     *
     */



   @Cacheable(cacheNames="emp")
    public Employee getEmp(Integer id){
        System.out.println("查询"+id+"号员工");
        Employee emp = employeeMapper.getEmpById(id);
        return emp;
    }



}

1测试一般查询

第1次请求

第2次请求(没有查sql)

测试生成key

新增一个配置

测试

2@CachePut:既调用方法,又更新缓存数据;同步更新缓存

核心代码

测试

查询

还是去缓存查了,这是因为查询和更新的缓存key不一样

修改代码

测试

3 @CacheEvict:缓存清除

 

测试

清空缓存

查sql

 

4 @Caching 定义复杂的缓存规则

测试

 

 

查询其他

7redis缓存

1docker安装redis

https://hub.docker.com/_/redis/

 

镜像加速

docker pull registry.docker-cn.com/library/镜像名称

[root@bogon ~]# docker pull registry.docker-cn.com/library/redis

如果不使用镜像加速

[root@bogon ~]# docker pull redis

 

[root@bogon ~]# docker pull redis
Using default tag: latest
Trying to pull repository docker.io/library/redis ...
latest: Pulling from docker.io/library/redis
fc7181108d40: Pull complete
3e0ac67cad82: Pull complete
6ee495cb7235: Pull complete
9f7206d08b9d: Pull complete
a8354ef8cccb: Pull complete
53afb10d81c2: Pull complete
Digest: sha256:ca2d9251c2818df48c6598e01a5bdeab46040dc7ab236abe075d7c7343465177
Status: Downloaded newer image for docker.io/redis:latest
[root@bogon ~]# docker ps
CONTAINER ID        IMAGE               COMMAND             CREATED             STATUS              PORTS               NAMES
[root@bogon ~]# docker images
REPOSITORY          TAG                 IMAGE ID            CREATED             SIZE
docker.io/tomcat    latest              5377fd8533c3        2 weeks ago         506 MB
docker.io/redis     latest              3c41ce05add9        2 weeks ago         95 MB
[root@bogon ~]# docker  run -d -p 6379:6379 --name myredis docker.io/redis
f9233d3b3cf4077d024d531c00ed624c8c7341d544c7bc3d4dc8fd45db247e7b
[root@bogon ~]# docker ps
CONTAINER ID        IMAGE               COMMAND                  CREATED             STATUS              PORTS                    NAMES
f9233d3b3cf4        docker.io/redis     "docker-entrypoint..."   7 seconds ago       Up 5 seconds        0.0.0.0:6379->6379/tcp   myredis

测试

2项目中怎么引入redis(只要pom添加下面jar就行,什么的代码就变成redis了)

代码测试

package com.feizhou.cache;

import com.feizhou.cache.bean.Employee;
import com.feizhou.cache.mapper.EmployeeMapper;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.test.context.junit4.SpringRunner;

@RunWith(SpringRunner.class)
@SpringBootTest
public class Springboot01CacheApplicationTests {
    @Autowired
    EmployeeMapper employeeMapper;

    @Autowired
    StringRedisTemplate stringRedisTemplate;  //操作k-v都是字符串的

    @Autowired
    RedisTemplate redisTemplate;  //k-v都是对象的




    /**
     * 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(){
        //给redis中保存数据
        stringRedisTemplate.opsForValue().append("msg","hello");
		String msg = stringRedisTemplate.opsForValue().get("msg");
		System.out.println(msg);

    }




    @Test
    public void contextLoads() {

//        Employee empById = employeeMapper.getEmpById(1);
//        System.out.println(empById);

    }

}

测试保存序列化对象

 

测试保存josn对象

 


 

3CacheManagerCustomizers可以来定制缓存的一些规则

//CacheManagerCustomizers可以来定制缓存的一些规则
@Primary  //将某个缓存管理器作为默认的
@Bean
public RedisCacheManager employeeCacheManager(RedisTemplate<Object, Employee> empRedisTemplate){
    RedisCacheManager cacheManager = new RedisCacheManager(empRedisTemplate);
    //key多了一个前缀

    //使用前缀,默认会将CacheName作为key的前缀
    cacheManager.setUsePrefix(true);

    return cacheManager;
}

这里就不一一测试了,上面最初缓存的代码,在redis都可以用,截图结果如下

测试数据

代码位置

https://gitee.com/DanShenGuiZu/GongKaiZiYuan.git

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值