SpringCache_概述、Cacheable、更新缓存、删除缓存、从0搭建缓存项目

news2024/12/22 20:48:07

文章目录

  • ①. Spring Cache概述
  • ②. 触发缓存入口 - @Cacheable
  • ③. 更新缓存 - CachePut
  • ④. 删除缓存 - CacheEvict
  • ⑤. 组合操作- Caching
  • ⑥. 共享缓存配置 - CacheConfig
  • ⑦. 从0搭建缓存项目

①. Spring Cache概述

  • ①. 如何找到Spring Cache的官方文档

在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述在这里插入图片描述

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

  • ③. JSR-107定义了5个核心接口来实现缓存操作,分别是CachingProvider, CacheManager, Cache, Entry和Expiry
    在这里插入图片描述

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

  • ⑤. SpringCache常用注解详解

注解解释
@Cacheable触发将数据保存到缓存的操作
@CacheEvict触发将数据从缓存删除的操作
@CachePut不影响方法执行更新缓存 双写模式
@Caching组合以上多个操作
@CacheConfig在类级别共享缓存的相同配置

②. 触发缓存入口 - @Cacheable

  • ①. @Cacheable 注解提供了一些参数,用于配置缓存的行为。下面是一些常用的参数:
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Cacheable {
    @AliasFor("cacheNames")
    String[] value() default {};

    @AliasFor("value")
    String[] cacheNames() default {};

    String key() default "";

    String keyGenerator() default "";

    String cacheManager() default "";

    String cacheResolver() default "";

    String condition() default "";

    String unless() default "";

    boolean sync() default false;
}
  • ②. value/cacheNames属性
  1. 这两个属性代表的意义相同,根据@AliasFor注解就能看出来了。
  2. 这两个属性都是用来指定缓存组件的名称,即将方法的返回结果放在哪个缓存中,属性定义为数组,可以指定多个缓存
    @AliasFor("cacheNames")
    String[] value() default {};

    @AliasFor("value")
    String[] cacheNames() default {};
	@Cacheable(cacheNames ={ CacheEnum.RedisCacheNameExpGroup.AN_MINUTE,CacheEnum.RedisCacheNameExpGroup.FIVE_MINUTES})
	 
	//这两种配置等价
	@Cacheable(value = "user") //@Cacheable(cacheNames = "user")
	User getUser(Integer id);
    @Cacheable(cacheNames = CacheEnum.RedisCacheNameExpGroup.AN_MINUTE, cacheManager =
            CacheEnum.CacheManager.REDIS_MANAGER,
            key = CacheEnum.RedisCacheKeys.PATROL_BOARD + "+#params.areaCode" + "+#params.staffCode")
  • ③. key属性:可以通过key属性来指定缓存数据所使用的的key,默认使用的是方法调用传过来的参数作为key

在这里插入图片描述在这里插入图片描述

@Cacheable(value = "user",key = "#root.method.name")
User getUser(Integer id);
  • ④. cacheManager属性:用来指定缓存管理器。针对不同的缓存技术,需要实现不同的 cacheManager,Spring 也为我们定义了如下的一些cacheManger实现

在这里插入图片描述

  • ⑤. unless属性,意为"除非"的意思。即只有unless指定的条件为true时,方法的返回值才不会被缓存。可以在获取到结果后进行判断
@Cacheable(value = "user",unless = "#result==null || #result.size()==0")//当方法返回值为 null 时,就不缓存
User getUser(Integer id);

@Cacheable(value = "user",unless = "#a0 == 1")//如果第一个参数的值是1,结果不缓存
User getUser(Integer id);
  • ⑥. condition:条件判断属性,用来指定符合指定的条件下才可以缓存。也可以通过SpEL 表达式进行设置。这个配置规则和上面表格中的配置规则是相同的
@Cacheable(value = "user",condition = "#id>0")//传入的 id 参数值>0才进行缓存
User getUser(Integer id);
  • ⑦. sync该属性用来指定是否使用异步模式,该属性默认值为false,默认为同步模式。异步模式指定sync = true即可,异步模式下unless属性不可用

③. 更新缓存 - CachePut

  • ①. @CachePut:不影响方法执行更新缓存(双写模式) 需要有返回值

  • ②. @CachePut标注的方法在执行前不会去检查缓存中是否存在之前执行过的结果,而是每次都会执行该方法,并将执行结果以键值对的形式存入指定的缓存中

  • ③. @Cacheput注解一般使用在保存,更新方法中

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

