@Cacheable缓存相关使用总结

2023-11-11

本篇文章主要讲解Spring当中@Cacheable缓存相关使用

在实际项目开发中,有些数据是变更频率比较低,但是查询频率比较高的,此时为了提升系统性能,可以使用缓存的机制实现,避免每次从数据库获取

第一步:使用@EnableCaching注解开启缓存

开启缓存功能,配置类中需要加上这个注解,有了这个注解之后,spring才知道你需要使用缓存的功能,其他和缓存相关的注解才会有效,Spring中主要是通过aop实现的,通过aop来拦截需要使用缓存的方法,实现缓存的功能

第二步:在方法或类上添加@Cacheable注解,表明某一个方法或者某一个类里的所有方法开启缓存功能;
@Cacheable可以标记在一个方法上,也可以标记在一个类上。当标记在一个方法上时表示该方法是支持缓存的,当标记在一个类上时则表示该类所有的方法都是支持缓存的。

对于一个支持缓存的方法,Spring会在其被调用后将其返回值缓存起来,以保证下次利用同样的参数来执行该方法时可以直接从缓存中获取结果,而不需要再次执行该方法。Spring在缓存方法的返回值时是以键值对进行缓存的,值就是方法的返回结果,至于键的话,Spring又支持两种策略,默认策略和自定义策略,需要注意的是当一个支持缓存的方法在对象内部被调用时是不会触发缓存功能的(因为是Aop实现的,Aop是核心是代理,内部调用无法被代理,也就不会生效)。@Cacheable可以指定三个属性,value、key和condition。

测试相关的类如下:ArticleService主要是提供模拟缓存的接口

package com.ym.example.demo.cachable;

import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.cache.annotation.Caching;
import org.springframework.stereotype.Component;

import java.util.*;


@Component
public class ArticleService {
   private Map<Long, String> articleMap = new HashMap<>();

   @Cacheable(cacheNames = {"cacheTest"})
   public List<String> list(){
      System.out.println("获取文章列表");
      return Arrays.asList("Spring", "MySQL", "java高并发", "Maven");
   }

   @Cacheable(value = {"cacheTest"})
   public List<String> listValue(){
      System.out.println("获取文章列表");
      return Arrays.asList("Spring", "MySQL", "java高并发", "Maven");
   }

   /**
   * @Author yangming
   * @Description
    * @Cacheable可以标记在方法上,也可以标记在一个类上,当标记在一个方法上是,表示该方法时支持缓存的,当标记到一个类上时,则表示该类所有的方法都是支持缓存的
    * 对于一个支持缓存的方法,Spring会在其被调用后将其返回值缓存起来,以保证下次利用同样的参数来执行该方法时可以直接从缓存中获取结果,而不需要再次执行该方法。
    * Spring在缓存方法的返回值时是以键值对进行缓存的,值就是方法的返回结果,至于键的话,Spring又支持两种策略,默认策略和自定义策略
    * 这里需要注意,因为Spring缓存是通过aop实现的,aop又是依赖的代理模式,所以当一个支持缓存的方法在对象内部被调用时是不会触发缓存功能的
    * value和cacheNames一样,都是指定缓存的名称,这个cache名称可以是一个,也可以是多个,需要指定多个cache时其是一个数组
    * 可以将Cache理解为一个HashMap,系统中可以有很多歌Cache,每个Cache都有一个名字,你需要将方法的返回值放在哪个缓存中,需要通过缓存的名称来指定
    * key属性用来指定Spring缓存方法的返回结果时对应的key,因为Cache可以理解为一个HashMap,缓存以key-value的形式存储在HashMap中,value就是需要缓存的值(即方法返回值)
    * key属性支持spel表达式,当我们没有指定该属性时,Spring将使用默认策略生成key(org.springframework.cache.interceptor.SimpleKeyGenerator),默认会以方法参数创建key
    * 自定义策略是指我们可以通过SpEL表达式来指定我们的key,这里的SpEL表达式可以使用方法参数及他们对应的属性,使用方法参数时我们可以直接使用"#参数名"或者"#p参数index"
    * Spring还未我们提供了一个root对象可以用来生成key,通过该root对象我们可以获取到以下信息
    * methodName  当前方法名                #root.methodName
    * method      当前方法                  #root.method.name
    * target      当前被调用的对象            #root.target
    * targetClass 当前被调用的对象的class     #root.targetClass
    * args        当前方法参数组成的数组       #root.args[0]
    * caches      当前被调用的方法使用的cache  #root.caches[0].name
    *
   * @Date 2023/8/12 10:36
   * @param page
   * @param pageSize
   **/
   @Cacheable(value = {"cacheTest"}, key = "#root.target.class.name+'-'+#page+'-'+#pageSize")
   public String getPage(int page, int pageSize){
      String msg = String.format("page-%s-pageSize-%s", page, pageSize);
      System.out.println("从db中获取数据: " + msg);
      return msg;
   }

