Springboot整合篇---Springboot缓存---01/02/03/04/05/06/07/08/09

SpringBoot与缓存:

         什么时候用缓存:电商网站的高频数据,比如商品信息,不需要直接查找数据库了。

         临时数据比如验证码就不放在数据库放在缓存里面。

        

      上面是JSR107缓存规范。

       得到缓存管理器,获得缓存组件,将key-value记录进去。

       

      缓存机制。

           首先是应用程序调用缓存的提供者,缓存的提供者管理多个缓存的管理器,管理器里面有多个缓存是Entry数组。

      CacheManager和Cache就像是连接池和连接一样。

      使用JSR107:

    导入这个包。

   Cache组件里面提供了创建和获得缓存组件的方法。同时也提供了crud的方法。都是接口面向接口编程。

  简化开发提供了自己的缓存的抽象。

1--------------------------------------------------------------------------------------------------------20190717

   Spring的缓存抽象.

        

        Cache是接口,学习下springboot提供的几个重要的接口和注解:

                 

                     一般在删除的方法上标注CacheEvict,用户删除缓存删除。

                     @CachePut更新缓存:updataUser方法,更新用户,传入用户信息,缓存里面的数据也缓存。总是会被调用。

                     @EnbleCaching开启缓存机制。

                     其他的注解是序列化的策略。

2-------------------------------------------------------------------------------------------------------------------------------------------------20190717

      基本的环境的搭建:

              代码github地址:https://github.com/FandyWw/springboot-01-cache已失效看这个去电脑找代码

              

              

              

程序:

1.配置数据源

spring.datasource.url=jdbc:mysql://192.168.224.130:3306/spring_cache
spring.datasource.username=root
spring.datasource.password=123456
#spring.datasource.driver-class-name=com.mysql.jdbc.Driver
#上面这一行可以省略springboot会自动判断的
#10.211.55.9

2.配置扫描的mapper包,写mapper,写在主配置类上的。

@MapperScan("com.atguigu.cache.mapper")

3.在路径下写mapper

package com.atguigu.cache.mapper;

import com.atguigu.cache.bean.Department;
import org.apache.ibatis.annotations.Mapper;
import org.apache.ibatis.annotations.Select;

@Mapper
public interface DepartmentMapper {

    @Select("SELECT * FROM department WHERE id = #{id}")
    Department getDeptById(Integer id);
}
package com.atguigu.cache.mapper;

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

@Mapper
//显示声明十mybatis的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);
}

 单元测试:

              

@Autowired
	EmployeeMapper employeeMapper;

@Test
	public void contextLoads() {

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

	}

      补全controller和service

       

@Autowired
    EmployeeMapper employeeMapper;
public Employee getEmp(Integer id){
        System.out.println("查询"+id+"号员工");
        Employee emp = employeeMapper.getEmpById(id);
        return emp;
    }
package com.atguigu.cache.controller;

import com.atguigu.cache.bean.Employee;
import com.atguigu.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.PostMapping;
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;
    }

     启动测试:

    注意一个问题:程序里面是dId到那时数据库是d_id要开启驼峰命名法。

   

# 开启驼峰命名匹配规则
mybatis.configuration.map-underscore-to-camel-case=true

   网址:localhost:8080/emp/1         

  

 3-------------------------------------------------------------------------------------------------------------------------------------------------20190707---

  快速体验缓存:

