一、JSR107

Java Caching定义了5个核心接口,分别是CachingProvider, CacheManager, Cache, Entry 和 Expiry。
1、CachingProvider定义了创建、配置、获取、管理和控制多个CacheManager。
    一个应用可以在运行期访问多个CachingProvider。 
2、CacheManager定义了创建、配置、获取、管理和控制多个唯一命名的Cache,
    这些Cache存在于CacheManager的上下文中。一个CacheManager仅被一个CachingProvider所拥有。 
3、Cache是一个类似Map的数据结构并临时存储以Key为索引的值。一个Cache仅被一个CacheManager所拥有。 
4、Entry是一个存储在Cache中的key-value对。
5、 Expiry 每一个存储在Cache中的条目有一个定义的有效期。一旦超过这个时间,条目为过期的状态。
    一旦过期,条目将不可访问、更新和删除。缓存有效期可以通过ExpiryPolicy设置。

使用比较麻烦

二、Spring缓存抽象

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、从缓存中读取之前缓存存储的数据
 
 
基本环境搭建:
代码实践讲解:
新建工程:

public class Department {
private Integer id;
private String departmentName;
...}
public class Employee {
private Integer id;
private String lastName;
private String email;
private Integer gender; //性别 1男 0女
private Integer dId;
...}
public interface EmployeeMapper {

    @Select("SELECT * FROM employee WHERE id = #{id}")
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);
}
@Service
public class EmpService {
@Autowired
EmployeeMapper employeeMapper; public Employee getEmp(Integer id){
System.out.println("查询:" + id +"员工");
Employee emp = employeeMapper.getEmpById(id);
return emp;
}
}
@Controller
public class EmpController { @Autowired
EmpService empService; @ResponseBody
@RequestMapping("/emp/{id}")
public Employee getEmp(@PathVariable("id")Integer id){
Employee emp = empService.getEmp(id);
return emp;
}
}

测试:

使用缓存:

1.开启基于注解的缓存    @EnableCaching
2.标注缓存即可

在主程序中:开启缓存

@MapperScan("com.example.springbootcache.mapper")
@SpringBootApplication
@EnableCaching
public class SpringbootCacheApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootCacheApplication.class, args);
} }

此时页面连续请求两次:

控制台会打印两次,具体看EmpService类的查询方法

@Cacheable
public Employee getEmp(Integer id){
System.out.println("查询:" + id +"员工");
Employee emp = employeeMapper.getEmpById(id);
return emp;
}
@Cacheable:将方法运行的结果进行缓存,之后在要相同的数据,直接从缓存中取,不在调用方法
属性:
CacheManager管理多個Cache組件的,對緩存的真正CRUD操作在Cache組件中,每一個緩存組件有自己唯一一個名字;
幾個屬性:
   cacheName/value:指定緩存組件的名字;將方法的返回結果放在哪個緩存中是數組的方式,可以指定多個緩存
   key:緩存數據使用的key;可以用它來指定。默認是使用方法的參數   1-方法的返回值
       編寫SpEl; #id;參數id的值  #a0 #p0 #root.args[0]
   keyGenerator: key的生成器: 可以指定key的生成器的組件id
       key/keyGenerator: 二選一使用
       key = "#root.methodName + '['+id+']'"
   cacheManager: 指定緩存管理器;或者cacheResolver指定獲取解析器
   condition: 指定符合條件的情況下才緩存
       condition = "#id>0"
      condition = "#a0>1" 第一個參數>1的情況下進行緩存
   unless: 否定緩存;當ubless指定的條件爲true,方法的返回值就不會被緩存;可以獲取到結果進行判斷
       unless = "#result == null"
       #result爲方法的返回值
   sync:是否使用異步緩存

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

此时刷新很多遍依然只打印一次

三、缓存原理

