Spring Boot 缓存 Cache 入门

Spring Boot 缓存 Cache 入门

1.概述

在系统访问量越来越大之后,往往最先出现瓶颈的往往是数据库。而为了减少数据库的压力,我们可以选择让产品砍掉消耗数据库性能的需求。 当然也可以引入缓存,在引入缓存之后,我们的读操作的代码,往往代码如下:

// UserService.java

@Autowired
private UserMapper userMapper; // 读取 DB

@Autowired
private UserCacheDao userCacheDao; // 读取 Cache

public UserDO getUser(Integer id) {
    // 从 Cache 中,查询用户信息
    UserDO user = userCacheDao.get(id);
    if (user != null) {
        return user;
    }
    // 如果 Cache 查询不到,从 DB 中读取
    user = userMapper.selectById(id);
    if (user != null) { // 非空,则缓存到 Cache 中
        userCacheDao.put(user);
    }
    // 返回结果
    return user;
}
  • 这段代码,是比较常用的缓存策略,俗称**“被动写”**。整体步骤如下:
    • 1)首先,从 Cache 中,读取用户缓存。如果存在,则直接返回。
    • 2)然后,从 DB 中,读取用户数据。如果存在,写入 Cache 中。
    • 3)最后,返回 DB 的查询结果。

Spring Cache 缓存让我们可以像使用 @Transactional 声明式事务,使用 Spring Cache 提供的 @Cacheable 等注解,😈 声明式缓存。而在实现原理上,也是基于 Spring AOP 拦截,实现缓存相关的操作。

下面,我们使用 Spring Cache 将 #getUser(Integer id) 方法进行简化。代码如下:

// UserService.java
public UserDO getUser2(Integer id) {
    return userMapper.selectById(id);
}

// UserMapper.java
@Cacheable(value = "users", key = "#id")
UserDO selectById(Integer id);
  • 在 UserService 的 #getUser2(Integer id) 方法上,我们直接调用 UserMapper ,从 DB 中查询数据。
  • 在 UserMapper 的 #selectById(Integer id) 方法上,有 @Cacheable 注解。Spring Cache 会拦截有 @Cacheable 注解的方法,实现“被动写”的逻辑。

2. 注解

在入门 Spring Cache 之前,我们先了解下其提供的所有注解:

  • @Cacheable
  • @CachePut
  • @CacheEvict
  • @CacheConfig
  • @Caching
  • @EnableCaching

2.1 @Cacheable

@Cacheable 注解,添加在方法上,缓存方法的执行结果。执行过程如下:

  • 1)首先,判断方法执行结果的缓存。如果有,则直接返回该缓存结果。
  • 2)然后,执行方法,获得方法结果。
  • 3)之后,根据是否满足缓存的条件。如果满足,则缓存方法结果到缓存。
  • 4)最后,返回方法结果。

@Cacheable 注解的常用属性,如下:

  • cacheNames 属性:缓存名。必填[] 数组,可以填写多个缓存名。
  • values 属性:和 cacheNames 属性相同,是它的别名。
  • key 属性:缓存的 key 。允许空。
    • 如果为空,则默认方法的所有参数进行组合。
    • 如果非空,则需要按照 SpEL(Spring Expression Language) 来配置。例如说,@Cacheable(value = "users", key = "#id") ,使用方法参数 id 的值作为缓存的 key 。
  • condition 属性:基于方法入参,判断要缓存的条件。允许空。
    • 如果为空,则不进行入参的判断。
    • 如果非空,则需要按照 SpEL(Spring Expression Language) 来配置。例如说,@Cacheable(condition="#id > 0") ,需要传入的 id 大于零。
  • unless 属性:基于方法返回,判断不缓存的条件。允许空。
    • 如果为空,则不进行入参的判断。
    • 如果非空,则需要按照 SpEL(Spring Expression Language) 来配置。例如说,@Cacheable(unless="#result == null") ,如果返回结果为 null ,则不进行缓存。
    • 要注意,conditionunless 都是条件属性,差别在于前者针对入参,后者针对结果。

