zoukankan      html  css  js  c++  java
  • springboot之缓存

    JSR107

    1.核心

    JAVA Caching定义了5哥核心接口,分别是CachingProvider,CacheManager,Cache,Entry和Expiry。

    • CachingProvider定义了创建、配置、获取、管理和控制多个CacheManager。一个应用可以再运行期间访问多个CachingProvider。

    • CachingManager定义了创建、配置、获取、管理和控制多个唯一命名的Cache,这些Cache存在于CachingManager的上下文中。一个CachingManager仅被一个CachingProvider所拥有的。

    • Cache是一个类似Map的数据结构,并临时存储以key为索引的值。一个Cache仅被一个CachingManager所拥有

    • Entry是一个存储在Cache中的key-value对。

    • Expiry每一个存储在Cahce中的条目有一个定义的有效期。一旦超过了这个时间,条目为过期状态。一旦过期,条目将不可被访问、更新和山村。缓存有效期可以通过ExpiryPolicy设置。

    2.如何使用?

    1.导入依赖

    <dependency>
    <groupId>javax.cache</groupId>
    <artifactId>cache-api</artifactId>
    </dependency>

     

     

    JSR107缓存,在实际开发中很少直接使用它。

     

     

    2.Spring缓存抽象

     

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

    • Cache接口为缓存的组件规范定义,包含缓存的各种操作集合。

    • Cache接口为spring提供了各种xxxCache的实现;如RedisCache,EhCacheCache,ConcurrentMapCache等;

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

      1.确定方法需要被缓存以及他们的缓存策略。

      2.从缓存中读取之前缓存存储的数据。

     

    1.几个重要的概念&缓存注解

    Cache缓存接口,定义缓存操作。实现由:RedisCache、EhCacheCache、ConcurrentMapCache等。
    CacheManager 缓存管理器,管理各种缓存(Cache)组件
    @Cacheable 主要针对方法配置,能够根据方法的请求参数对其结果进行缓存。
    @CacheEvict 清空缓存
    @CachePut 保证方法被调用,有希望结果被缓存。
    @EnableCaching 开启基于注解的缓存
    keyGenerator 缓存数据时key生成的策略
    serialize 缓存数据时value序列化策略

     

    缓存SpEL规则:

    名字位置描述实例
    methodName root object 当前被调用的方法名 #root.methodName
    method root object 当前被调用的方法 #root.method.name
    target root object 当前被调用的目标对象 #root.target
    targetClass root object 当前被调用的目标对象类 #root.targetClass
    args root object 当前被调用的方法的参数列表 #root.args[0]
    caches root object 当前方法使用的缓存列表(如@Cacheable(value={"cache1","cache2"})),则有两个cache #root.caches[0].name
    argumentname evaluation context 方法参数的名字,可以直接#参数名,也可以使用#p0或者#a0的形式,0代表参数索引。 #iban、#a0、#p0
    result evaluation context 方法执行后的返回值(晋档方法执行后的判断有效,如 “unless”,'cache put'表达式 'cache evict'的表达式beforelnvocation=false) #result
           

    工程代码

    环境:

    jdk:1.8

    idea:2019.3.1

    springboot 2.2.6

    mysql:5.7.29

    pom.xml文件:

    <?xml version="1.0" encoding="UTF-8"?>
    <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
             xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
        <modelVersion>4.0.0</modelVersion>
        <groupId>com.seegot</groupId>
        <artifactId>spring-boot-01-cache</artifactId>
        <version>0.0.1-SNAPSHOT</version>
        <name>spring-boot-01-cache</name>
        <description>Demo project for Spring Boot</description>
    
        <properties>
            <java.version>1.8</java.version>
            <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
            <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
            <spring-boot.version>2.2.6.RELEASE</spring-boot.version>
        </properties>
    
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-cache</artifactId>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-web</artifactId>
            </dependency>
            <dependency>
                <groupId>org.mybatis.spring.boot</groupId>
                <artifactId>mybatis-spring-boot-starter</artifactId>
                <version>2.1.1</version>
            </dependency>
            <!--引入druid-->
            <dependency>
                <groupId>com.alibaba</groupId>
                <artifactId>druid-spring-boot-starter</artifactId>
                <version>1.1.10</version>
            </dependency>
            <!--引入log4j日志管理-->
            <dependency>
                <groupId>log4j</groupId>
                <artifactId>log4j</artifactId>
                <version>1.2.17</version>
            </dependency>
            <dependency>
                <groupId>mysql</groupId>
                <artifactId>mysql-connector-java</artifactId>
                <scope>runtime</scope>
            </dependency>
            <dependency>
                <groupId>org.projectlombok</groupId>
                <artifactId>lombok</artifactId>
                <optional>true</optional>
            </dependency>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-starter-test</artifactId>
                <scope>test</scope>
                <exclusions>
                    <exclusion>
                        <groupId>org.junit.vintage</groupId>
                        <artifactId>junit-vintage-engine</artifactId>
                    </exclusion>
                </exclusions>
            </dependency>
        </dependencies>
    
        <dependencyManagement>
            <dependencies>
                <dependency>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-dependencies</artifactId>
                    <version>${spring-boot.version}</version>
                    <type>pom</type>
                    <scope>import</scope>
                </dependency>
            </dependencies>
        </dependencyManagement>
    
        <build>
            <plugins>
                <plugin>
                    <groupId>org.apache.maven.plugins</groupId>
                    <artifactId>maven-compiler-plugin</artifactId>
                    <configuration>
                        <source>1.8</source>
                        <target>1.8</target>
                        <encoding>UTF-8</encoding>
                    </configuration>
                </plugin>
                <plugin>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-maven-plugin</artifactId>
                    <version>2.2.6.RELEASE</version>
                </plugin>
            </plugins>
        </build>
    
    </project>

    application.yml

    spring:
      datasource:
        # 配置监控服务器
        type: com.alibaba.druid.pool.DruidDataSource
        druid:
          #   druid 数据源其他配置 当添加了如下配置,我们需要在程序中增加configuration配置来将这些配置映射到系统默认配置参数上去。
          driver-class-name: com.mysql.cj.jdbc.Driver
          username: root
          password: root
          url: jdbc:mysql://192.168.100.158:3306/spring_cache?characterEncoding=utf-8&autoReconnect=true&zeroDateTimeBehavior=convertToNull&useSSL=false
          initialSize: 5
          minIdle: 5
          maxActive: 20
          maxWait: 60000
          timeBetweenEvictionRunsMillis: 60000
          minEvictableIdleTimeMillis: 300000
          validationQuery: SELECT 1 FROM DUAL
          testWhileIdle: true
          testOnBorrow: false
          testOnReturn: false
          poolPreparedStatements: true
          #   配置监控统计拦截的filters,去掉后监控界面sql无法统计,'wall'用于防火墙
          filter:
            config:
              enabled: true
            stat:
              enabled: true
              db-type: mysql
            wall:
              enabled: true
              db-type: mysql
          maxPoolPreparedStatementPerConnectionSize: 20
          useGlobalDataSourceStat: true
          connectionProperties: druid.stat.mergeSql=true;druid.stat.slowSqlMillis=500
          stat-view-servlet:
            login-username: admin
            login-password: admin
            reset-enable: false
            url-pattern: /druid/*
          web-stat-filter:
            url-pattern: /*
            exclusions: "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*"
    mybatis:
      configuration:
        # 开启驼峰命名法
        map-underscore-to-camel-case: true
    logging:
      level:
        # 设置包下日志输出级别
       com.seegot.mapper: debug

    数据库spring_cache的代码:

    /*
    Navicat MySQL Data Transfer
    
    Source Server         : 本地
    Source Server Version : 50528
    Source Host           : 127.0.0.1:3306
    Source Database       : springboot_cache
    
    Target Server Type    : MYSQL
    Target Server Version : 50528
    File Encoding         : 65001
    
    Date: 2020-04-28 14:54:04
    */
    
    SET FOREIGN_KEY_CHECKS=0;
    
    -- ----------------------------
    -- Table structure for department
    -- ----------------------------
    DROP TABLE IF EXISTS `department`;
    CREATE TABLE `department` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `departmentName` varchar(255) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
    -- ----------------------------
    -- Table structure for employee
    -- ----------------------------
    DROP TABLE IF EXISTS `employee`;
    CREATE TABLE `employee` (
      `id` int(11) NOT NULL AUTO_INCREMENT,
      `lastName` varchar(255) DEFAULT NULL,
      `email` varchar(255) DEFAULT NULL,
      `gender` int(2) DEFAULT NULL,
      `d_id` int(11) DEFAULT NULL,
      PRIMARY KEY (`id`)
    ) ENGINE=InnoDB DEFAULT CHARSET=utf8;

    部门bean代码

    package com.seegot.bean;
    
    import lombok.Data;
    
    /**
     * @program: spring-boot-01-cache
     * @description:
     * @author: PP Zhang
     * @create: 2020-04-27 10:46
     */
    @Data
    public class Department {
        private Integer id;
        private String departmentName;
    }

    员工bean代码

    package com.seegot.bean;
    
    import lombok.Data;
    
    /**
     * @program: spring-boot-01-cache
     * @description:
     * @author: PP Zhang
     * @create: 2020-04-27 10:47
     */
    @Data
    public class Employee {
        private Integer id;
        private String lastName;
        private String email;
        private Integer gender;
        private Integer dId;
    }

    EmployeeMapper的Mapper

    package com.seegot.mapper;
    
    import com.seegot.bean.Employee;
    import org.apache.ibatis.annotations.*;
    
    /**
     * @program: spring-boot-01-cache
     * @description:
     * @author: PP Zhang
     * @create: 2020-04-27 11:06
     */
    @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 emp);
        @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 insertEmp(Employee emp);
    }

    Service层

    package com.seegot.service;
    
    import com.seegot.bean.Employee;
    import com.seegot.mapper.EmployeeMapper;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.cache.annotation.CacheEvict;
    import org.springframework.cache.annotation.CachePut;
    import org.springframework.cache.annotation.Cacheable;
    import org.springframework.stereotype.Service;
    
    /**
     * @program: spring-boot-01-cache
     * @description:
     * @author: PP Zhang
     * @create: 2020-04-27 11:15
     */
    @Service
    public class EmployeeService {
        @Autowired
        EmployeeMapper employeeMapper;
        /**
        * @Description:
         *      将方法运行的结果进行缓存,以后再要相同的数据,直接从缓存中获取,不用调用方法去数据库查询。
         *  CacheManager管理多个Cache组件的,对缓存真正的CRUD操作在Cache组件中,每个缓存组件有自己唯一的名字;
         * @Cacheable几个属性:(key-value)
         *      cacheNames/values:指定缓存组件的名字。讲方法的反馈结果放在哪个缓存中,而且可以使数组的方式,可以指定多个缓存
         *      key:缓存数据时用的key;可以用它来指定,如果不指定Key默认是使用方法参数的值;
         *          编写SpEL:
         *              #id 是参数id的值。
         *      keyGenerator:key的生成器,可以自定义key的生成器组件id
         *      指定了Key 就不要再指定keyGenerator 二选一
         *      cacheManager:指定缓存管理器;或者cacheResolver指定获取解析器
         *      condition:指定符合条件的情况下才会缓存。@Cacheable(cacheNames = "emp",condition = "#id>0")
         *      unless:否定缓存,当unless指定的条件为true,方法返回值就不被缓存。可以获取到结果进行判断。unless = "#result==null
         *      sync:是否开启异步模式
         *
         *  原理:
         *      1.自动配置类:CacheAutoConfiguration
         *      2.缓存配置类
         *
         *      3.哪些配置类生效?
         *          SimpleCacheConfiguration
         *
         *   运行流程:
         * @Cacheable
         *      1.方法运行之前,先去查询Cache(缓存组件),按照cacheNames指定的名字获取;
         *      (CacheManager先获取相应的缓存),第一次获取缓存,如果没有Cache组件会自动创建,
         *      2.去Cache中查找缓存的内容,按使用一个Key,默认就是方法的参数;
         *        key是按照某种策略生成的。默认是使用keyGenerator生成的,默认使用SimpleKeyGenerator生成。
         *        SimpleKeyGenerator生成Key的默认策略:
         *          如果没有参数;key=new SimpleKey();
         *          如果有一个参数:key=参数的值()
         *          如果有多个参数:key=new SimpleKey(params);
         *      3.没有查到缓存就调用目标方法;
         *      4.将目标方法返回的结果,放进缓存中
         *
         *       @Cacheable标注的方法执行之前下来检查缓存中有没有这个数据,默认按照参数的值作为key去查询缓存,
         *       如果没有就运行方法并将结果放到缓存;以后在来调用,就可以直接使用缓存中的数据;
         *
         *
         *       核心:
         *          1)使用CacheManager【ConcurrentMapCacheManager】按照名字得到Cache【ConcurrentMapCache】组件
         *          2)key使用keyGenerator生成的,默认是SimpleKeyGenerator生成的。
        * @Param:
        * @return:
        * @Author: PP Zhang
        * @Date: 2020/4/27
        */
        // @Cacheable(value="emp",keyGenerator=myKeyGenerator)
        //  @Cacheable(cacheNames = "emp",key = "#root.methodName+'['+#id+']'",condition = "#a0>1 and #root.nethodName eq['aaa']")
        @Cacheable(cacheNames = "emp",key = "#id")
        public Employee getEmp(Integer id){
            System.out.println("查询"+id+"号的员工信息");
            Employee employee = employeeMapper.getEmpById(id);
            return  employee;
        }
        /**
         * @CachePut:即调用方法,又更新缓存数据;
         * 修改了数据库的某个数据,同时更新缓存;
         * 运行时机:
         *  1.先调用目标方法
         *  2.将目标方法结果缓存起来
         *
         * 测试步骤:
         *  1.查询1号员工;查到的结果会放到缓存中。
         *  key:1  value:lastName:Lilei
         *  2.以后查询还是之前的结果。
         *  3.更新1号员工信息。【lastName:zhangsan;gender=1】
         *      使用 @CachePut(value = "emp") 其实也将该条结果存入了缓存,
         *      由于没有指定缓存名称,所以默认是参数也就是 employee
         *      key:employee  value:返回的employee对象。
         *      @Cacheable是不可能用#result的。
         *  4.查询1号员工
         *     应该是更新的员工
         *     key = "#employee.id" 使用传入的参数员工id;
         *     key="#result.id" 是返回后的Id
         *
         *     为什么是没有更新前的?
         * */
        @CachePut(value = "emp",key = "#result.id")
        public  Employee updateEmp(Employee employee){
            System.out.println("updateEmp"+employee);
            employeeMapper.updateEmp(employee);
            return  employee;
        }
        /**
         * @CacheEvict:清除缓存
         * key 指定要清除的Key
         *
         * allEntries = true 删除指定缓存中的所有数据
         *
         * beforeInvocation = false 缓存的清除是否在方法之前执行?
         *  默认缓存清除是在方法执行之后执行的,如果方法出现异常,缓存就不会被清除。
         *
         *  beforeInvocation = true
         *   代表清除缓存操作是在方法运行之前执行的,无论方法是否出现异常,缓存都被清除。
         * */
        @CacheEvict(value = "emp",/*key = "#id",*//*allEntries = true*/beforeInvocation = true)
        public  void deleteEmp(Integer id){
            System.out.println("deleteEmp"+id);
            // employeeMapper.deleteEmpById(id);
            int i = 10/0;
        }
    }

    Controller代码

    package com.seegot.controller;
    
    import com.seegot.bean.Employee;
    import com.seegot.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;
    
    /**
     * @program: spring-boot-01-cache
     * @description:
     * @author: PP Zhang
     * @create: 2020-04-27 11:17
     */
    @RestController
    public class EmployeeController {
        @Autowired
        EmployeeService employeeService;
    
        @GetMapping("/emp/{id}")
        public Employee getEmpById(@PathVariable("id") Integer id){
            Employee employee = employeeService.getEmp(id);
            return  employee;
        }
        @GetMapping("/emp")
        public Employee update(Employee employee){
            Employee emp = employeeService.updateEmp(employee);
            return emp;
        }
        @GetMapping("/delemp")
        public String deleteEmp(Integer id){
            employeeService.deleteEmp(id);
            return "success";
        }
    }

    http://192.168.100.106:8080/emp/1

    完成上诉操作后,当第一次查询id=1用户时,会查一次数据库。当第二次查询id=1用户时,就不会在访问数据库了。

     
  • 相关阅读:
    [转] 使用Git进行小项目代码管理
    [转] Linux抓包工具tcpdump详解
    几天都是气温骤降,感冒了还没有收获
    1128进入自学实习培训路
    hdu 4952
    hdu 4937 2014 Multi-University Training Contest 7 1003
    hdu 4941 2014 Multi-University Training Contest 7 1007
    hdu 4939 2014 Multi-University Training Contest 7 1005
    hdu 4932 BestCoder Round #4 1002
    POJ 2362
  • 原文地址:https://www.cnblogs.com/pengpengzhang/p/12788104.html
Copyright © 2011-2022 走看看