@Configuration
@ConditionalOnClass({CacheManager.class})
@ConditionalOnBean({CacheAspectSupport.class})
@ConditionalOnMissingBean(
value = {CacheManager.class},
name = {"cacheResolver"}
)
@EnableConfigurationProperties({CacheProperties.class})
@AutoConfigureAfter({CouchbaseAutoConfiguration.class, HazelcastAutoConfiguration.class, HibernateJpaAutoConfiguration.class, RedisAutoConfiguration.class}) //给容器导入一些组件
@Import({CacheAutoConfiguration.CacheConfigurationImportSelector.class})
public class CacheAutoConfiguration {
... public String[] selectImports(AnnotationMetadata importingClassMetadata) {
CacheType[] types = CacheType.values();
String[] imports = new String[types.length]; for(int i = ; i < types.length; ++i) {
imports[i] = CacheConfigurations.getConfigurationClass(types[i]);
} return imports;
}
}
}

public String[] selectImports(AnnotationMetadata importingClassMetadata)中的return imports;

会给容器导入10个配置类

在配置文件中:判断那个缓存是生效的

debug=true

由图可知是SimpleCacheConfiguration 缓存类

@Configuration
@ConditionalOnMissingBean({CacheManager.class})
@Conditional({CacheCondition.class})
class SimpleCacheConfiguration {
private final CacheProperties cacheProperties;
private final CacheManagerCustomizers customizerInvoker; SimpleCacheConfiguration(CacheProperties cacheProperties, CacheManagerCustomizers customizerInvoker) {
this.cacheProperties = cacheProperties;
this.customizerInvoker = customizerInvoker;
} @Bean
public ConcurrentMapCacheManager cacheManager() {
ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
List<String> cacheNames = this.cacheProperties.getCacheNames();
if (!cacheNames.isEmpty()) {
cacheManager.setCacheNames(cacheNames);
} return (ConcurrentMapCacheManager)this.customizerInvoker.customize(cacheManager);
}
}
给容器中注册了一个缓存管理器ConcurrentMapCacheManager可以获取和创建

@Nullable
public Cache getCache(String name) {
Cache cache = (Cache)this.cacheMap.get(name);
if (cache == null && this.dynamic) {
ConcurrentMap var3 = this.cacheMap;
synchronized(this.cacheMap) {
cache = (Cache)this.cacheMap.get(name);
if (cache == null) {
cache = this.createConcurrentMapCache(name);
this.cacheMap.put(name, cache);
}
}
}
return cache;
}

按照名字取获取,如果为空则会创建一个缓存

运行流程:

@Cacheable
1.方法运行之前先去查询缓存组件,按照Cache的name取获取(CacheManager先获取相应的缓存)
    第一次获取缓存没有则会自动创建
2.取Cache中查找缓存,使用以可key,默认就是方法的参数
    key是按照某种策略生成的,默认使用keyGenerator,默认使用SimpleKeyGenerator

3.没有查到缓存就调用目标方法
4.将目标方法返回的结果放入缓存中
  @Cacheable标注的方法执行之前先来检查缓存中有没有这个数据,
  默认按照参数的值作为key取查询缓存
  如果没有就运行方法并且将结果放入缓存

其属性的使用:

key:数据库使用的key

此时的key就是 方法名+id;   即  getEmp[1]

keyGenerator:
    指定key
@Configuration
public class MyConfig {
@Bean("MyKeyGenerator")
public KeyGenerator keyGenerator(){
return new KeyGenerator() {
@Override
public Object generate(Object o, Method method, Object... objects) {
//Objects事获得其输入的cache中的属性值
return method.getName() +"[" + Arrays.asList(objects).toString()+"]";
}
};
}
}

断点位置:

可以看到debug进入时的值:

cacheManager:指定缓存管理器

condition:

@Cacheable(cacheNames ="e", keyGenerator = "MyKeyGenerator",condition = "#a0>1")

此时查询一次打印一次