@Cacheable 注解的不常用属性,如下:

  • keyGenerator 属性:自定义 key 生成器 KeyGenerator Bean 的名字。允许空。如果设置,则 key 失效。
  • cacheManager 属性:自定义缓存管理器 CacheManager Bean 的名字。允许空。一般不填写,除非有多个 CacheManager Bean 的情况下。
  • cacheResolver 属性:自定义缓存解析器 CacheResolver Bean 的名字。允许空。
  • sync 属性,在获得不到缓存的情况下,是否同步执行方法。
    • 默认为 false ,表示无需同步。
    • 如果设置为 true ,则执行方法时,会进行加锁,保证同一时刻,有且仅有一个方法在执行,其它线程阻塞等待。通过这样的方式,避免重复执行方法。注意,该功能的实现,需要参考第三方缓存的具体实现。

2.2 @CachePut

@CachePut 注解,添加在方法上,缓存方法的执行结果。不同于 @Cacheable 注解,它的执行过程如下:

  • 1)首先,执行方法,获得方法结果。也就是说,无论是否有缓存,都会执行方法
  • 2)然后,根据是否满足缓存的条件。如果满足,则缓存方法结果到缓存。
  • 3)最后,返回方法结果。

一般来说,使用方式如下:

  • @Cacheable:搭配操作,实现缓存的被动写。
  • @CachePut:配置操作,实现缓存的主动写。

@Cacheable 注解的属性,和 @Cacheable 注解的属性,基本一致,只少一个 sync 属性。

2.3 @CacheEvict

@CacheEvict 注解,添加在方法上,删除缓存。

相比 @CachePut 注解,它额外多了两个属性:

  • allEntries 属性,是否删除缓存名( cacheNames )下,所有 key 对应的缓存。默认为 false ,只删除指定 key 的缓存。
  • beforeInvocation 属性,是否在方法执行删除缓存。默认为 false ,在方法执行删除缓存。

2.4 @Caching

@Caching 注解,添加在方法上,可以组合使用多个 @Cacheable@CachePut@CacheEvict 注解。不太常用,可以暂时忽略。

2.5 @CacheConfig

@CacheConfig 注解,添加在类上,共享如下四个属性的配置:

  • cacheNames
  • keyGenerator
  • cacheManager
  • cacheResolver

2.6 @EnableCaching

@EnableCaching 注解,标记开启 Spring Cache 功能,所以一定要添加。代码如下:

// EnableCaching.java

boolean proxyTargetClass() default false;

AdviceMode mode() default AdviceMode.PROXY;

int order() default Ordered.LOWEST_PRECEDENCE;

3. Spring Boot 集成

在 Spring Boot 里,提供了 spring-boot-starter-cache 库,实现 Spring Cache 的自动化配置,通过 CacheAutoConfiguration 配置类。

在 Java 后端开发中,常见的缓存工具和框架列举如下:

  • 本地缓存:Guava LocalCache、Ehcache、Caffeine 。

    Ehcache 的功能更加丰富,Caffeine 的性能要比 Guava LocalCache 好。

  • 分布式缓存:Redis、Memcached、Tair 。

    Redis 最为主流和常用。

4.Redis示例

4.1引入依赖

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <!-- 实现对数据库连接池的自动化配置 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-jdbc</artifactId>
        </dependency>
        <dependency> <!-- 本示例,我们使用 MySQL -->
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!-- 实现对 MyBatis Plus 的自动化配置 -->
        <dependency>
            <groupId>com.baomidou</groupId>
            <artifactId>mybatis-plus-boot-starter</artifactId>
        </dependency>

        <!-- 实现对 Caches 的自动化配置 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-cache</artifactId>
        </dependency>

        <!-- 实现对 Spring Data Redis 的自动化配置 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-data-redis</artifactId>
            <exclusions>
                <!-- 去掉对 Lettuce 的依赖,因为 Spring Boot 优先使用 Lettuce 作为 Redis 客户端 -->
                <exclusion>
                    <groupId>io.lettuce</groupId>
                    <artifactId>lettuce-core</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <!-- 引入 Jedis 的依赖,这样 Spring Boot 实现对 Jedis 的自动化配置 -->
        <dependency>
            <groupId>redis.clients</groupId>
            <artifactId>jedis</artifactId>
        </dependency>
        <!--引入lombok-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
        </dependency>
        <!-- 方便等会写单元测试 -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