   /**
   * @Author yangming
   * @Description 没有指定key。默认为方法参数创建key,该方法的key为SimpleKey [1,10]
   * @Date 2023/8/12 10:55
   * @param page
   * @param pageSize
   **/
   @Cacheable(value = {"cacheTest"})
   public String getPageKey(int page, int pageSize){
      String msg = String.format("page-%s-pageSize-%s", page, pageSize);
      System.out.println("从db中获取数据: " + msg);
      return msg;
   }

   /**
    * @Author yangming
    * @Description 没有指定key。默认为方法参数创建key,该方法的key为SimpleKey []
    * @Date 2023/8/12 10:55
    **/
   @Cacheable(value = {"cacheTest"})
   public String getPageKey(){
      String msg = "测试key";
      System.out.println("从db中获取数据: " + msg);
      return msg;
   }

   /**
   * @Author yangming
   * @Description 有时候希望方法调用走缓存,有时候不希望走缓存,condition为true表示先尝试从缓存中取,如果缓存中没有,则执行方法,并将方法返回结果放到缓存中,
    *             condition为false表示不走缓存,直接执行方法,并且返回的结果也不会放到缓存中
   * @Date 2023/8/11 19:39
   * @param id
   * @param cache
   **/
   @Cacheable(cacheNames = "cacheTest", key="'getById'+#id", condition = "#cache")
   public String getById(Long id, boolean cache){
      System.out.println("getById获取数据!");
      return "Spring缓存: " + UUID.randomUUID().toString();
   }

   /**
   * @Author yangming
   * @Description 当condition为空或者为true的情况下,unless才有效,condition为false的时候,unless无效,
    *             unless为true,表示方法防结果不放到缓存中,unless为false,表示方法返回结果要放到缓存中
    * condition和unless对比
    * 缓存的使用过程中有两个点:
    * 1、查询缓存中是否有数据;
    * 2、如果缓存中没有数据,则去执行目标方法,然后将方法结果放到缓存中
    * Spring中通过condition和unless对这2点进行干预
    * condition作用在上面2个点的过程中,当为true的时候,会尝试从缓存中获取数据,如果没有,会执行方法,然后将方法返回值丢到缓存中;
    *                               当为false的时候,则直接调用目标方法,并且结果不会放到缓存中
    * unless在condition为true的时候才有效,用来判断上面的第2点,看要不要将执行结果放到缓存中,
    *                               如果为true,表示执行的结果不放到缓存中,
    *                               如果为false,表示执行的结果要放到缓存中,在unless中可以使用spel表达式通过#result来获取方法返回值
   * @Date 2023/8/11 19:50
   * @param id
   **/
   @Cacheable(cacheNames = "cacheTest", key = "'findById'+#id", unless = "#result==null")
   public String findById(Long id){
      this.articleMap.put(1L, "Spring系列");
      System.out.println("-----获取文章: " + id + "-------");
      return this.articleMap.get(id);
   }

/**
   * @Author yangming
   * @Description @CachePut也可以标注在类或者方法上,被标注的方法每次都会被调用,然后方法执行完毕之后,会将方法结果放到缓存中;当标注在类上,相当于在类的所有方法上都标注了@CachePut
    * @CachePut有3种情况,结果不会放到缓存
    * 1、当方法向外抛出异常的时候
    * 2、当condition的计算结果为false的时候
    * 3、unless的计算结果为true的时候
    * value,cacheNames,key,condition,unless的用法和@Cacheable中类似
   * @Date 2023/8/12 11:05
   * @param id
   * @param content
   **/
   @CachePut(cacheNames = "cacheTest", key = "'findById'+#id")
   public String add(Long id, String content){
      System.out.println("新增文章: " + id);
      this.articleMap.put(id, content);
      return content;
   }