23、springboot与缓存(1)的更多相关文章

  1. 带着新人学springboot的应用03(springboot+mybatis+缓存 下)

    springboot+mybatis+缓存,基本的用法想必是会了,现在说一说内部大概的原理. 稍微提一下mybatis,只要导入了mybatis的依赖,那么有个自动配置类就会生效,你可以去mybati ...

  2. SpringBoot 与缓存

    1. JSR107 Java Caching 定义了5个核心接口: CachingProvider:定义了创建,配置,获取,管理和控制多个CacheManager; CacheManager:定义了创 ...

  3. SpringBoot 整合缓存Cacheable实战详细使用

    前言 我知道在接口api项目中,频繁的调用接口获取数据,查询数据库是非常耗费资源的,于是就有了缓存技术,可以把一些不常更新,或者经常使用的数据,缓存起来,然后下次再请求时候,就直接从缓存中获取,不需要 ...

  4. springboot redis 缓存对象

    只要加入spring-boot-starter-data-redis , springboot 会自动识别并使用redis作为缓存容器,使用方式如下 gradle加入依赖 compile(" ...

  5. springboot~hazelcast缓存中间件

    缓存来了 在dotnet平台有自己的缓存框架,在java springboot里当然了集成了很多,而且缓存的中间件也可以进行多种选择,向redis, hazelcast都是分布式的缓存中间件,今天主要 ...

  6. 带着新人学springboot的应用02(springboot+mybatis+缓存 中)

    继续接着上一节,大家应该知道驼峰命名法吧!就是我们javabean中属性一般命名是lastName,userName这种类型的,而数据库中列名一般都是last_name,user_name这种的,要让 ...

  7. 带着新人学springboot的应用01(springboot+mybatis+缓存 上)

    上一篇结束,第一次做一个这么长的系列,很多东西我也是没有说到,也许是还没有想到,哈哈哈,不过基本的东西还是说的差不多了的.假如以后碰到了不会的,随便查查资料配置一下就ok. 咳,还有大家如果把我前面的 ...

  8. SpringBoot Redis缓存 @Cacheable、@CacheEvict、@CachePut

    文章来源 https://blog.csdn.net/u010588262/article/details/81003493 1. pom.xml <dependency> <gro ...

  9. springboot Redis 缓存

    1,先整合 redis 和 mybatis 步骤一: springboot 整合 redis 步骤二: springboot 整合 mybatis 2,启动类添加 @EnableCaching 注解, ...

随机推荐

  1. UbuntuServer 16.04 with LNMP搭建WordPress

    前几天弄了个腾讯云服务器,一时新鲜,就想着在上面搭建一个wordpress博客,前后搞了四五天,各种度娘谷歌,各种错误,不过还好,最终总算是被我搭建出来了!不啰嗦,书归正传,下面开始搭建! 目录: 一 ...

  2. JDBC数据库连接池

    用户每次请求都需要向数据库获得链接,而数据库创建连接通常需要消耗相对较大的资源,创建时间也较长.假设网站一天10万访问量,数据库服务器就需要创建10万次连接,极大的浪费数据库的资源,并且极易造成数据库 ...

  3. 洛谷P3384 树链剖分

    如题,已知一棵包含N个结点的树(连通且无环),每个节点上包含一个数值,需要支持以下操作: 操作1: 格式: 1 x y z 表示将树从x到y结点最短路径上所有节点的值都加上z 操作2: 格式: 2 x ...

  4. Linux(Ubuntu)下MySQL的安装

    1)首先检查系统中是否已经安装了MySQL 在终端里面输入 sudo netstat -tap | grep mysql 若没有反映,没有显示已安装结果,则没有安装.若如下显示,则表示已经安装 2)如 ...

  5. 关于构造器和Serlvet的知识点

    关于java的构造器方法: 1.java构造方法可以有任何的访问修饰符:public.protected.private或者没有修饰(通常被package或者friendly调用)但是不能有非访问性质 ...

  6. alembic 数据库管理

    alembic简介 Alembic是SQLAlchemy作者编写的Python数据库迁移工具 安装 pip install alembic alembic 操作流程 初始化 alembic init ...

  7. 闭包&执行环境和作用域

    闭包 执行环境和作用域参考:<javascript高级程序设计(第3版)>4.2节

  8. 毕向东_Java基础视频教程第19天_IO流(18~19)

    第19天-18-IO流(流操作规律 - 1) 通过三个步骤来明确"流操作"的规律: 明确数据流的"源和目的" 源, 输入流: InputStream/Reade ...

  9. Word操作总结

    1.竖向选择 Notepad中:先把鼠标光标放在起始位置,然后同时按 Alt+Ctrl 或Alt+shift键,然后移动鼠标选取内容. Word中只能用Alt+Shift .

  10. java获取文件大小的方法

    目前Java获取文件大小的方法有两种: 1.通过file的length()方法获取: 2.通过流式方法获取: 通过流式方法又有两种,分别是旧的java.io.*中FileInputStream的ava ...