Spring Data 使用 Redis 作为缓存的方案的时候,底层使用的是 Spring Data 提供的 RedisTemplate ,所以我们引入 spring-boot-starter-data-redis 依赖,实现对 RedisTemplate 的自动化配置。

4.2应用配置文件

spring:
  # datasource 数据源配置内容
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/llp?useSSL=false&useUnicode=true&characterEncoding=UTF-8
    driver-class-name: com.mysql.jdbc.Driver
    username: root
    password: root
  # 对应 RedisProperties 类
  redis:
    host: 127.0.0.1
    port: 6379
    password: # Redis 服务器密码,默认为空。生产中,一定要设置 Redis 密码!
    database: 0 # Redis 数据库号,默认为 0 。
    timeout: 0 # Redis 连接超时时间,单位:毫秒。
    # 对应 RedisProperties.Jedis 内部类
    jedis:
      pool:
        max-active: 8 # 连接池最大连接数,默认为 8 。使用负数表示没有限制。
        max-idle: 8 # 默认连接数最小空闲的连接数,默认为 8 。使用负数表示没有限制。
        min-idle: 0 # 默认连接池最小空闲的连接数,默认为 0 。允许设置 0 和 正数。
        max-wait: -1 # 连接池最大阻塞等待时间,单位:毫秒。默认为 -1 ,表示不限制。
  # cache 缓存配置内容
  cache:
    type: redis

# mybatis-plus 配置内容
mybatis-plus:
  configuration:
    map-underscore-to-camel-case: true # 虽然默认为 true ,但是还是显示去指定下。
  global-config:
    db-config:
      id-type: auto # ID 主键自增
      logic-delete-value: 1 # 逻辑已删除值(默认为 1)
      logic-not-delete-value: 0 # 逻辑未删除值(默认为 0)
  mapper-locations: classpath*:mapper/*.xml
  type-aliases-package: com.llp.cache.dataobject

# logging
logging:
  level:
    # dao 开启 debug 模式 mybatis 输入 sql
    com:
      llp:
        cache:
          mapper: debug

  • spring.datasource 配置项下,设置数据源相关的配置。
  • spring.cache配置项下,设置 Cache 相关的配置。
    • type 属性,设置 Cache 使用方案为 Redis 。
  • spring.redis 配置项下,设置 Spring Data Redis 相关的配置。如果没有使用过 Spring Data Redis 的胖友,不用慌,照着改就好。
  • mybatis-plus 配置项下,设置 MyBatis-Plus 相关的配置。
  • logging 配置项,设置打印 SQL 日志,方便我们查看是否读取了 DB 。

4.3 Application启动类

@EnableCaching //开启缓存支持
@MapperScan(basePackages = {"com.llp.cache.mapper"})
@SpringBootApplication
public class CacheApplication {
    public static void main(String[] args) {
        SpringApplication.run(CacheApplication.class, args);
    }
}

4.4UserDO

@TableName(value = "users")
@Data
public class UserDO {

    /**
     * 用户编号
     */
    private Integer id;
    /**
     * 账号
     */
    private String username;
    /**
     * 密码(明文)
     *
     * ps:生产环境下,千万不要明文噢
     */
    private String password;
    /**
     * 创建时间
     */
    private Date createTime;
    /**
     * 是否删除
     */
    @TableLogic
    private Integer deleted;

}

4.5UserMapper

import com.baomidou.mybatisplus.core.mapper.BaseMapper;
import com.llp.cache.dataobject.UserDO;
import org.springframework.cache.annotation.CacheConfig;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.CachePut;
import org.springframework.cache.annotation.Cacheable;
import org.springframework.stereotype.Repository;

@Repository
//统一配置该 UserMapper 使用的缓存名为 users ,类的方法上使用cacheNames将不会生效
@CacheConfig(cacheNames = "users")
public interface UserMapper extends BaseMapper<UserDO> {