④. 删除缓存 - CacheEvict

  • @CacheEvict:触发将数据从缓存删除的操作
    @CacheEvict(cacheNames = CacheEnum.RedisCacheNameExpGroup.ONE_DAY, cacheManager = CacheEnum.CacheManager.REDIS_MANAGER,
            key =CacheEnum.RedisCacheKeys.DISTRICT_DETAIL + "+#params.areaCode" + "+#params.timeRange")
    public void districtDetail(DiagnosisParams params) {
        log.info("refresh districtDetail!,areaCode:{},timeRange:{}",params.getAreaCode(),params.getTimeRange());
    }

⑤. 组合操作- Caching

  • ①. 组合以上多个操作: @Caching不常用
  1. @Caching 注解可以在一个方法或者类上同时指定多个Spring Cache相关的注解。
  2. 其拥有三个属性:cacheable、put和evict,分别用于指定@Cacheable、@CachePut和
  • ②. @CacheEvict。对于一个数据变动,更新多个缓存的场景,可以通过@Caching来实现:
@Caching(cacheable = @Cacheable(cacheNames = "caching", key = "#age"), evict = @CacheEvict(cacheNames = "t4", key = "#age"))
public String caching(int age) {
    return "caching: " + age + "-->" + UUID.randomUUID().toString();
}

⑥. 共享缓存配置 - CacheConfig

  • ①. @CacheConfig不常用:@CacheConfig是Spring提供的一个注解,用于统一配置缓存的一些属性,例如缓存名称、缓存管理器等

  • ②. 使用@CacheConfig注解需要注意以下几点:

  1. @CacheConfig可以放在类上,也可以放在方法上。如果放在类上,则该类中所有的缓存方法都会继承该注解的属性。
  2. @CacheConfig的属性可以被缓存方法上的@Cacheable、@CachePut、@CacheEvict等注解覆盖。
  3. @CacheConfig的属性可以通过Spring的EL表达式进行动态配置。
// 在上面的示例中,@CacheConfig注解指定了缓存名称为"myCache",缓存管理器为"myCacheManager"。这些属性会被该类中所有的缓存方法继承
// 但是,如果某个缓存方法上使用了@Cacheable、@CachePut、@CacheEvict等注解,并且指定了相同的属性,则该注解的属性会覆盖@CacheConfig注解的属性
@CacheConfig(cacheNames = "myCache", cacheManager = "myCacheManager")
@Service
public class MyService {

    @Cacheable(key = "#id")
    public MyObject findById(Long id) {
        // ...
    }

    @CachePut(key = "#myObject.id")
    public MyObject save(MyObject myObject) {
        // ...
    }

    @CacheEvict(key = "#id")
    public void deleteById(Long id) {
        // ...
    }
}

⑦. 从0搭建缓存项目

  • ①. 导入依赖
<?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 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <parent>
        <artifactId>study2022_xz</artifactId>
        <groupId>com.xiaozhi</groupId>
        <version>0.0.1-SNAPSHOT</version>
    </parent>
    <modelVersion>4.0.0</modelVersion>

    <artifactId>springboot-swagger</artifactId>

    <properties>
        <maven.compiler.source>8</maven.compiler.source>
        <maven.compiler.target>8</maven.compiler.target>
        <knife4j.version>2.0.9</knife4j.version>
        <lombok.version>1.18.12</lombok.version>
        <caffeine.version>2.7.0</caffeine.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>knife4j-spring-boot-starter</artifactId>
            <version>${knife4j.version}</version>
        </dependency>
        <!--web场景-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>1.2.80</version>
        </dependency>

        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>

        <!--引入caffeine依赖-->
        <dependency>
            <groupId>com.github.ben-manes.caffeine</groupId>
            <artifactId>caffeine</artifactId>
            <version>${caffeine.version}</version>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>

        <!--引入guava依赖-->
        <dependency>
            <groupId>com.google.guava</groupId>
            <artifactId>guava</artifactId>
            <version>30.1-jre</version>
        </dependency>

        <!--引入redis依赖-->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
        </dependency>
        <dependency>
            <groupId>org.apache.commons</groupId>
            <artifactId>commons-pool2</artifactId>
            <scope>compile</scope>
        </dependency>
    </dependencies>
</project>
  • ②. 配置类 - 常量分组概念
public interface CacheEnum {

    /**
     * cacheTemplate
     */
    interface CacheTemplates {
        /**
         * 业务用redis
         */
        String REDIS_CACHE_TEMPLATE = "redisTemplate";
    }