   /**
   * @Author yangming
   * @Description @CacheEvict是用来清除缓存的,可以标注在类或者方法上,被标注在方法上,则目标方法被调用的时候,会清除指定的缓存;当标注在类上,相当于在类的所有方法上标注了@CacheEvict
   * value,cacheNames,key,condition的用法和@Cacheable中类似,@CacheEvict多了allEntries和beforeInvocation属性
    * allEntries:表示是否清理cacheNames指定的缓存中的所有缓存信息,默认为false
    *             可以将cache理解为一个HashMap,当allEntries为true的时候,相当于HashMap.clear(),
    *             当allEntries为false的时候,只会干掉key对应的数据,相当于HashMap.remove(key)
    * beforeInvocation:表示何时执行清除操作(方法执行前 or 方法执行成功之后)
    *              true:表示@CacheEvict 标注的方法执行之前,执行清除操作
    *              false:表示@CacheEvict 标注的方法执行成功之后,执行清除操作,当方法弹出异常的时候,不会执行清除操作
   * @Date 2023/8/12 11:25
   * @param id
   **/
   @CacheEvict(cacheNames = "cacheTest", key = "'findById'+#id")
   public void delete(Long id){
      System.out.println("根据id删除文章: " + id);
      this.articleMap.remove(id);
   }

   @Caching(cacheable = {@Cacheable(value = "cacheTest", key="#root.methodName")},
           put={@CachePut(value = "cacheTest", key = "#root.methodName")})
   public void testCaching(){

   }
}

CacheConfig提供缓存相关的配置,这个CacheMangager有多种实现,本例是使用的ConcurrentMapCacheManager实现,也可以是RedisCacheManager的实现

package com.ym.example.demo.cachable;

import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.EnableCaching;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

/**
 * @author yangming
 * @date 2023/8/11 19:24
 * @desc @EnableCaching表示开启缓存,有了这个注解之后,Spring才知道你需要使用缓存的功能,其他和缓存相关的注解才会生效
 *       Spring中主要是通过aop实现的,通过aop来拦截需要使用缓存的方法,实现缓存的功能
 * @package com.ym.example.demo.cachable
 */
@EnableCaching
@ComponentScan
@Configuration
public class CacheConfig {

   /**
   * @Author yangming
   * @Description 开启缓存之后,还需要在配置类中定义一个bean,作为缓存管理器,类型为CacheManager,
    *             CacheManager是一个接口,有好几个实现,比如使用redis,ConcurrentMap为存储缓存信息,
    *             本例使用的是ConcurrentMapCacheManager,内部使用ConcurrentHashMap将缓存信息直接存储在本地jvm内存中
    *             不过线上环境一般是集群的方式,可以通过redis实现
   * @Date 2023/8/12 10:45
   **/
   @Bean
   public CacheManager cacheManager(){
      ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager("cacheTest");
      return cacheManager;
   }
}

CacheTest是测试相关的方法

package com.ym.example.demo.cachable;

import org.junit.Test;
import org.springframework.cache.concurrent.ConcurrentMapCache;
import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

/**
 * @author yangming
 * @date 2023/8/11 19:26
 * @package com.ym.example.demo.cachable
 */
public class CacheTest {

   @Test
   public void test(){
      AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
      context.register(CacheConfig.class);
      context.refresh();;
      ArticleService articleService = context.getBean(ArticleService.class);
      System.out.println(articleService.list());
      System.out.println(articleService.list());
   }

    执行结果:
    获取文章列表
    [Spring, MySQL, java高并发, Maven]
    [Spring, MySQL, java高并发, Maven]


   @Test
   public void test1(){
      AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
      context.register(CacheConfig.class);
      context.refresh();;
      ArticleService articleService = context.getBean(ArticleService.class);
      //page=1,pageSize=10调用2次
      System.out.println(articleService.getPage(2, 10));
      System.out.println(articleService.getPage(2, 10));

      //page=2,pageSize=10调用2次
      System.out.println(articleService.getPage(3, 10));
      System.out.println(articleService.getPage(3, 10));

      {
         System.out.println("下面打印出cacheTest缓存中的key列表");
         ConcurrentMapCacheManager cacheManager = context.getBean(ConcurrentMapCacheManager.class);
         ConcurrentMapCache cacheTest = (ConcurrentMapCache) cacheManager.getCache("cacheTest");
         cacheTest.getNativeCache().keySet().stream().forEach(System.out::println);
      }
   }

    执行结果:
    从db中获取数据: page-2-pageSize-10
    page-2-pageSize-10
    page-2-pageSize-10
    从db中获取数据: page-3-pageSize-10
    page-3-pageSize-10
    page-3-pageSize-10
    下面打印出cacheTest缓存中的key列表
    com.ym.example.demo.cachable.ArticleService-3-10
    com.ym.example.demo.cachable.ArticleService-2-10