/**
 * 一、搭建基本环境
 * 1、导入数据库文件 创建出department和employee表
 * 2、创建javaBean封装数据
 * 3、整合MyBatis操作数据库
 * 		1.配置数据源信息
 * 		2.使用注解版的MyBatis;
 * 			1)@MapperScan指定需要扫描的mapper接口所在的包
 * 二、快速体验缓存
 * 		步骤:
 * 			1、开启基于注解的缓存 @EnableCaching
 * 			2、标注缓存注解即可
 * 				@Cacheable  将方法的运行结果缓存  查
 * 				@CacheEvict 删除
 * 				@CachePut  修改
 * 默认使用的是ConcurrentMapCacheManager==ConcurrentMapCache;将数据保存在	ConcurrentMap<Object, Object>中
 * 开发中使用缓存中间件;redis、memcached、ehcache;
 * 三、整合redis作为缓存
 * Redis 是一个开源(BSD许可)的,内存中的数据结构存储系统,它可以用作数据库、缓存和消息中间件。
 * 	1、安装redis:使用docker;
 * 	2、引入redis的starter
 * 	3、配置redis
 * 	4、测试缓存
 * 		原理:CacheManager===Cache 缓存组件来实际给缓存中存取数据
 *		1)、引入redis的starter,容器中保存的是 RedisCacheManager;
 *		2)、RedisCacheManager 帮我们创建 RedisCache 来作为缓存组件;RedisCache通过操作redis缓存数据的
 *		3)、默认保存数据 k-v 都是Object;利用序列化保存;如何保存为json
 *   			1、引入了redis的starter,cacheManager变为 RedisCacheManager;
 *   			2、默认创建的 RedisCacheManager 操作redis的时候使用的是 RedisTemplate<Object, Object>
 *   			3、RedisTemplate<Object, Object> 是 默认使用jdk的序列化机制
 *      4)、自定义CacheManager;
 *
 */

 开启基于注解的缓存,用的话必须先开启缓存:

package com.atguigu.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.atguigu.cache.mapper")
@EnableCaching
@SpringBootApplication
public class CacheApplication {

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

}

     课堂讲解:

       

//打印这个包下面mooer的所有的日志
logging.level.com.atguigu.cache.mapper=debug

  打印日志要在配置文件配置这个。

2019-04-09 16:41:39.849  INFO 9628 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Initializing Servlet 'dispatcherServlet'
2019-04-09 16:41:39.857  INFO 9628 --- [nio-8080-exec-1] o.s.web.servlet.DispatcherServlet        : Completed initialization in 8 ms
查询1号员工
2019-04-09 16:41:39.906  INFO 9628 --- [nio-8080-exec-1] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Starting...
2019-04-09 16:41:40.554  INFO 9628 --- [nio-8080-exec-1] com.zaxxer.hikari.HikariDataSource       : HikariPool-1 - Start completed.
2019-04-09 16:41:40.563 DEBUG 9628 --- [nio-8080-exec-1] c.a.c.mapper.EmployeeMapper.getEmpById   : ==>  Preparing: SELECT * FROM employee WHERE id = ? 
2019-04-09 16:41:40.596 DEBUG 9628 --- [nio-8080-exec-1] c.a.c.mapper.EmployeeMapper.getEmpById   : ==> Parameters: 1(Integer)
2019-04-09 16:41:40.653 DEBUG 9628 --- [nio-8080-exec-1] c.a.c.mapper.EmployeeMapper.getEmpById   : <==      Total: 1

  在方法上加入缓存:可以指定多个缓存的名字{"emp","temp"}   a0,p0,ai,pi也是代表的参数的索引。

 @Cacheable(value = {"emp"}/*,keyGenerator = "myKeyGenerator",condition = "#a0>1",unless = "#a0==2"*/)
    public Employee getEmp(Integer id){
        System.out.println("查询"+id+"号员工");
        Employee emp = employeeMapper.getEmpById(id);
        return emp;
    }

 

 SPEL表达式:

     

缓存的解释这个注释好好看下:

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

     *
     * 原理:
     *   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中;
     *
     *   运行流程:
     *   @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
     *
     *
     *   几个属性:
     *      cacheNames/value:指定缓存组件的名字;将方法的返回结果放在哪个缓存中,是数组的方式,可以指定多个缓存;
     *
     *      key:缓存数据使用的key;可以用它来指定。key默认是使用方法参数的值就是传的值-value是方法的返回值就是存入后的返回值
     *              编写SpEL; #id;取出参数id的值   #a0取出第一个参数  #p0取出第一个参数  #root.args[0]取出第一个参数 
     *           
     *
     *      keyGenerator:key的生成器;可以自己指定key的生成器的组件id
     *              key/keyGenerator:二选一使用;
     *
     *
     *      cacheManager:指定缓存管理器,就是两个缓存管理器都有一样的缓存;或者cacheResolver指定获取解析器也是二选一
     *
     *      condition:指定符合条件的情况下才缓存;
     *              ,condition = "#id>0"
     *          condition = "#a0>1":第一个参数的值》1的时候才进行缓存
     *
     *      unless:否定缓存;当unless指定的条件为true,方法的返回值就不会被缓存;可以获取到结果进行判断
     *              unless = "#result == null"
     *              unless = "#a0==2":如果第一个参数的值是2,结果不缓存;
     *      sync:是否使用异步模式
     * @param id
     * @return
     *
     */