    /**
     * cacheManager
     */
    interface CacheManager {

        /**
         * caffeine Cache Manager
         */
        String CAFFEINE_MANAGER = "caffeineManager";

        /**
         * redis Cache Manager
         */
        String REDIS_MANAGER = "redisCacheManager";
    }

    /**
     * redis keys
     */
    interface RedisCacheKeys{
        String SSM_USER_KEY = "SSM:USER_KEY:";
    }


    /**
     * redis exp group
     */
    interface RedisCacheNameExpGroup {
        String AN_MINUTE = "ssm:anMinute";
        String FIVE_MINUTES = "ssm:fiveMinute";
        String AN_HOUR = "ssm:anHour";
        String TWO_HOUR = "ssm:twoHour";
        String FIVE_HOURS = "ssm:fiveHour";
        String ONE_DAY = "ssm:oneDay";
        String TWO_DAY ="ssm:twoDay";
    }


    /**
     * caffeine cache names conf
     */
    @AllArgsConstructor
    @Getter
    enum CaffeineCacheNamesConf {
        SSM_TEST(CaffeineCacheNames.SSM_TEST,100,10000,1, TimeUnit.DAYS),
        ;


        private String cacheName;

        private int initialCapacity;

        private int maximumSize;

        private int expsNum;

        private TimeUnit timeUnit;
    }

    /**
     * Caffeine Cache Names
     */
    interface CaffeineCacheNames{
        String SSM_TEST = "SSM_TEST";
    }
}
/**
 * caffeine缓存配置,根据{@link CacheEnum.CaffeineCacheNamesConf}配置,
 * 动态设置参数以及自动加载策略
 */
@Configuration
public class CaffeineCacheConfig {

    @Bean(CacheEnum.CacheManager.CAFFEINE_MANAGER)
    public CacheManager cacheManagerWithCaffeine() {
        SimpleCacheManager caffeineCacheManager = new SimpleCacheManager();
        caffeineCacheManager.setCaches(buildCaffeineCache());
        return caffeineCacheManager;
    }

    //动态设置缓存参数
    private List<CaffeineCache> buildCaffeineCache() {
        ArrayList<CaffeineCache> caches = Lists.newArrayList();
        CacheEnum.CaffeineCacheNamesConf[] cacheNames = CacheEnum.CaffeineCacheNamesConf.values();
        for (CacheEnum.CaffeineCacheNamesConf nameCof : cacheNames) {
            caches.add(new CaffeineCache(nameCof.getCacheName(),
                    Caffeine.newBuilder().recordStats()
                            .initialCapacity(nameCof.getInitialCapacity())
                            .maximumSize(nameCof.getMaximumSize())
                            .expireAfterWrite(nameCof.getExpsNum(), nameCof.getTimeUnit()).build()));
        }
        return caches;
    }
}
@EnableCaching
@Configuration
@Slf4j
public class RedisConfig extends JCacheConfigurerSupport {
    @Value("${spring.redis.database}")
    private int dbIndex;
    @Value("${spring.redis.host}")
    private String host;
    @Value("${spring.redis.port}")
    private int port;
    @Value("${spring.redis.password}")
    private String password;
    @Value("${spring.redis.timeout}")
    private int timeout;
    @Value("${spring.redis.lettuce.pool.max-active}")
    private int redisPoolMaxActive;
    @Value("${spring.redis.lettuce.pool.max-wait}")
    private int redisPoolMaxWait;
    @Value("${spring.redis.lettuce.pool.max-idle}")
    private int redisPoolMaxIdle;
    @Value("${spring.redis.lettuce.pool.min-idle}")
    private int redisPoolMinIdle;

    public RedisSerializer<Object> redisSerializer() {
        ObjectMapper objectMapper = new ObjectMapper();
        objectMapper.configure(JsonParser.Feature.ALLOW_COMMENTS, true);
        objectMapper.configure(SerializationFeature.INDENT_OUTPUT, true);
        objectMapper.configure(DeserializationFeature.ACCEPT_SINGLE_VALUE_AS_ARRAY, true);
        objectMapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
        objectMapper.configure(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS, false);
        objectMapper.configure(SerializationFeature.FAIL_ON_EMPTY_BEANS, false);
        objectMapper.configure(DeserializationFeature.FAIL_ON_INVALID_SUBTYPE, false);
        objectMapper.activateDefaultTyping(LaissezFaireSubTypeValidator.instance, ObjectMapper.DefaultTyping.NON_FINAL, JsonTypeInfo.As.PROPERTY);
        return new GenericJackson2JsonRedisSerializer(objectMapper);
    }


