SpringBoot整合Cache缓存技术(二十一)
二八佳人体似酥,腰间仗剑斩愚夫。虽然不见人头落,暗里教君骨髓枯。
上一章简单介绍了SpringBoot整合Redis_Jedis版(二十),如果没有看过,请观看上一章
一. SpringCache
一.一 SpringCache 的出现
在SpringBoot 整合 Redis 时,无论是使用 Lettuce 还是使用 Jedis 连接池, 在查询单个对象,查询全部对象的时候,都是我们自己手动进行判断缓存的信息。
SpringBoot 使用 Lettuce 连接池时:
@Overridepublic User findById(int id) {log.info("先从缓存中查询用户编号为{} 是否存在",id);User user=redisUtil.get(KEY_PRE+id);if(user!=null){log.info(">>>>>>>>>>使用的是缓存中的数据");return user;}log.info(">>>>>>>>>>>从数据库中查询,并放置到缓存中");user= userMapper.findById(id);redisUtil.set(KEY_PRE+id,user);return user;}@Overridepublic List<User> findAll() {log.info("先从缓存中查询用户列表是否存在");List<User> userList= (List<User>) redisUtil.range(KEY_PRE+"ALL");if(!CollectionUtils.isEmpty(userList)){log.info(">>>>>>>>>>使用的是缓存中的数据");return userList;}log.info(">>>>>>>>>>>从数据库中查询,并放置到缓存中");userList= userMapper.findAll();redisUtil.leftPushAll(KEY_PRE+"ALL",userList);return userList;}
SpringBoot 使用 Jedis 连接池时:
@Overridepublic User findById(int id) {log.info("先从缓存中查询用户编号为{} 是否存在",id);User user=BeanConvertUtil.stringToBean(redisUtil.get(KEY_PRE+id,redisDB),User.class);if(user!=null){log.info(">>>>>>>>>>使用的是缓存中的数据");return user;}log.info(">>>>>>>>>>>从数据库中查询,并放置到缓存中");user= userMapper.findById(id);redisUtil.set(KEY_PRE+id,BeanConvertUtil.beanToString(user),redisDB);return user;}@Overridepublic List<User> findAll() {log.info("先从缓存中查询用户列表是否存在");List<String> userStringList= (List<String>) redisUtil.lrange(KEY_PRE+"ALL",0,-1,redisDB);List<User> userList=new ArrayList<>();if(!CollectionUtils.isEmpty(userStringList)){log.info(">>>>>>>>>>使用的是缓存中的数据");for(String userString:userStringList){userList.add(BeanConvertUtil.stringToBean(userString,User.class));}return userList;}log.info(">>>>>>>>>>>从数据库中查询,并放置到缓存中");userList= userMapper.findAll();for(User user:userList){redisUtil.lpush(redisDB,KEY_PRE+"ALL",BeanConvertUtil.beanToString(user));}return userList;}
可以发现, 两个都需要开发者自己手动处理缓存的信息。
并且,如果缓存的工具不同,处理的方式也不同。
实际上,这些与业务是没有太大的联系的。
我们希望能够有一种方式,能够通过简单的配置+注解,达到以前原生的写法就完美了。
@Overridepublic User findById(int id) {return userMapper.findById(id);}@Overridepublic List<User> findAll() {return userMapper.findAll();}
在这两个方法上,添加某个注解, 能够达到 有缓存走缓存,没有缓存走数据库查询,
然后将查询结果放置在缓存中,下一次查询时走缓存的结果,
并且与缓存的实现方式无关 (无论是 Lettuce 还是 Jedis)
有这么一种技术, 叫做SpringCache
一.二 SpringCache 的简单使用
按照 SpringBoot_Redis 项目,创建 SpringBoot_Cache 项目。
Redis服务器打开,使用的仍然是 database 15 数据库。
采用的是 springboot 2.2.13 版本。
目前数据库表 user 里面有三条记录
一.二.一 pom.xml 添加依赖
<!--依赖 data-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></dependency><dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-cache</artifactId></dependency><!--添加cache的依赖信息-->
一.二.二 application.yml 进行配置
与redis整合时一样,没有改变。
# 引入 数据库的相关配置
spring:datasource:driver-class-name: com.mysql.cj.jdbc.Driverurl: jdbc:mysql://localhost:3306/springboot?serverTimezone=GMT%2B8&useUnicode=true&characterEncoding=UTF-8&useSSL=false&allowMultiQueries=trueusername: rootpassword: abc123# 配置Redis的使用redis:database: 15 # 所使用的数据库 默认是0host: 127.0.0.1 #所使用的redis的主机地址port: 6379 # 端口号 默认是 6379password: zk123 # 密码timeout: 5000 # 超时时间 5000毫秒# 连接池 lettuce 的配置lettuce:pool:max-active: 100min-idle: 10max-wait: 100000
#整合mybatis时使用的
mybatis:#包别名type-aliases-package: top.yueshushu.learn.pojo#映射文件路径mapper-locations: classpath:mybatis/mapper/**/*.xmlconfiguration:#日志信息log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
一.二.三 启动类上 添加 @EnableCaching 注解
需要在启动类上 添加 @EnableCaching 注解, 开启缓存。
@MapperScan("top.yueshushu.learn.mapper")
@SpringBootApplication
//开启缓存
@EnableCaching
public class RedisApplication {public static void main(String[] args) {SpringApplication.run(RedisApplication.class,args);System.out.println("运行 Redis Cache缓存");}
}
一.二.四 不使用缓存时处理
一.二.四.一 查询 findById 实现
@Overridepublic User findById(int id) {return userMapper.findById(id);}
一.二.四.二 查询测试
@Testpublic void findByIdTest(){User user=userService.findById(40); //id随时更换log.info(user);}
运行测试方法 findByIdTest()
第一次查询
发现查询了数据库
第二次查询
依然走的是数据库查询.
这是以前的常规的写法。
一.二.五 使用SpringCache 缓存时处理
一.二.五.一 查询 findById 实现
@Override// 指定了参数为 id: 变成了: value::id 的key值@Cacheable(value=KEY_PRE,key = "#id")public User findById(int id) {return userMapper.findById(id);}
在方法上 添加了一个注解 @Cacheable ,补充属性信息
value 表示使用的缓存组, key 表示缓存的值。
一.二.五.二 查询测试
@Testpublic void findByIdTest(){User user=userService.findById(40); //id随时更换log.info(user);}
运行测试方法 findByIdTest()
第一次查询
发现查询了数据库
第二次查询
发现,并没有查询数据库,走的是缓存里面的数据。
查看 Redis客户端
发现存储的数据乱码了.
一.二.六 处理存储信息乱码问题
除了 RedisConfig.java 配置之外 ,再添加一个 CacheConfig.java 的配置信息
package top.yueshushu.learn.config;import com.fasterxml.jackson.annotation.JsonAutoDetect;
import com.fasterxml.jackson.annotation.PropertyAccessor;
import com.fasterxml.jackson.databind.ObjectMapper;
import lombok.extern.log4j.Log4j2;
import lombok.extern.slf4j.Slf4j;
import org.springframework.cache.CacheManager;
import org.springframework.cache.annotation.CachingConfigurerSupport;
import org.springframework.cache.interceptor.*;
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 javax.annotation.Resource;
import java.time.Duration;
import java.util.HashMap;
import java.util.Map;/*** @author :zk_yjl* @description:Cache的缓存配置信息,可以解决乱码问题* @date :2021/09/23 17:09*/
@Log4j2
@Configuration
public class CacheConfig extends CachingConfigurerSupport {@Resourceprivate RedisConnectionFactory factory;/*** 自定义生成redis-key** @return*/@Override@Beanpublic KeyGenerator keyGenerator() {return (o, method, objects) -> {StringBuilder sb = new StringBuilder();sb.append(o.getClass().getName()).append(".");sb.append(method.getName()).append(".");for (Object obj : objects) {sb.append(obj.toString());}log.info("keyGenerator=" + sb.toString());return sb.toString();};}@Bean@Overridepublic CacheResolver cacheResolver() {return new SimpleCacheResolver(cacheManager());}@Bean@Overridepublic CacheErrorHandler errorHandler() {// 用于捕获从Cache中进行CRUD时的异常的回调处理器。return new SimpleCacheErrorHandler();}@Bean@Overridepublic CacheManager cacheManager() {return new RedisCacheManager(RedisCacheWriter.nonLockingRedisCacheWriter(factory),this.getRedisCacheConfigurationWithTtl(30*60), // 默认策略,未配置的 key 会使用这个this.getRedisCacheConfigurationMap() // 指定 key 策略);}private Map<String, RedisCacheConfiguration> getRedisCacheConfigurationMap() {Map<String, RedisCacheConfiguration> redisCacheConfigurationMap = new HashMap<>();//DayCache和SecondsCache进行过期时间配置translates缓存丢弃改为了redisredisCacheConfigurationMap.put("translates", this.getRedisCacheConfigurationWithTtl(12*60*60));redisCacheConfigurationMap.put("strategies", this.getRedisCacheConfigurationWithTtl(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 清空后再执行, 运行是成功的,
从数据库里面查询, 将查询结果放置到Redis缓存里面,并且缓存信息正常展示。
二. Spring Cache 的概念
参考文章: https://www.cnblogs.com/morganlin/p/12000223.html (转载很多,不知道原版是哪一个)
Spring Cache 介绍
二.一 几个重要概念&缓存注解
名称 | 解释 |
---|---|
Cache | 缓存接口,定义缓存操作。实现有:RedisCache、EhCacheCache、ConcurrentMapCache等 |
CacheManager | 缓存管理器,管理各种缓存(cache)组件 |
@Cacheable | 主要针对方法配置,能够根据方法的请求参数对其进行缓存.不保证方法被调用 |
@CacheEvict | 清空缓存 常用于删除 |
@CachePut | 保证方法被调用,又希望结果被缓存。 与@Cacheable区别在于是否每次都会调用方法,常用于更新 |
@EnableCaching | 开启基于注解的缓存 在启动类上进行配置 |
keyGenerator | 缓存数据时key生成策略 |
serialize | 缓存数据时value序列化策略 |
@CacheConfig | 统一配置本类的缓存注解的属性 |
@Caching | 同一个方法,操作多个缓存时使用 |
二.二 @Cacheable/@CachePut/@CacheEvict 主要的参数
名称 | 解释 |
---|---|
value | 缓存的名称,在 spring 配置文件中定义,必须指定至少一个 例如: @Cacheable(value=”mycache”) 或者 @Cacheable(value={”cache1”,”cache2”} |
key | 缓存的 key,可以为空,如果指定要按照 SpEL 表达式编写, 如果不指定,则缺省按照方法的所有参数进行组合 例如: @Cacheable(value=”testcache”,key=”#id”) |
condition | 缓存的条件,可以为空,使用 SpEL 编写,返回 true 或者 false, 只有为 true 才进行缓存/清除缓存 例如:@Cacheable(value=”testcache”,condition=”#userName.length()>2”) |
unless | 否定缓存。当条件结果为TRUE时,就不会缓存。 @Cacheable(value=”testcache”,unless=”#userName.length()>2”) |
allEntries (@CacheEvict ) | 是否清空所有缓存内容,缺省为 false,如果指定为 true, 则方法调用后将立即清空所有缓存 例如: @CachEvict(value=”testcache”,allEntries=true) |
beforeInvocation (@CacheEvict) | 是否在方法执行前就清空,缺省为 false,如果指定为 true, 则在方法还没有执行的时候就清空缓存,缺省情况下,如果方法 执行抛出异常,则不会清空缓存 例如: @CachEvict(value=”testcache”,beforeInvocation=true) |
二.三 SpEL上下文数据
Spring Cache提供了一些供我们使用的SpEL上下文数据,下表直接摘自Spring官方文档:
名称 | 位置 | 描述 | 示例 |
---|---|---|---|
methodName | root对象 | 当前被调用的方法名 |
#root.methodname
|
method | root对象 | 当前被调用的方法 |
#root.method.name
|
target | root对象 | 当前被调用的目标对象实例 |
#root.target
|
targetClass | root对象 | 当前被调用的目标对象的类 |
#root.targetClass
|
args | root对象 | 当前被调用的方法的参数列表 |
#root.args[0]
|
caches | root对象 | 当前方法调用使用的缓存列表 |
#root.caches[0].name
|
Argument Name | 执行上下文 | 当前被调用的方法的参数,如findArtisan(Artisan artisan),可以通过#artsian.id获得参数 |
#artsian.id
|
result | 执行上下文 | 方法执行后的返回值(仅当方法执行后的判断有效,如 unless cacheEvict的beforeInvocation=false) |
#result
|
注意:
1.当我们要使用root对象的属性作为key时我们也可以将“#root”省略,因为Spring默认使用的就是root对象的属性。 如
@Cacheable(key = "targetClass + methodName +#p0")
2.使用方法参数时我们可以直接使用“#参数名”或者“#p参数index”。 如:
@Cacheable(value="users", key="#id")
@Cacheable(value="users", key="#p0")
SpEL提供了多种运算符
类型 | 运算符 |
---|---|
关系 | <,>,<=,>=,==,!=,lt,gt,le,ge,eq,ne |
算术 | +,- ,* ,/,%,^ |
逻辑 | &&,||,!,and,or,not,between,instanceof |
条件 | ?: (ternary),?: (elvis) |
正则表达式 | matches |
其他类型 | ?.,?[…],![…],$[…] |
三. SpringCache 的注解用法
通常情况下, 传入的参数为 key, 返回的结果为 value
三.一 @Cacheable
@Override// 指定了参数为 id: 变成了: value::id 的key值@Cacheable(value=KEY_PRE,key = "#id")public User findById(int id) {return userMapper.findById(id);}
进行测试 传入的参数 是 40
当参数有多个时
@Cacheable(value = KEY_PRE)@Overridepublic List<User> findByNameAndSex(String name, String sex) {return userMapper.findByNameAndSex(name,sex);}
不指定 key 时 (传入参数 name为 周小欢 sex为 女 时), 默认的生成的缓存 key为:
user_::top.yueshushu.learn.service.UserServiceImpl.findByNameAndSex.周小欢女
可以 通过 key 进行指定
@Cacheable(value = KEY_PRE,key = "#name")@Overridepublic List<User> findByNameAndSex(String name, String sex) {return userMapper.findByNameAndSex(name,sex);}
生成 后的 key 为: user_::周小欢
key值 可以进行拼接
@Cacheable(value = KEY_PRE,key = "#name+#sex")
生成后的key 为: user_::周小欢女
也可以使用 #p+参数序号 来指定
@Cacheable(value = KEY_PRE,key = "#p0+#p1")
生成的key 是: user_::周小欢女
也可以使用 SpEL 上下文进行处理
@Override@Cacheable(value = KEY_PRE,key="#root.targetClass+#root.methodName+#id")public User findById(int id) {return userMapper.findById(id);}
传入参数是 40 的话,
生成的 key 是: user_::class top.yueshushu.learn.service.UserServiceImplfindById40
也可以指定 条件 condition 当条件满足时,才使用缓存。
传入 id 为 40, <30为false, 即条件为 false
@Cacheable(value = KEY_PRE,key="#root.targetClass+#root.methodName+#id",
condition ="#id<30" )
重新运行
发生会查询数据库,不走缓存。 即使Redis里面有这个 key
unless 表示条件不满足时,使用缓存
@Override@Cacheable(value = KEY_PRE,key="#root.targetClass+#root.methodName+#id",unless ="#id<30" )public User findById(int id) {return userMapper.findById(id);}
三.二 @CachePut 缓存更新
常用于修改缓存里面的内容。
设置 id=40 的用户的缓存, key为: user_::40
@Override@Cacheable(value = KEY_PRE,key="#id" )public User findById(int id) {return userMapper.findById(id);}
修改用户的信息, 注意, 这个修改方法有返回值 User, 并不是以前的 void
@Override@CachePut(value = KEY_PRE,key = "#user.id")public User updateUser(User user) {userMapper.updateUser(user);//更新全部的缓存信息return user;}
将返回值 放置到缓存里面。
@Testpublic void updateTest(){User user=userService.findById(40); //id随时更换user.setName("我换新的名字了");userService.updateUser(user);log.info("修改成功{}",user);findByIdTest();;}
运行处理
发现缓存里面的内容 也同步进行更新了.
添加方法时
注意, 方法有返回值, 为 User userMapper.addUser() 方法,会自动回显 id.
所以 key值用的是 #result 结果里面的id
@CachePut(value=KEY_PRE,key = "#result.id")@Overridepublic User addUser(User user) {userMapper.addUser(user);return user;}
测试方法
@Testpublic void insertTest(){//1. 构建对象User user=new User();user.setName("岳泽霖");user.setAge(26);user.setSex("男");user.setDescription("一个快乐的程序员");//2. 添加方法userService.addUser(user);log.info("添加成功,{}",user);}
查看控制台输出
新添加的 用户 id 为56
根据 id=56 查询的话, 也是直接从缓存里面获取数据.
三.三 @CacheEvict 清空缓存
根据id 清空缓存 返回值可以是 void
@Override@CacheEvict(value = KEY_PRE,key = "#id")public void deleteUser(int id) {userMapper.deleteById(id);}
清空缓存
@Testpublic void deleteTest(){userService.deleteUser(56); //id随时更换}
发现数据库里面没有 id=56 的记录了, redis缓存里面也没有 key为 user_:: 56 的记录了。
findById 中 id=40 和 findAll() 生成两个缓存
删除时, 使用 allEntries=true 属性
@Override@CacheEvict(value = KEY_PRE,key = "#id",allEntries = true)public void deleteUser(int id) {userMapper.deleteById(id);}
运行删除 id=56(已经不存在这条记录信息了)
发现, 会清空当前数据库下所有的缓存信息。 所以,这个属性 allEntries 不要乱用。
三.四 @CacheConfig 在类上统一设置
我们发现, 我们设置缓存时, 每一个方法,无论是 findById , 还是 deleteUser, updateUser , 都使用了一个前缀 value=KEY_PRE, 这个值是 user_ 可不可以将这个前缀统一设置呢?
可以使用 @CacheConfig 注解在类上来简化缓存的开发.
@Service
@Log4j2
@CacheConfig(cacheNames ={"user_"})
public class UserServiceImpl implements UserService {...
}
这样在 方法上,就可以省略掉 以前的 value 属性。
@Override@Cacheable(key="#id" )public User findById(int id) {return userMapper.findById(id);}@Override@Cacheable(key = "#root.targetClass+#root.methodName")public List<User> findAll() {return userMapper.findAll();}@Cacheable(key = "#root.targetClass+#root.methodName")@Overridepublic List<User> findByNameAndSex(String name, String sex) {return userMapper.findByNameAndSex(name,sex);}
与以前是相同的效果。
可以一个实体类,设置一个相应的前缀信息。
三.五 @Caching 多注解组合
@Target({ElementType.TYPE, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
@Inherited
@Documented
public @interface Caching {Cacheable[] cacheable() default {};CachePut[] put() default {};CacheEvict[] evict() default {};}
里面包含了三个常用的注解
@Caching(cacheable = {@Cacheable(key = "#name"), // 放置缓存到 name@Cacheable(key="#sex") // 放置缓存到 sex , id的缓存用 put更新},put = {@CachePut(key="#id") //同时更新 id缓存})@Overridepublic List<User> findByNameAndSexAndId(String name, String sex, Integer id) {return userMapper.findByNameAndSexAndId(name,sex,id);}
会同时将查询的信息 放置到 name 属性的缓存, sex属性的缓存里面, 同时更新 id属性的缓存信息。
测试类
@Testpublic void findNameAndSexAndIdTest(){log.info(">>>>>>>>目前数据库中存在的用户信息:");List<User> userList=userService.findByNameAndSexAndId("欢欢","女",40);userList.forEach(n->log.info(n));}
查看缓存的信息
这些就是 Spring Cache 的基本用法.
本章节的代码放置在 github 上:
https://github.com/yuejianli/springboot/tree/develop/SpringBoot_Cache
谢谢您的观看,如果喜欢,请关注我,再次感谢 !!!
SpringBoot整合Cache缓存技术(二十一)相关推荐
- SpringBoot集成Cache缓存(Ehcache缓存框架,注解方式)
1.说明 Spring定义了CacheManager和Cache接口, 用来统一不同的缓存技术, 例如JCache,EhCache,Hazelcast,Guava,Redis等. 本文通过Spring ...
- SpringBoot整合Redis缓存
SpringBoot整合Redis缓存 一.缓存概念知识 1.是什么缓存 2.缓存的优缺点 3.为什么使用缓存 二.Redis概念知识 1.Redis简介 2.为什么用Redis作为缓存 3.Redi ...
- springboot整合持久层技术
springboot整合持久层技术 整合jdbcTemplate 1.依赖导入 <?xml version="1.0" encoding="UTF-8"? ...
- Spring学习笔记(三十二)——SpringBoot中cache缓存的介绍和使用
目录 Spring Boot与缓存 什么是cache java cache:JSR107 Spring缓存抽象 redis和cache的使用场景和区别 SpringBoot缓存的使用 0. 开启缓存的 ...
- .Net MVC Cache 缓存技术总结
一.细说 ASP.NET Cache 及其高级用法 二..Net环境下的缓存技术介绍 (转) 三.asp.net中缓存的使用介绍一 四.HttpContext.Current.Cache 过期时间
- SpringBoot集成Cache缓存(Redis缓存,RedisTemplate方式)
1.说明 SpringBoot集成Redis缓存, 首先创建一个Spring Boot工程, 使用Maven向导方式创建:SpringBoot集成Maven工程 然后引入redis的spring bo ...
- Spring Cache缓存技术,Cacheable、CachePut、CacheEvict、Caching、CacheConfig注解的使用
前置知识: 在Spring Cache缓存中有两大组件CacheManager和Cache.在整个缓存中可以有多个CacheManager,他们负责管理他们里边的Cache.一个CacheManage ...
- 【SpringBoot】SpringBoot——整合持久层技术
文章目录 5. 整合持久层技术 5.1 整合JdbcTemplate 5.2 整合MyBatis 5.3 Spring Data JPA 5.3.1 JPA.Spring Data.Spring Da ...
- 深度理解springboot集成cache缓存之源码解析
一.案例准备 1.创建数据表(employee表) 2.创建Employee实体类封装数据库中的数据 @AllArgsConstructor @NoArgsConstructor @Data @ToS ...
- SpringBoot整合redis缓存(一)
一. 准备工作 1.Linux系统 2.安装redis(也可以安装docker,然后再docker中装redis,本文章就直接用Linux安装redis做演示)redis下载地址:http://dow ...
最新文章
- 媒体行业注册什么企业邮箱比较好?
- CentOS安装MongoDB
- 《Oracle数据库管理与维护实战》——2.11 Oracle数据字典
- 奇偶数判断(信息学奥赛一本通-T1041)
- Autolayout屏幕适配——代码实现(苹果公司 / VFL语言 / 第三方框架Masonry)
- python函数式编程:apply, map, lambda和偏函数
- 4200有linux版本么,如何检查Linux版本
- SGuard64.exe(SGuardwnd) ACE-Guard Client EXE:造成磁盘经常读写,游戏卡顿,及解决方案
- 1.2 说说大学这滩泥淖——《逆袭大学》连载
- 华为盒子联网后显示无法连接服务器,【当贝市场】华为盒子连上无线后不能上网怎么办?...
- Selenium调用使用360浏览器,QQ浏览器,遨游浏览器,猎豹浏览器,Chromium
- matlab二维正弦曲线
- linux物联网项目,6个开源项目提升物联网开发效率
- Android 手机灭屏流程分析详解
- html格式显示图标异常,HTM或HTML图标变成无法显示和识别的解决方法大全
- 大数据24小时:地质局发布地质大数据共享平台,科大讯飞将语音识别植入腾讯小Q机器人
- 延时加载技术-----仿照手机淘宝网站图片延时加载
- JavaScript中的数组方法和循环
- 阻塞状态和等待状态的区别
- Rock5 KubeSphere常规部署
热门文章
- Linux下wm8978调试指南
- java 对错代厔_将汉语转换成拼音,实现拼音和中文双重登录
- 传奇世界修改服务器时间,《传奇世界手游时长版》测试结束公告
- 美团java笔试题_美团笔试题目(Java后端5题2小时)
- 串口(串行接口)相关概念
- 四象限法推导lm曲线_数据分析四象限法详解
- win10系统pyCharm安装及最新2018激活码
- vs2015中工具箱不显示DevExpress控件的解决办法
- Finalize()、Dispose(bool disposing)和Dispose()的使用场景与对比
- GPIO推挽输出和开漏输出模式区别详解