指定缓存管理器,就是两个缓存管理器都有一样的缓存

@Cacheable源码:  注解类的属性可以用spel表达式进行赋值。

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//

package org.springframework.cache.annotation;

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
import org.springframework.core.annotation.AliasFor;

@Target({ElementType.METHOD, ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Cacheable {
    @AliasFor("cacheNames")
    String[] value() default {};

    @AliasFor("value")
    String[] cacheNames() default {};//缓存的唯一的名字

    String key() default "";//缓存数据使用的key 默认是方法参数的值 值是方法的返回值 可以使用spel表达式编写

    String keyGenerator() default "";//自己指定key的生成器的组件id key与这个二选一

    String cacheManager() default "";//指定缓存管理器redis还是普通的

    String cacheResolver() default "";//缓存解析器 和上面的二选一

    String condition() default "";//判断条件

    String unless() default "";//判断条件  可以对获取的结果判断

    boolean sync() default false;//异步?
}

原理图解:

总结下重要的知识点:

   cache是ConcurrentMap。

   cache:https://blog.csdn.net/qq_38023748/article/details/89188337

   cachemanager是ConcurrentHashMap,因为有许多的cache,每个cache有自己的名字,不同的cachemanager的cache名字可以是一样的。

细节:

指定key或者key="#id的值“

其他spel表达式:

     #id为参数id的值#a0 #p0 #root.args[0]

     

指定什么条件下用缓存:

@Cacheable(value = {"emp"},condition = "#a0>1",unless = "#a0==2")
    public Employee getEmp(Integer id){
        System.out.println("查询"+id+"号员工");
        Employee emp = employeeMapper.getEmpById(id);
        return emp;
    }

测试:

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

     

    unless是可以用返回值判断的。

4---------------------------------------------------------------------------------------------------------------------------------------------------20190717

      重点:缓存的工作原理和工作步骤: 

                 1.自动配置类入手---

                    

                 代码:

@Import({CacheAutoConfiguration.CacheConfigurationImportSelector.class})

  点进去看源码调用这个方法会给容器导入缓存用到的组件:

        

打断点调试下:

查看到返回的imports的所有的缓存的配置:

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

   这些都是配置类,是有顺序的。点进去上面的任意一个配置类看源码是有条件运行的。

    在配置文件中加入

debug=true

   打印自动配置报告:

SimpleCacheConfiguration matched:
      - Cache org.springframework.boot.autoconfigure.cache.SimpleCacheConfiguration automatic cache type (CacheCondition)
      - @ConditionalOnMissingBean (types: org.springframework.cache.CacheManager; SearchStrategy: all) did not find any beans (OnBeanCondition)

只有这个匹配了,默认生效。

 进入这个类这个方法,给容器中注册了一个cacheManager---ConcurrentMapCacheManager:

@Bean
	public ConcurrentMapCacheManager cacheManager() {
		ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
		List<String> cacheNames = this.cacheProperties.getCacheNames();
		if (!cacheNames.isEmpty()) {
			cacheManager.setCacheNames(cacheNames);
		}
		return this.customizerInvoker.customize(cacheManager);
	}

   发现其给容器中注册了一个ConcurrentMapCacheManager

   内部是ConcurrentHashMap  里面存的是n个<name,Cache>。

   进入ConcurrentMapCacheManager 

   

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

 点进去createConcurrentMapCache:

	protected Cache createConcurrentMapCache(String name) {
		SerializationDelegate actualSerialization = (isStoreByValue() ? this.serialization : null);
		return new ConcurrentMapCache(name, new ConcurrentHashMap<Object, Object>(256),
				isAllowNullValues(), actualSerialization);

	}

这个是ConcurrentMap就是Cache里面是ConcurrentMap

  可以获取和创建ConcurrentMapCache类型缓存组件,作用是将数据保存在ConcurrentMap(看这个容器的源码)。

  点进去ConcurrentMapCache

  

  

   cache将数据保存在ConcurrentMap(cache)中。

  ------------------------------------缓存的运行流程----原理:

    以Cacheable为例:
    @Cacheable:第一次是null

                

                 

                第一次为null的时候,注意这个emp是cache的名字。

                

       注意没有的话会创建的。
                1、目标方法运行之前,先去查询Cache(缓存组件),按照cacheNames指定的名字获取;
                      CacheManager先获取相应的缓存),第一次获取缓存如果没有Cache组件会自动创建。

 在cache组件中:

看以前的步骤key是按照某种策略生成的。

       

           

           

  点进去看生成原理

       

       执行目标方法,第一次走生成key了,put的值就是目标方法的了。

      

     值就是方法的返回值。

      

      默认是SimpleKeyGenerator
                2、去Cache中查找缓存的内容,使用一个key,默认就是方法的参数;
                       key是按照某种策略生成的;默认是使用keyGenerator生成的,默认使用SimpleKeyGenerator生成key;
                       SimpleKeyGenerator生成key的默认策略;
                       如果没有参数;key=new SimpleKey();
                       如果有一个参数:key=参数的值
                       如果有多个参数:key=new SimpleKey(params);
                3、没有查到缓存就调用目标方法那个value就已经是目标方法返回的了;


                4、将目标方法返回的结果,放进缓存中

             

             第一次运行。

      已经有缓存了就要查了查之前也要生成key:

       

     

   

5-----------------------------------------------------------------------------------------------------------------------------------------------------------会了

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

     *
     * 原理:
     *   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中;
     *
     *   运行流程:
     *   @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
     *
     *
     *   几个属性:
     *      cacheNames/value:指定缓存组件的名字;将方法的返回结果放在哪个缓存中,是数组的方式,可以指定多个缓存;
     *
     *      key:缓存数据使用的key;可以用它来指定。key默认是使用方法参数的值就是传的值-value是方法的返回值就是存入后的返回值
     *              编写SpEL; #id;取出参数id的值   #a0取出第一个参数  #p0取出第一个参数  #root.args[0]取出第一个参数 
     *           
     *
     *      keyGenerator:key的生成器;可以自己指定key的生成器的组件id
     *              key/keyGenerator:二选一使用;
     *
     *
     *      cacheManager:指定缓存管理器,就是两个缓存管理器都有一样的缓存;或者cacheResolver指定获取解析器也是二选一
     *
     *      condition:指定符合条件的情况下才缓存;
     *              ,condition = "#id>0"
     *          condition = "#a0>1":第一个参数的值》1的时候才进行缓存
     *
     *      unless:否定缓存;当unless指定的条件为true,方法的返回值就不会被缓存;可以获取到结果进行判断
     *              unless = "#result == null"
     *              unless = "#a0==2":如果第一个参数的值是2,结果不缓存;
     *      sync:是否使用异步模式
     * @param id
     * @return
     *
     */

              分析了缓存的配置原理:

                              属性:key

                        过程:

  

                       拿到缓存                             在缓存里面找。

        

           没有缓存就去运行目标方法。   

           运行打了注解的目标方法之后将数据存入:

                  

       ----------

     指定的话自己编写spel表达式:

                 

                  

上面的红框的方法是对应的。

root.methodName:获得方法名getEmp

 //网址:http://localhost:8080/emp/1
    @Cacheable(cacheNames = {"emp"},key="#root.methodName+']'+#id+']'")
    public Employee getEmp(Integer id){
        System.out.println("查询"+id+"号员工");
        Employee emp = employeeMapper.getEmpById(id);
        return emp;
    }

                       在这个方法上打断点

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

         在进入这里:

           

@Nullable
	private Cache.ValueWrapper findCachedItem(Collection<CacheOperationContext> contexts) {
		Object result = CacheOperationExpressionEvaluator.NO_RESULT;
		for (CacheOperationContext context : contexts) {
			if (isConditionPassing(context, result)) {
				Object key = generateKey(context, result);
				Cache.ValueWrapper cached = findInCaches(context, key);
				if (cached != null) {
					return cached;
				}
				else {
					if (logger.isTraceEnabled()) {
						logger.trace("No cache entry for key '" + key + "' in cache(s) " + context.getCacheNames());
					}
				}
			}
		}
		return null;
	}

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

  另一种主要的方法:

   指定自己的key的生成器,新建一个包加一个类。注意下面的代码是自己写的。

package com.atguigu.cache.config;

import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import java.lang.reflect.Method;
import java.util.Arrays;

@Configuration
public class MyCacheConfig {

    //不指定的话这个组件就是叫做keyGenerator就是方法名字
    @Bean("myKeyGenerator")
    public KeyGenerator keyGenerator(){
        return new KeyGenerator(){

            @Override
            public Object generate(Object target, Method method, Object... params) {
                return method.getName()+"["+ Arrays.asList(params).toString()+"]";
            }
        };
    }
}

注意加@configuration必须在方法上加@bean返回给容器方法名字的bean                   

 //网址:http://localhost:8080/emp/1
   /* @Cacheable(cacheNames = {"emp"},key="#root.methodName+']'+#id+']'")*/
    @Cacheable(cacheNames = {"emp"},keyGenerator = "myKeyGenerator")
    public Employee getEmp(Integer id){
        System.out.println("查询"+id+"号员工");
        Employee emp = employeeMapper.getEmpById(id);
        return emp;
    }

 
  放行的话就走我们自己的key生成策略。

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

下一个属性:condition    

  //网址:http://localhost:8080/emp/1
   /* @Cacheable(cacheNames = {"emp"},key="#root.methodName+']'+#id+']'")*/
    @Cacheable(cacheNames = {"emp"},keyGenerator = "myKeyGenerator",condition="#a0>1")
    public Employee getEmp(Integer id){
        System.out.println("查询"+id+"号员工");
        Employee emp = employeeMapper.getEmpById(id);
        return emp;
    }

  

   注意这里的a0就是第一个参数。

 unless属性:a0==2不缓存

 @Cacheable(cacheNames = {"emp"},keyGenerator = "myKeyGenerator",condition="#a0>1",unless="#a0==2")
    public Employee getEmp(Integer id){
        System.out.println("查询"+id+"号员工");
        Employee emp = employeeMapper.getEmpById(id);
        return emp;
    }

 sync属性:是否异步,默认是false,就是方法执行完毕在加入缓存。true的话不支持unless。

  6-------------------------------------------------------------------------------------------------------------------------------------------会了

@CachePut注解:

/**
     * @CachePut:既调用方法,又更新缓存数据;同步更新缓存
     * 修改了数据库的某个数据,同时更新缓存;
     * 运行时机:
     *  1、先调用目标方法 注意这个时机
     *  2、将目标方法的结果缓存起来
     *
     * 测试步骤:
     *  1、查询1号员工;查到的结果会放在缓存中;
     *          key:1  value:lastName:张三
     *  2、以后查询还是之前的结果
     *  3、更新1号员工;【lastName:zhangsan;gender:0】
     *          将方法的返回值也放进缓存了;
     *          key:不指定的话是方法的参数就是传入的employee对象  值:返回的employee对象;
     *  4、查询1号员工?
     *      应该是更新后的员工;
     *          key = "#employee.id":使用传入的参数的员工id;
     *          key = "#result.id":使用返回后的id
     *             @Cacheable的key是不能用#result
     *      为什么是没更新前的?【1号员工没有在缓存中更新】
     *
     */
    @CachePut(value = "emp")
    public Employee updateEmp(Employee employee){
        System.out.println("updateEmp:"+employee);
        employeeMapper.updateEmp(employee);
        return employee;
    }

  注意这个注解的执行时机。

 这个是在调方法之前的。

    更新的网址:

    

   http://localhost:8080/emp?id=1&lastName=zahngsan&gender=0

    注意这样的话查完了还是有问题的,就是查询的时候传入的是key是方法的参数(id)的值,但是更新的时候传入的是方法的参数(对象)的值个查询的是不一样的。

     解决办法:

@CachePut(value = "emp",key = "#result.id")
    public Employee updateEmp(Employee employee){
        System.out.println("updateEmp:"+employee);
        employeeMapper.updateEmp(employee);
        return employee;
    }

result是返回值。

之前的写法如下图:

是因为运行时机不一样。

 总结:注意的一点就是查询和更新的时候缓存要是同一个key

小结:cachePut先调用目标方法,一定会执行目标方法放入。

7------------------------------------------------------------------------------------------------------------会了

@CacheEvit注解,缓存清除,就是删除数据,将数据之前的缓存也删除掉。

                  

/**
     * @CacheEvict:缓存清除
     *  key:指定要清除的数据
     *  allEntries = true:指定清除 emp这个cache缓存中所有的数据
     *  beforeInvocation = false:缓存的清除是否在方法之前执行
     *      默认代表缓存清除操作是在方法执行之后执行;如果出现异常缓存就不会清除
     *
     *  beforeInvocation = true:
     *      代表清除缓存操作是在方法运行之前执行,无论方法是否出现异常,缓存都清除
     *
     *
     */
   /* @CacheEvict(value="emp",beforeInvocation = true*//*key = "#id",*//*)*/
    @CacheEvict(value="emp",key = "#id")
    public void deleteEmp(Integer id){
        employeeMapper.deleteEmpById(id);
        System.out.println("deleteEmp:"+id);
    }

         删除的是缓存的。是按照指定的key删除的。

         添加属性:

 /* @CacheEvict(value="emp",beforeInvocation = true*//*key = "#id",*//*)*/
    /*@CacheEvict(value="emp",key = "#id")*/
    @CacheEvict(value="emp",allEntries = true)
    public void deleteEmp(Integer id){
        System.out.println("deleteEmp:"+id);
        //employeeMapper.deleteEmpById(id);  
        int i = 10/0;
    }

 allEntries = true就是不用指定key了删除所有的数据。就是把1 2号员工的缓存都删除了。

 属性beforeInvocation是否在方法之前执行,默认是false,true的话就是方法报错也执行。


    @CacheEvict(value="emp",beforeInvocation = true)
    public void deleteEmp(Integer id){
        System.out.println("deleteEmp:"+id);
        //employeeMapper.deleteEmpById(id);
        int i = 10/0;
    }

  fasle时候方法错了就不清除了,因为默认是再方法之后执行的。    

8-------------------------------------------------------------------------------------------------------------会了

  @caching注解就是指定的多个条件:

   //@Caching 定义复杂的缓存规则
    @Caching(
            cacheable = {
                    @Cacheable(value="emp",key = "#lastName")
            },
            put = {
                    @CachePut(value="emp",key = "#result.id"),
                    @CachePut(value="emp",key = "#result.email")
            }
    )
    public Employee getEmpByLastName(String lastName){
        return employeeMapper.getEmpByLastName(lastName);
    }

 注意put规则是在方法运行之后调用的。

 网址:

          第一次在数据库查的

         

        再次用lastName查还是会执行数据库。因为cachePut注解的意义在于方法一定执行。目的是为了更新的,不是查询的。

         http://localhost:8080/emp/lastname/zahngsan

        公共属性的抽取:

        在类上加注解。

@CacheConfig(cacheNames="emp"/*,cacheManager = "employeeCacheManager"*/) //抽取缓存的公共配置
@Service
public class EmployeeService {

9---------------------------------------------------------------------------------------------------------------

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值