    public LettuceConnectionFactory lettuceConnectionFactory() {
        RedisStandaloneConfiguration configuration = new RedisStandaloneConfiguration();
        configuration.setHostName(host);
        configuration.setPort(port);
        configuration.setDatabase(dbIndex);
        if (!ObjectUtils.isEmpty(password)) {
            RedisPassword redisPassword = RedisPassword.of(password);
            configuration.setPassword(redisPassword);
        }
        GenericObjectPoolConfig genericObjectPoolConfig = new GenericObjectPoolConfig();
        genericObjectPoolConfig.setMaxTotal(redisPoolMaxActive);
        genericObjectPoolConfig.setMinIdle(redisPoolMinIdle);
        genericObjectPoolConfig.setMaxIdle(redisPoolMaxIdle);
        genericObjectPoolConfig.setMaxWaitMillis(redisPoolMaxWait);
        LettucePoolingClientConfiguration.LettucePoolingClientConfigurationBuilder builder = LettucePoolingClientConfiguration.builder();
        builder.poolConfig(genericObjectPoolConfig);
        builder.commandTimeout(Duration.ofSeconds(timeout));
        LettuceConnectionFactory connectionFactory = new LettuceConnectionFactory(configuration, builder.build());
        connectionFactory.afterPropertiesSet();
        return connectionFactory;
    }

    @Bean(CacheEnum.CacheTemplates.REDIS_CACHE_TEMPLATE)
    public RedisTemplate<String, Object> redisTemplate() {
        RedisTemplate<String, Object> redisTemplate = new RedisTemplate();
        redisTemplate.setKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashKeySerializer(new StringRedisSerializer());
        redisTemplate.setHashValueSerializer(redisSerializer());
        redisTemplate.setValueSerializer(redisSerializer());
        redisTemplate.setConnectionFactory(lettuceConnectionFactory());
        redisTemplate.afterPropertiesSet();
        return redisTemplate;
    }

    @Bean(CacheEnum.CacheManager.REDIS_MANAGER)
    @Primary
    public CacheManager redisCacheManager() {
        RedisCacheConfiguration defConfig = RedisCacheConfiguration.defaultCacheConfig();
        ImmutableSet.Builder<String> cacheNames = ImmutableSet.builder();
        ImmutableMap.Builder<String, RedisCacheConfiguration> cacheConfig = ImmutableMap.builder();
        HashMap<String, Duration> exps = new HashMap<>();
        exps.put(CacheEnum.RedisCacheNameExpGroup.AN_MINUTE, Duration.ofMinutes(1));
        exps.put(CacheEnum.RedisCacheNameExpGroup.FIVE_MINUTES, Duration.ofMinutes(5));
        exps.put(CacheEnum.RedisCacheNameExpGroup.AN_HOUR, Duration.ofHours(1));
        exps.put(CacheEnum.RedisCacheNameExpGroup.TWO_HOUR, Duration.ofHours(2));
        exps.put(CacheEnum.RedisCacheNameExpGroup.FIVE_HOURS, Duration.ofHours(5));
        exps.put(CacheEnum.RedisCacheNameExpGroup.ONE_DAY, Duration.ofDays(1));
        exps.put(CacheEnum.RedisCacheNameExpGroup.TWO_DAY, Duration.ofDays(2));
        for (String cacheName : exps.keySet()) {
            defConfig = defConfig.entryTtl(exps.get(cacheName));
            cacheConfig.put(cacheName, defConfig);
            cacheNames.add(cacheName);
        }
        return RedisCacheManager.builder(lettuceConnectionFactory())
                .initialCacheNames(cacheNames.build())
                .withInitialCacheConfigurations(cacheConfig.build())
                .build();
    }