    /**
     * @Cacheable 注解,添加在方法上,缓存方法的执行结果。执行过程如下:
     *
     * 1)首先,判断方法执行结果的缓存。如果有,则直接返回该缓存结果。
     * 2)然后,执行方法,获得方法结果。
     * 3)之后,根据是否满足缓存的条件。如果满足,则缓存方法结果到缓存。
     * 4)最后,返回方法结果。
     * 只有一个参数: #a0 或 #p0
     * @param id
     * @return
     */
    @Cacheable(key = "#a0")
    UserDO selectById(Integer id);

    /**
     * @CachePut 注解,添加在方法上,缓存方法的执行结果。不同于 @Cacheable 注解,它的执行过程如下:
     *
     * 1)首先,执行方法,获得方法结果。也就是说,无论是否有缓存,都会执行方法。
     * 2)然后,根据是否满足缓存的条件。如果满足,则缓存方法结果到缓存。
     * 3)最后,返回方法结果。
     * @param user
     * @return
     */
    @CachePut(key = "#user.id")
    default UserDO insert0(UserDO user) {
        // 插入记录
        this.insert(user);
        // 返回用户
        return user;
    }
	
    //@CacheEvict清理掉缓存,常用于修改和删除
    @CacheEvict(key = "#p0")
    int deleteById(Integer id);

}

4.6UserMapperTest

@RunWith(SpringRunner.class)
@SpringBootTest(classes = CacheApplication.class)
public class UserMapperTest {

    private static final String CACHE_NAME_USER = "users";

    @Autowired
    private UserMapper userMapper;

    @Autowired
    private CacheManager cacheManager;

    @Test
    public void testCacheManager() {
        System.out.println(cacheManager);
    }

    @Test
    public void testSelectById() {
        // 这里,胖友事先插入一条 id = 1 的记录。
        Integer id = 1;

        // <1.1> 查询 id = 1 的记录
        UserDO user = userMapper.selectById(id);
        System.out.println("user:" + user);
        // <1.2> 判断缓存中,是不是存在
        Assert.assertNotNull("缓存为空", cacheManager.getCache(CACHE_NAME_USER).get(user.getId(), UserDO.class));

        // <2> 查询 id = 1 的记录
        user = userMapper.selectById(id);
        System.out.println("user:" + user);
    }


    @Test
    public void testInsert() {
        // <1> 插入记录
        UserDO user = new UserDO();
        user.setUsername(UUID.randomUUID().toString()); // 随机账号,因为唯一索引
        user.setPassword("llp");
        user.setCreateTime(new Date());
        user.setDeleted(0);
        userMapper.insert0(user);

        // <2> 判断缓存中,是不是存在
        Assert.assertNotNull("缓存为空", cacheManager.getCache(CACHE_NAME_USER).get(user.getId(), UserDO.class));
    }

    @Test
    public void testDeleteById() {
        // <1> 插入记录,为了让缓存里有记录
        UserDO user = new UserDO();
        user.setUsername(UUID.randomUUID().toString()); // 随机账号,因为唯一索引
        user.setPassword("llp");
        user.setCreateTime(new Date());
        user.setDeleted(0);
        userMapper.insert0(user);
        // <2> 判断缓存中,是不是存在
        Assert.assertNotNull("缓存为空", cacheManager.getCache(CACHE_NAME_USER).get(user.getId(), UserDO.class));

        // <3.1> 删除记录,为了让缓存被删除
        userMapper.deleteById(user.getId());
        // <3.2> 判断缓存中,是不是存在
        Assert.assertNull("缓存不为空", cacheManager.getCache(CACHE_NAME_USER).get(user.getId(), UserDO.class));
    }

}

4.7过期时间

在 Spring Data 使用 Redis 作为缓存方案时,默认情况下是永不过期的。

127.0.0.1:6379> ttl users::1
(integer) -1
  • 在 Redis 命令行中,我们可以看到 users::1 的过期时间为 -1 永不过期。

虽然说,我们可以通 spring.cache.redis.time-to-live 配置项,设置过期时间。但是,它是全局的统一的。这样在实际使用时,是无法满足我们希望不同的缓存,使用不同的过期时间。