   @Test
   public void test2(){
      AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
      context.register(CacheConfig.class);
      context.refresh();;
      ArticleService articleService = context.getBean(ArticleService.class);

      //第一次,缓存中没有,执行方法,将结果放到缓存
      System.out.println(articleService.getById(1L, true));
      //第二次,缓存中有,直接从缓存中获取
      System.out.println(articleService.getById(1L, true));
      //第三次,condition为false,表示不从缓存取,直接执行方法,同时方法执行结果也不放到缓存
      System.out.println(articleService.getById(1L, false));
      //第四次,condition为true,缓存有,直接从缓存中取
      System.out.println(articleService.getById(1L, true));
   }

    执行结果:
    getById获取数据!
    Spring缓存: 1df6227d-53ae-46a6-9a70-a85e32e39f08
    Spring缓存: 1df6227d-53ae-46a6-9a70-a85e32e39f08
    getById获取数据!
    Spring缓存: 7ff6c668-87f1-4432-8844-61bf66b6e3ef
    Spring缓存: 1df6227d-53ae-46a6-9a70-a85e32e39f08



   @Test
   public void test3(){
      AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
      context.register(CacheConfig.class);
      context.refresh();;
      ArticleService articleService = context.getBean(ArticleService.class);

      //第一次,没有缓存,执行方法,unless为false,表示执行结果要放到缓存中
      System.out.println(articleService.findById(1L));
      //第二次,第一次之后,缓存有数据,直接从缓存中取数据
      System.out.println(articleService.findById(1L));
      //第三次,缓存中没有,执行方法,result==null,unless为true,表示执行结果不放到缓存中
      System.out.println(articleService.findById(2L));
      //第四次,为了验证第三次的结论
      System.out.println(articleService.findById(2L));

      {
         System.out.println("下面打印出cacheTest缓存中的key列表");
         ConcurrentMapCacheManager cacheManager = context.getBean(ConcurrentMapCacheManager.class);
         ConcurrentMapCache cacheTest = (ConcurrentMapCache) cacheManager.getCache("cacheTest");
         cacheTest.getNativeCache().keySet().stream().forEach(System.out::println);
      }
   }

    执行结果:
    -----获取文章: 1-------
    Spring系列
    Spring系列
    -----获取文章: 2-------
    null
    -----获取文章: 2-------
    null
    下面打印出cacheTest缓存中的key列表
    findById1


   @Test
   public void test4(){
      AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
      context.register(CacheConfig.class);
      context.refresh();;
      ArticleService articleService = context.getBean(ArticleService.class);

      articleService.getPageKey(1,10);
      articleService.getPageKey();

      {
         System.out.println("下面打印出cacheTest缓存中的key列表");
         ConcurrentMapCacheManager cacheManager = context.getBean(ConcurrentMapCacheManager.class);
         ConcurrentMapCache cacheTest = (ConcurrentMapCache) cacheManager.getCache("cacheTest");
         cacheTest.getNativeCache().keySet().stream().forEach(System.out::println);
      }
   }
    
    执行结果:
    从db中获取数据: page-1-pageSize-10
    从db中获取数据: 测试key
    下面打印出cacheTest缓存中的key列表
    SimpleKey []
    SimpleKey [1,10]



   @Test
   public void test5(){
      AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
      context.register(CacheConfig.class);
      context.refresh();;
      ArticleService articleService = context.getBean(ArticleService.class);

      //增加2个文章,由于add方法上有@CachePut注解,所以新增之后会自动丢到缓存中
      articleService.add(1L, "java高并发系列");
      articleService.add(2L, "MySQL高手系列");

      //然后调用findById获取,看看是否会走缓存
      System.out.println("调用findById方法,会尝试从缓存中获取");
      System.out.println(articleService.findById(1L));
      System.out.println(articleService.findById(2L));

      {
         System.out.println("下面打印出cacheTest缓存中的key列表");
         ConcurrentMapCacheManager cacheManager = context.getBean(ConcurrentMapCacheManager.class);
         ConcurrentMapCache cacheTest = (ConcurrentMapCache) cacheManager.getCache("cacheTest");
         cacheTest.getNativeCache().keySet().stream().forEach(System.out::println);
      }
   }