    @Override
    public CacheErrorHandler errorHandler() {
        return new CacheErrorHandler() {
            @Override
            public void handleCacheGetError(RuntimeException exception, Cache cache, Object key) {
                log.error("handleCacheGetError!!!->{}", exception.getMessage());
            }

            @Override
            public void handleCachePutError(RuntimeException exception, Cache cache, Object key, Object value) {
                log.error("handleCachePutError!!!->{}", exception.getMessage());
            }

            @Override
            public void handleCacheEvictError(RuntimeException exception, Cache cache, Object key) {
                log.error("handleCacheEvictError!!!->{}", exception.getMessage());
            }

            @Override
            public void handleCacheClearError(RuntimeException exception, Cache cache) {
                log.error("handleCacheClearError!!!->{}", exception.getMessage());
            }
        };
    }
}
  • ③. 测试
    @GetMapping("/testUser")
    @Cacheable(cacheNames = {CacheEnum.RedisCacheNameExpGroup.AN_MINUTE, 
    		CacheEnum.RedisCacheNameExpGroup.FIVE_MINUTES}, // 缓存组概念
            cacheManager = CacheEnum.CacheManager.REDIS_MANAGER, // 使用哪个manager管理
            condition = "#a0>2", // 满足条件
            key = "#root.method.name", // key = 当前方法名词
            unless = "#result==null || #result.size()==0") // 值为空无效
    //key = "'" + CacheEnum.RedisCacheKeys.SSM_USER_KEY + "'" + "+#userId", unless = "#result==null || #result.size()==0")
    public List<Integer> testUser(int userId) {
        return Lists.newArrayList(userId);
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1066991.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

听说C++有类和对象,所以好好八卦一下

文章目录 前言Ⅰ. 了解面向过程和面向对象Ⅱ. 类的引入和定义Ⅲ. 类的访问限定符及封装0x00 访问限定符0x01 封装 Ⅳ. 类的作用域Ⅴ. 类的实例化Ⅵ. 类对象模型0x00 类对象大小0x01 类对象存储方式 Ⅶ. this指针 前言 亲爱的夏目友人帐的小伙伴们&#xff0c;今天我们继续讲解…

黑豹程序员-架构师学习路线图-百科:Database数据库

文章目录 1、什么是Database2、发展历史3、数据库排行网4、总结 1、什么是Database 当今世界是一个充满着数据的互联网世界&#xff0c;各处都充斥着大量的数据。即这个互联网世界就是数据世界。 支撑这个数据世界的基石就是数据库&#xff0c;数据库也可以称为数据的仓库。 …

应用层协议 HTTP

一、应用层协议 我们已经学过 TCP/IP , 已然知道数据能从客户端进程经过路径选择跨网络传送到服务器端进程。 我们还需要知道的是&#xff0c;我们把数据从 A 端传送到 B 端&#xff0c; TCP/IP 解决的是顺丰的功能&#xff0c;而两端还要对数据进行加工处理或者使用&#xf…

泛微E-Office前台文件读取漏洞

一、漏洞描述 泛微E-Office是一款企业级的全流程办公自动化软件&#xff0c;它包括协同办公、文档管理、知识管理、工作流管理等多个模块&#xff0c;涵盖了企业日常工作中的各个环节。泛微E-Office能够帮助企业实现全流程数字化、自动化&#xff0c;提高工作效率和管理质量&a…

网络安全--安全认证、IPSEC技术

目录 1. 什么是数据认证&#xff0c;有什么作用&#xff0c;有哪些实现的技术手段&#xff1f; 2. 什么是身份认证&#xff0c;有什么作用&#xff0c;有哪些实现的技术手段&#xff1f; 3. 什么是VPN技术&#xff1f; 4. VPN技术有哪些分类&#xff1f; 5. IPSEC技术能够…

【Redis实战】击穿+雪崩+穿透

架构 短信登录 基于session实现登录 流程图 代码实现 Slf4j Service public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements IUserService {/*** session用户key*/public static final String USER_CONSTANT "user";Overridepub…

Springboot学习笔记——2

Springboot学习笔记——2 一、打包与运行1.1、程序打包与运行&#xff08;windows版&#xff09;1.2、打包插件1.3、Boot工程快速启动&#xff08;Linux版&#xff09; 二、配置高级2.1、临时属性设置2.2、配置程序四级分类2.3、自定义配置文件 三、多环境开发3.1、多环境开发&…

【计算机网络-自顶向下方法】应用层(HTTP、FTP)

1. Principles of network applications 创建一个网络应用 创建一个网络应用的核心&#xff0c;是编写一个分布式程序&#xff0c;使其可以运行在不同的端系统上&#xff0c;并能通过网络相互通信。&#xff08;例如&#xff0c;web服务器软件与浏览器软件&#xff09;   应…

为什么很多编程语言中数组都是从0开始编号?

文章来源于极客时间前google工程师−王争专栏。 如何实现随机访问? 什么是数组&#xff1f; 数组&#xff08;Array&#xff09;是一种线性表数据结构。它用一组连续的内存空间&#xff0c;来存储一组具有相同类型的数据。 线性表&#xff0c;顾名思义&#xff0c;线性表就…

林沛满-TCP之在途字节数

本文整理自&#xff1a;《Wireshark网络分析的艺术 第1版》 作者&#xff1a;林沛满 著 出版时间&#xff1a;2016-02 我一直谨记斯蒂芬霍金的金玉良言—每写一道数学公式就会失去一半读者。不过为了深度分析网络包&#xff0c;有时候是不得不计算的&#xff0c;好在小学一年级…

DirectX12_Windows_GameDevelop_3:Direct3D的初始化

引言 查看龙书时发现&#xff0c;第四章介绍预备知识的代码不太利于学习。因为它不像是LearnOpenGL那样从头开始一步一步教你敲代码&#xff0c;导致你没有一种整体感。如果你把它当作某一块的代码进行学习&#xff0c;你跟着敲会发现&#xff0c;总有几个变量是没有定义的。这…

【C++设计模式之策略模式】分析及示例

描述 策略模式&#xff08;Strategy Pattern&#xff09;是一种行为型设计模式&#xff0c;它允许在运行时根据不同的情况选择算法的行为。该模式将算法的定义封装成一组易于切换和替换的类&#xff0c;使得算法可以独立于其使用者进行变化。 原理 策略模式通过将具体的算法…

FastThreadLocal 快在哪里 ?

FastThreadLocal 快在哪里 &#xff1f; 引言FastThreadLocalset如何获取当前线程私有的InternalThreadLocalMap &#xff1f;如何知道当前线程使用到了哪些FastThreadLocal实例 ? get垃圾回收 小结 引言 FastThreadLocal 是 Netty 中造的一个轮子&#xff0c;那么为什么放着…

前端到底有多卷?可以转行吗?

我前几天招人&#xff0c;前后端各招一个人。 后端一天大概60多个投简历的。 前端岗位发出去&#xff0c;我吃了个饭&#xff0c;1小时回来 收到300多份简历…… 是一位HR回复的前端卷到什么程度的回答&#xff01; 下面我们来看两组官方纰漏的数据&#xff1a; 2023届全国高…

Git 学习笔记 | Git 的简介与历史

Git 学习笔记 | Git 的简介与历史 Git 学习笔记 | Git 的简介与历史Git 简介Git 历史 Git 学习笔记 | Git 的简介与历史 Git 简介 Git是分布式版本控制系统&#xff08;Distributed Version Control System&#xff0c;简称 DVCS&#xff09;&#xff0c;分为两种类型的仓库&…

100M跨境电商服务器能同时容纳多少人访问?

​  随着“出国”“出海”需求的业务量增多&#xff0c;网络的不断发展&#xff0c;服务商开始在带宽资源配备上作出各种改进。无论是纯国际带宽还是优化回国带宽租用&#xff0c;我们都可以独享&#xff0c;并且享受到大带宽。一般&#xff0c;做跨境电商业务的群体&#xf…

黑客都是土豪吗?真实情况是什么?

黑客的利益链条真的这么大这么好么,连最外围的都可以靠信息不对称赚普通人大学毕业上班族想都不敢想的金钱数目,黑客们是不是基本都是土豪 网络技术可以称为黑客程度的技术是不是真的很吃香&#xff1f;如果大部分大学生的智力资源都用在学习网络技术&#xff0c;会不会出现僧…

如何杜绝聊天泄密事件的发生呢(企业如何管理通讯工具,防止员工聊天泄密)

在现代企业中&#xff0c;员工之间的沟通是必不可少的。然而&#xff0c;随着科技的发展&#xff0c;员工聊天泄密的风险也日益增加。企业需要采取一系列措施来防止员工聊天泄密&#xff0c;以保护企业的核心竞争力和商业机密。本文将介绍一些有效的防止员工聊天泄密的方法。 1…

PHP8的匿名类-PHP8知识详解

PHP8支持通过new class 来实例化一个匿名类。所谓匿名类&#xff0c;就是指没有名称的类&#xff0c;只能在创建时使用new语句来声明它们。 匿名类是一种没有命名的即时类&#xff0c;可以用于简单的对象封装和实现接口。 以下是PHP 8中匿名类的基本语法示例&#xff1a; $ob…

Springboot使用Aop保存接口请求日志到mysql(及解决Interceptor拦截器中引用mapper和service为null)

一、Springboot使用Aop保存接口请求日志到mysql 1、添加aop依赖 <!-- aop日志 --><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-aop</artifactId></dependency> 2、新建接口保存数据…