spring:
  # datasource 数据源配置内容
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/llp?useSSL=false&useUnicode=true&characterEncoding=UTF-8
    driver-class-name: com.mysql.jdbc.Driver
    username: root
    password: root
  # 对应 RedisProperties 类
  redis:
    host: 127.0.0.1
    port: 6379
    password: # Redis 服务器密码,默认为空。生产中,一定要设置 Redis 密码!
    database: 0 # Redis 数据库号,默认为 0 。
    timeout: 0 # Redis 连接超时时间,单位:毫秒。
  cache:
    type: redis
    redis:
      time-to-live: 1h

4.8 Cacheable(Redis)缓存失效时间解决方案

问题

@Cacheable注解不支持配置过期时间,所有需要通过配置CacheManneg来配置默认的过期时间和针对每个类或者是方法进行缓存失效时间配置。

解决

可以采用如下的配置信息来解决的设置失效时间问题

  • 配置信息
import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.springframework.cache.CacheManager;
import org.springframework.cache.interceptor.KeyGenerator;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.redis.cache.RedisCacheConfiguration;
import org.springframework.data.redis.cache.RedisCacheManager;
import org.springframework.data.redis.cache.RedisCacheWriter;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.serializer.Jackson2JsonRedisSerializer;
import org.springframework.data.redis.serializer.RedisSerializationContext;

import java.lang.reflect.Method;
import java.time.Duration;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

@Configuration
public class CacheConfig {

    @Bean
    public CacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {
        return new RedisCacheManager(
                RedisCacheWriter.nonLockingRedisCacheWriter(redisConnectionFactory),
                this.getRedisCacheConfigurationWithTtl(30 * 60), // 默认策略,未配置的 key 会使用这个
                this.getRedisCacheConfigurationMap() // 指定 key 策略
        );
    }

    private Map<String, RedisCacheConfiguration> getRedisCacheConfigurationMap() {
        Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();
        //SsoCache和BasicDataCache进行过期时间配置
        redisCacheConfigurationMap.put("SsoCache", this.getRedisCacheConfigurationWithTtl(24 * 60 * 60));
        redisCacheConfigurationMap.put("BasicDataCache", this.getRedisCacheConfigurationWithTtl(30 * 60));
        return redisCacheConfigurationMap;
    }

    private RedisCacheConfiguration getRedisCacheConfigurationWithTtl(Integer seconds) {
        Jackson2JsonRedisSerializer<Object> jackson2JsonRedisSerializer = new Jackson2JsonRedisSerializer<>(Object.class);
        ObjectMapper om = new ObjectMapper();
        om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);
        om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
        jackson2JsonRedisSerializer.setObjectMapper(om);

        RedisCacheConfiguration redisCacheConfiguration = RedisCacheConfiguration.defaultCacheConfig();
        redisCacheConfiguration = redisCacheConfiguration.serializeValuesWith(
                RedisSerializationContext
                        .SerializationPair
                        .fromSerializer(jackson2JsonRedisSerializer)
        ).entryTtl(Duration.ofSeconds(seconds));

        return redisCacheConfiguration;
    }

    //指定缓存key的生成方式
    @Bean
    public KeyGenerator wiselyKeyGenerator() {
        KeyGenerator keyGenerator = new KeyGenerator() {
            @Override
            public Object generate(Object target, Method method, Object... params) {
                StringBuilder sb = new StringBuilder();
                sb.append(target.getClass().getName());
                sb.append("." + method.getName());
                if (params == null || params.length == 0 || params[0] == null) {
                    return null;
                }
                String join = String.join("&", Arrays.stream(params).map(Object::toString).collect(Collectors.toList()));
                String format = String.format("%s{%s}", sb.toString(), join);
                //log.info("缓存key:" + format);
                return format;
            }
        };
        return keyGenerator;
    }

}

  • 使用方式
@Repository
//统一配置该 UserMapper 使用的缓存名为 users ,方法中使用了cacheNames,则类的方法上使用cacheNames将不会生效
@CacheConfig(cacheNames = "users")
public class SsoCache{
    @Cacheable(value = "BasicDataCache",keyGenerator = "wiselyKeyGenerator")
    UserDO selectById(Integer id);
    