    执行结果:
    新增文章: 1
    新增文章: 2
    调用findById方法,会尝试从缓存中获取
    java高并发系列
    MySQL高手系列
    下面打印出cacheTest缓存中的key列表
    findById2
    findById1


   @Test
   public void test6(){
      AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext();
      context.register(CacheConfig.class);
      context.refresh();;
      ArticleService articleService = context.getBean(ArticleService.class);

      //第一次调用findById,缓存中没有,则调用方法,将结果丢到缓存中
      System.out.println(articleService.findById(1L));
      //第二次调用findById,缓存存在,直接从缓存中取
      System.out.println(articleService.findById(1L));

      //执行删除操作,delete方法上加了@CacheEvict注解,会清除缓存
      articleService.delete(1L);

      //再次调用findById方法,缓存中没有了,则会调用目标方法
      System.out.println(articleService.findById(1L));
   }

    执行结果:
    -----获取文章: 1-------
    Spring系列
    Spring系列
    根据id删除文章: 1
    -----获取文章: 1-------
    Spring系列
}

@Caching:缓存注解组
当我们在类上或者同一个方法上同时使用@Cacheable、@CachePut和@CacheEvic这几个注解中的多个的时候,此时可以使用@Caching这个注解来实现
 

@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Caching {
Cacheable[] cacheable() default {};
CachePut[] put() default {};
CacheEvict[] evict() default {};
}

@CacheConfig:提取公共配置
这个注解标注在类上,可以将其他几个缓存注解(@Cacheable、@CachePut和@CacheEvic)的公共参数给提取出来放在@CacheConfig中。比如当一个类中有很多方法都需要使用(@Cacheable、@CachePut和@CacheEvic)这些缓存注解的时候,大家可以看一下这3个注解的源码,他们有很多公共的属性,比如:cacheNames、keyGenerator、cacheManager、cacheResolver,若这些属性值都是一样的,可以将其提取出来,放在@CacheConfig中,不过这些注解(@Cacheable、@CachePut和@CacheEvic)中也可以指定属性的值对@CacheConfig中的属性值进行覆盖。

@CacheConfig(cacheNames = "cache1")
public class ArticleService {
    @Cacheable(key = "'findById'+#id")
    public String findById(Long id) {
        this.articleMap.put(1L, "spring系列");
        System.out.println("----获取文章:" + id);
        return articleMap.get(id);
    }
}

spring中的缓存主要是利用spring中aop实现的,通过Aop对需要使用缓存的bean创建代理对象,通过代理对象拦截目标方法的执行,实现缓存功能。重点在于 @EnableCaching 这个注解,可以从 @Import 这个注解看起
 

@Import(CachingConfigurationSelector.class)
public @interface EnableCaching {

}

最终会给需要使用缓存的bean创建代理对象,并且会在代理中添加一个拦截器
org.springframework.cache.interceptor.CacheInterceptor ,这个类中的 invoke 方法是关键,
会拦截所有缓存相关的目标方法的执行,有兴趣可以去细看一下。
 

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

@Cacheable缓存相关使用总结 的相关文章