    //二者选其一,可以使用value上的信息,来替换类上cacheNames的信息
    @Cacheable(value = "SsoCache",keyGenerator = "wiselyKeyGenerator")
    UserDO selectById(Integer id); 
}

BasicDataCache

image-20230725153809280

SsoCache

image-20230725154444466

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

嵌入式硬件系统的基本组成

嵌入式硬件系统的基本组成 嵌入式系统的硬件是以包含嵌入式微处理器的SOC为核心&#xff0c;主要由SOC、总线、存储器、输入/输出接口和设备组成。 嵌入式微处理器 每个嵌入式系统至少包含一个嵌入式微处理器 嵌入式微处理器体系结构可采用冯.诺依曼&#xff08;Von Neumann&…

leetcode 55. 跳跃游戏

2023.7.29 本题不用纠结于可以跳几步&#xff0c;可以聚焦于覆盖范围&#xff0c;即 当前位置当前跳数 能够覆盖的范围&#xff0c;若这个范围足以到达最后一个位置&#xff0c;则返回true&#xff1b;若for循环结束&#xff0c;则还没返回true&#xff0c;则返回false。 下面看…

24考研数据结构-第一章 绪论

数据结构 引用文章第一章&#xff1a;绪论1.0 数据结构在学什么1.1 数据结构的基本概念1.2 数据结构的三要素1.3 算法的基本概念1.4 算法的时间复杂度1.4.1 渐近时间复杂度1.4.2 常对幂指阶1.4.3 时间复杂度的计算1.4.4 最好与最坏时间复杂度 1.5 算法的空间复杂度1.5.1 空间复…

如何建立Docker私有仓库?

文章目录 docker私有仓库harborHarbor仓库部署Harbor仓库使用 docker私有仓库 Docker 私有仓库是一个用于存储和管理 Docker 镜像的私有存储库。它允许你在内部网络中创建和管理 Docker 镜像&#xff0c;并提供了更好的安全性和控制&#xff0c;因为你可以完全控制谁能够访问和…

jmeter接口测试、压力测试简单实现

jmeter测试的组件执行顺序&#xff1a; 测试计划—>线程组—>配置元件—>前置处理器—>定时器—>逻辑控制器—>取样器—>后置处理器—>断言—>监听器 组件的作用范围&#xff1a; 同级组件同级组件下的子组件父组件 目前市面上的三类接口 1、基…

PyCharm安装pip依赖,如何添加国内镜像源?

目录 前言 PyCharm如何安装依赖 PyCharm如何配置国内镜像源 前言 首先我们都知道&#xff0c;使用pip安装依赖的方式&#xff0c;却很少有人知道使用PyCharm如何安装依赖。 PyCharm如何安装依赖 但你会发现&#xff0c;基本都是安装失败的&#xff0c;因为你是去外网下载的…

[JAVAee]文件操作-IO

本文章讲述了通过java对文件进行IO操作 IO:input/output,输入/输出. 建议配合文章末尾实例食用 目录 文件 文件的管理 文件的路径 文件的分类 文件系统的操作 File类的构造方法 File的常用方法 文件内容的读写 FileInputStream读取文件 构造方法 常用方法 Scan…

探索容器镜像安全管理之道

邓宇星&#xff0c;Rancher 中国软件架构师&#xff0c;7 年云原生领域经验&#xff0c;参与 Rancher 1.x 到 Rancher 2.x 版本迭代变化&#xff0c;目前负责 Rancher for openEuler(RFO)项目开发。 最近 Rancher v2.7.4 发布了&#xff0c;作为一个安全更新版本&#xff0c;也…

【bar堆叠图形绘制】

绘制条形图示例 在数据可视化中&#xff0c;条形图是一种常用的图表类型&#xff0c;用于比较不同类别的数据值。Python的matplotlib库为我们提供了方便易用的功能来绘制条形图。 1. 基本条形图 首先&#xff0c;我们展示如何绘制基本的条形图。假设我们有一个包含十个类别的…

ElasticSearch 7.x

前言 elastic表示可伸缩&#xff0c;search表示查询。所以es的核心即为查询。通常情况下&#xff0c;我们的数据可以分为三类&#xff1a;结构化数据、非结构化数据、半结构化数据。 结构化数据&#xff1a;一般会用特定的结构来组织和管理数据&#xff0c;表现为二维表结构。…