随机推荐

  • vue中实现删除校验-iview的气泡提示

    前言 很多时候我们担心删除会出现误删的情况 这样就需要对删除进行二次校验 确定是否继续删除 效果图 实现代码
  • Type Incompatible operand types String and int

    今天eclipse包了一个错误 意思就是Description Resource Path Location Type Incompatible operand types String an 但是昨天还没有错误为什么那 最后找了好久发现不
  • 区块链之PBFT算法

    在公有链中用的最多的是pow算法和pos算法 这些算法都是参与者的利益直接相关 通过利益来制约节点诚实的工作 解决分布式系统中的拜占庭问题 拜占庭容错算法是一种状态机副本复制算法 通过节点间的多轮消息传递 网络内的所有诚实节点就可以达成一致
  • jQuery实现省市二级联动

    主要实现流程 步骤分析 1 设置加载页面函数事件 2 在里面获取select的id并且设置下拉事件并且绑定函数 3 定义2维数组存放相应的城市 4 遍历2维数组省份 并且使用if判断 点击时的this value值 如果值与省份 二位数组下
  • 深入理解 == 与 equals 区别

    深入理解 与 equals 区别 这是一个老生常谈的问题了 也是在面试过程中常见的问题之一 网上所提供的常用回答是 equals比较值 比较值和引用 对java源码有了一定了解了之后回头再思考这个问题并不是那么的简单单一 java中的二元运
  • springboot的配置注入

    文章目录 第一种 使用 Value 第二种 使用 ConfigurationProperties springboot配置注入 数据绑定 有两种方式 第一种 使用 Value 首先 在application yml中定义自己的数据 appl
  • 蓝桥杯基础试题汇总(Python)看这一篇就够了

    目录 蓝桥杯习题汇总 1 试题 基础练习 A B问题 2 数列问题 3 试题 基础练习 十六进制转八进制 4 试题 基础练习 十六进制转十进制 5 试题 基础练习 十进制转十六进制 6 试题 基础练习 序列求和 7 试题 基础练习 圆的面积
  • 浅谈数据同步实现rsync+inotify

    浅谈数据同步实现rsync inotify 数据的实时同步介绍 数据的实时同步实现 inotify inotify的介绍 实现inotify软件 inotify rsync使用方式 实现inotify rsync 1 rsync基本概述 2
  • ubuntu安装deb包

    ubuntu安装deb包 安装deb包 sudo dpkg i 包名 安装deb包后 可能会出现依赖关系而不能正常安装软件 这个时候先更新下源然后解决依赖关系后重装即可 sudo apt get update 更新 sudo apt get
  • 教程网站 汇总:Linux 、 C /C++ 、HTML、CSS

    C 语言教程 菜鸟教程 https www runoob com cprogramming c tutorial html C 教程 菜鸟教程 https www runoob com cplusplus cpp tutorial html
  • 安装apache后无法访问localhost但可以访问127.0.0.1的解决方法

    localhost与127 0 0 1的概念和工作原理之不同 概念 localhost 也叫local 正确的解释是 本地服务器 127 0 0 1 在windows等系统的正确解释是 本机地址 本机服务器 工作原理 localhot 是不
  • VS2019的常见错误和调试功能

    目录 一 VS2019常见问题 1 scanf问题 2 如何在当前页面下再创建新项目和创建多项目后无法运行当前项目的问题 二 VS2019的调试功能 不打断点 三 VS2019的调试功能 打断点 四 总结 一 VS2019常见问题 1 sc
  • 爬虫实战爬取豆瓣电影Top250榜单电影

    爬虫实战爬取豆瓣电影Top250榜单电影 实战内容 直接上代码 重要地方有注释 from bs4 import BeautifulSoup import re import urllib request urllib error impor
  • Postman + Pre-resuestScript:预请求脚本发送GET请求

    通过预执行脚本 Pre request Script 发送GET请求 一 效果演示 二 控制台 Console 打印响应结果 代码注释详解 pm sendRequest 是发送一个请求 function 中的 err 表示请求返回的错误信息
  • Node的Buffer对象和fs模块

    一 Node的模块化管理 1 模块化 node应用程序由模块组成 遵循的是CommonJS模块规范 使用模块管理的好处是隔离模块的作用域 避免出现命名冲突 2 什么是CommonJS 是一套代码的规范 构建一个在浏览器之外的JavaScri
  • 报错:ImportError: rocketmq dynamic library not found解决方法

    目录 一 ImportError rocketmq dynamic library not found 二 OSError librocketmq so cannot open shared object file No such file
  • Shell脚本的通配符和特殊符号

    通配符 符号 意义 0到无穷个任意字符 一个任意字符 如 abcd 表示a b c d中任意一个 在编码顺序内的所有字符 如 0 9 表示0到9间的数字 反向选择 如 abc 表示非a b c的其它字符 特殊符号 符号 内容 管线 分割两个
  • 项目质量管理__七种基本质量工具__老七工具和新七工具

    七种基本质量工具 用于在PDCA Plan Do Check Action 循环的框架内解决与质量相关的问题 1 老七工具 包括因果图 流程图 核查表 帕累托图 直方图 控制图和散点图 因果图 又称鱼骨图或石川馨图 流程图 也称过程图 核查
  • 热部署系统实现

    热部署 是指在不关闭或重启服务的情况下 更新Java类文件或配置文件 实现修改内容生效 通过热部署 可提高开发效率 节省程序打包重启的时间 同时 可实现生产环境中需要不停机或重启的服务的升级 在大厂的核心中台 订单服务 商品服务往往有几千台
  • @Cacheable缓存相关使用总结

    本篇文章主要讲解Spring当中 Cacheable缓存相关使用 在实际项目开发中 有些数据是变更频率比较低 但是查询频率比较高的 此时为了提升系统性能 可以使用缓存的机制实现 避免每次从数据库获取 第一步 使用 EnableCaching