【Android常见问题(五)】- Flutter项目性能优化

文章目录 知识回顾前言源码分析1. 渲染过程2. 分析工具3. 优化方法合理使用const关键词合理使用组件管理着色器编译垃圾 知识回顾 前言 项目迭代开发一定程度后&#xff0c;性能优化是重中之重&#xff0c;其中包括了包体积&#xff0c;UI 渲染、交互等多个方面。 通过 Flutt…

码农该如何延长周末体验感

码农该如何延长周末体验感 码农该如何延长周末体验感 码农该如何延长周末体验感1.制定合理的工作计划&#xff1a;2.实践工作与生活的平衡&#xff1a;3.学习新技术或扩展知识领域4.参与开源项目或个人项目&#xff1a;5.与同事或朋友组织活动&#xff1a;6.自己写博客或者总结…

百万数据快速导入导出

百万数据快速导入 pom <dependency><groupId>com.alibaba</groupId><artifactId>easyexcel</artifactId><version>3.2.0</version></dependency>Resourceprivate SalariesListener salariesListener;private ExecutorService…

求解方程x^2=a的根,不使用库函数直接求解(不动点迭代法)

首先可以将方程两边同时加上x&#xff0c;&#xff0c;这时候两边同时再除以1x&#xff0c;就得到了&#xff0c;变形为。&#xff08;变性后的迭代式不唯一&#xff0c;这里随便选取一个&#xff09; 当x是准确值的时候&#xff0c;两边应该是相等的&#xff0c;如果x是近似值…

【Git】分支管理之创建、切换、合并、删除分支以及冲突处理

目录 一、理解分支 二、创建、切换、合并分支 三、删除分支 四、冲突处理 五、合并模式 六、合并策略 七、Bug分支处理 八、强制删除分支 一、理解分支 master其实就是一个指针 &#xff0c;他指向的是主分支最近一次commit。我们可以创建新的分支&#xff0c;在新的分…

Arcgis之 KML/KMZ文件转shp

一般我们在Goole Earth上勾画的区域导出后都为KML或者KMZ格式的&#xff0c;但无法在arcgis等软件上直接应用&#xff0c;故需进行一定的转换 1.打开ArcMap&#xff0c;选择ArcToolbox->Conversion Tools->From KML->KML To Layer 得到如下结果&#xff08;由于本KML…

请问学JavaScript 前要学html 和css 吗?

前言 html和css可以理解为是一个网站的骨架和皮肤&#xff0c;这两部分做好后整个网站的外观展示的完成度基本就有了个90%左右&#xff0c;所以在学习js前是需要学习html和css 的&#xff0c;这两部分不用花特别多的时间&#xff08;虽然css如果想做一些非常炫酷的效果个人认为…

什么是SVM算法?硬间隔和软间隔的分类问题

SVM全称是supported vector machine(支持向量机)&#xff0c;即寻找到一个超平面使样本分成两类&#xff0c;并且间隔最大。 SVM能够执行线性或⾮线性分类、回归&#xff0c;甚至是异常值检测任务。它是机器学习领域最受欢迎的模型之一。SVM特别适用于中小型复杂数据集的分类。…

MobaXterm通过SSH访问Ubuntu服务器遇到的一个问题

在Windows下的MobaXterm界面配置完ubuntuIP以后显示access denied&#xff0c;排查发现是因为在ubuntu那边忘记安装了SSH Serve&#xff0c;安装过程如下&#xff1a; 第一步&#xff1a;安装所需包 让我们从打开终端输入一些必要命令开始。 注意&#xff0c;在安装新的包或…

SpringBoot统一异常处理和统一返回格式

上篇博客我们讲解了使用AOP来进行统一的用户登录判断&#xff0c;其实像这种功能统一且使用较多的地方&#xff0c;都可以用AOP来处理&#xff0c;除了统⼀的⽤户登录判断之外&#xff0c;AOP 还可以实现&#xff1a; 统⼀⽇志记录统⼀⽅法执⾏时间统计&#xff08;在性能优化…