缓存是分布式系统中的重要组件,主要解决数据库数据的高并发访问。在实际开发中,尤其是用户访问量较大的网站,用户对高频热点数据的访问非常频繁,为了提高服务器访问性能、减少数据库的压力、提高用户体验,使用缓存显得尤为重要。

一、基础环境搭建

1.准备数据,创建项目

这里使用之前创建的springbootdata的数据库,该数据库有两个表t_article和t_comment,这两个表预先插入几条测试数据。

使用Spring starter project方式创建一个Spring Boot项目,选择JPA依赖、MySQL依赖和Web依赖,如下图

2.编写数据库表对应的实体类

@Entity(name = "t_comment")
public class Comment {@Id@GeneratedValue(strategy = GenerationType.IDENTITY)private Integer id;private String content;private String author;@Column(name = "a_id")private Integer aId;//补充get set toString}

3.编写数据库操作的Repository接口文件CommentRepository

public interface CommentRepository extends JpaRepository<Comment,Integer>{@Transactional@Modifying@Query("UPDATE t_comment c SET c.author= ?1 WHERE  c.id = ?2")public int updateComment(String author,Integer id);}

4.编写业务操作类Service文件CommentService

在该类中编写数据的查询、修改和删除操作

@Service
public class CommentService {@Autowiredprivate CommentRepository commentRepository;public Comment findById(int comment_id){Optional<Comment> optional = commentRepository.findById(comment_id);if(optional.isPresent()){return optional.get();}return null;}public Comment updateComment(Comment comment){commentRepository.updateComment(comment.getAuthor(), comment.getId());return comment;}public void deleteComment(int comment_id){commentRepository.deleteById(comment_id);}
}

5.编写表示的控制器CommentController

使用注入的CommentService实例对象编写对Comment评论数据的查询、修改和删除方法。

@RestController
public class CommentController {@Autowiredprivate CommentService commentService;@GetMapping("/get/{id}")public Comment findById(@PathVariable("id") int comment_id) {Comment comment = commentService.findById(comment_id);return comment;}@GetMapping("/update/{id}/{author}")public Comment updateComment(@PathVariable("id") int comment_id, @PathVariable("author") String author) {Comment comment = commentService.findById(comment_id);comment.setAuthor(author);Comment updateComment = commentService.updateComment(comment);return updateComment;}@GetMapping("/delete/{id}")public void deleteComment(@PathVariable("id") int comment_id) {commentService.deleteComment(comment_id);}
}

6.编写配置文件

spring.datasource.url=jdbc:mysql://localhost:3306/springbootdata?serverTimezone=UTC
spring.datasource.username=root
spring.datasource.password=root
spring.jpa.show-sql=true

7.项目测试

项目启动成功后,在浏览器上访问http://localhost:8080/get/1,浏览器每刷新一次,控制台会新输出一条SQL语句

每执行一次查询操作,都会访问一次数据库并执行一次SQL语句。

二、Spring Boot 默认缓存体验

1、启动类上添加@EnableCaching注解

开启基于注解的缓存支持

//启动类
@EnableCaching
@SpringBootApplication
public class Chapter06Application {public static void main(String[] args) {SpringApplication.run(Chapter06Application.class, args);}
}

2.使用@Cacheable注解对数据操作方法进行缓存管理

//业务层
@Cacheable(cacheNames = "comment")
public Comment findById(int comment_id){Optional<Comment> optional = commentRepository.findById(comment_id);if(optional.isPresent()){return optional.get();}return null;
}

3.Spring Boot默认缓存测试

项目启动成功后,在浏览器上访问http://localhost:8080/get/1,不论浏览器刷新多少次,页面的查询结果都会显示同一条数据

重复进行同样的查询操作,数据库只执行了一次SQL查询语句,说明项目开启的默认缓存支持已经生效。

三、Spring Boot 缓存注解介绍

1.@EnableCaching注解

@EnableCaching是由Spring框架提供的,Spring Boot框架对该注解进行了继承,该注解需要配置在类上(在Spring Boot中,通常配置在项目启动类上),用于开启基于注解的缓存支持。

2.@Cacheable注解

@Cacheable注解也是由Spring框架提供的,可以作用于类或方法(通常用在数据查询方法上),用于对方法结果进行缓存存储。

@Cacheable注解的执行顺序是,先进行缓存查询,如果为空则进行方法查询,并将结果进行缓存;如果缓存中有数据,不进行方法查询,而是直接使用缓存数据。

属性名

说明

value/cacheNames

指定缓存空间的名称,必配属性。这两个属性二选一使用

key

指定缓存数据的key,默认使用方法参数值,可以使用SpEL表达式

keyGenerator

指定缓存数据的key的生成器,与key属性二选一使用

cacheManager

指定缓存管理器

cacheResolver

指定缓存解析器,与cacheManager属性二选一使用

condition

指定在符合某条件下,进行数据缓存

unless

指定在符合某条件下,不进行数据缓存

sync

指定是否使用异步缓存。默认false

3.@CachePut注解

@CachePut注解是由Spring框架提供的,可以作用于类或方法(通常用在数据更新方法上),该注解的作用是更新缓存数据。@CachePut注解的执行顺序是,先进行方法调用,然后将方法结果更新到缓存中。

@CachePut注解也提供了多个属性,这些属性与@Cacheable注解的属性完全相同。

4.@CacheEvict注解

@CacheEvict注解是由Spring框架提供的,可以作用于类或方法(通常用在数据删除方法上),该注解的作用是删除缓存数据。@CacheEvict注解的默认执行顺序是,先进行方法调用,然后将缓存进行清除。

@CacheEvict注解也提供了多个属性,这些属性与@Cacheable注解的属性基本相同,除此之外,还额外提供了两个特殊属性allEntries和beforeInvocation。

@CacheEvict注解的特殊属性

(1)allEntries属性

allEntries属性表示是否清除指定缓存空间中的所有缓存数据,默认值为false(即默认只删除指定key对应的缓存数据)。

(2)beforeInvocation属性

beforeInvocation属性表示是否在方法执行之前进行缓存清除,默认值为false(即默认在执行方法后再进行缓存清除)。

5.@Caching注解

@Caching注解用于针对复杂规则的数据缓存管理,可以作用于类或方法,在@Caching注解内部包含有Cacheable、put和evict三个属性,分别对应于@Cacheable、@CachePut和@CacheEvict三个注解。

@Caching(cacheable={@Cacheable(cacheNames ="comment",key = "#id")},

put = {@CachePut(cacheNames = "comment",key = "#result.author")})

public Comment getComment(int comment_id){

return commentRepository.findById(comment_id).get();

}

6.@CacheConfig注解

@CacheConfig注解使用在类上,主要用于统筹管理类中所有使用@Cacheable、@CachePut和@CacheEvict注解标注方法中的公共属性,这些公共属性包括有cacheNames、keyGenerator、cacheManager和cacheResolver。

@CacheConfig(cacheNames = "comment")
@Service
public class CommentService {@Autowiredprivate CommentRepository commentRepository;@Cacheablepublic Comment findById(int comment_id){Comment comment = commentRepository.findById(comment_id).get();return comment; }...}

四、Spring Boot支持的缓存组件

(1)Generic

(2)JCache (JSR-107) (EhCache 3、Hazelcast、Infinispan等)

(3)EhCache 2.x

(4)Hazelcast

(5)Infinispan

(6)Couchbase

(7)Redis

(8)Caffeine

(9)Simple(默认)

开启缓存管理后,springboot会按照上述列表顺序查找有效的缓存组件进行缓存管理。

五、基于注解的Redis缓存实现

1.添加Spring Data Redis 依赖启动器

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

如果查不到缓存,重启项目或updateproject下,有可能没有下载这个依赖

2.Redis服务连接配置

服务器顺便开启下,省得待会忘了

spring.redis.host=127.0.0.1
spring.redis.port=6379
spring.redis.password=

3.使用@Cacheable、@CachePut、@CacheEvict注解定制缓存管理

对CommentService类中的方法进行修改使用@Cacheable、@CachePut、@CacheEvict三个注解定制缓存管理,修改后的方法如下

@Cacheable(cacheNames = "comment",unless = "#result==null")
public Comment findById(int comment_id){Optional<Comment> optional = commentRepository.findById(comment_id);if(optional.isPresent()){return optional.get();}
return null;
}@CachePut(cacheNames = "comment",key = "#result.id")
public Comment updateComment(Comment comment){commentRepository.updateComment(comment.getAuthor(), comment.getaId());return comment;}@CacheEvict(cacheNames = "comment")
public void deleteComment(int comment_id){commentRepository.deleteById(comment_id);}

4.基于注解的Redis查询缓存测试

项目启动成功后,通过浏览器访问http://localhost:8080/get/1,如果是热部署,可能不成功,需要关闭项目再重启

控制台显示如下:

。。。。

Hibernate: select comment0_.id as id1_0_0_, comment0_.a_id as a_id2_0_0_, comment0_.author as author3_0_0_, commen。。。。

。。。。

java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [com.itheima.my06.entity.Comment]

。。。。

出现非法参数异常,需要对实体类进行序列化

public class Comment implements Serializable {

再次做基于注解的Redis缓存查询测试

项目启动成功后,通过浏览器访问http://localhost:8080/get/1,并重复刷新浏览器,只出现一条sql语句

打开Redis客户端可视化管理工具Redis Desktop Manager,连接本地启用的Redis服务

可以看出,评论存储到了redis缓存库中comment名称空间下,key=comment::1,value=经过JDK默认序列化格式后的HEX格式值,不方便查看,需要自定义数据的序列化格式。

5.基于注解的Redis缓存更新测试

项目启动成功后,通过浏览器访问http://localhost:8080/update/1/shitou,接着,继续访问http://localhost:8080/get/1

执行updateComment()方法更新id为1的数据时执行了一条更新SQL语句,后续调用findById()方法查询id为1的用户评论信息时没有执行查询SQL语句。

6.基于注解的Redis缓存删除测试

项目启动成功后,通过Redis客户端可视化管理工具Redis Desktop Manager查看缓存信息。

如果不存在,通过浏览器访问http://localhost:8080/get/1,缓存数据,

再次通过Redis Desktop Manager查看,reload下

接着访问http://localhost:8080/delete/1,

reload,查看缓存信息,该条缓存被删除

六、基于API的Redis缓存实现

1.使用Redis API 进行业务数据缓存管理

编写一个进行业务处理的类ApiCommentService,使用@Autowired注解注入Redis API中常用的RedisTemplate(类似于Java基础API中的JdbcTemplate);

然后在数据查询、修改和删除三个方法中,根据业务需求分别进行数据缓存查询、缓存存储、缓存更新和缓存删除。

同时,Comment数据对应缓存管理的key值都手动设置了一个前缀“comment_”,这是针对不同业务数据进行缓存管理设置的唯一key,避免与其他业务缓存数据的key重复。

@Service
public class ApiCommentService {@Autowiredprivate CommentRepository commentRepository;@Autowiredprivate RedisTemplate redisTemplate;public Comment findById(int comment_id){// 先从Redis缓存中查询数据Object object =  redisTemplate.opsForValue().get("comment_"+comment_id);if (object!=null){return (Comment)object;}else {// 缓存中没有,就进入数据库查询Optional<Comment> optional = commentRepository.findById(comment_id);if(optional.isPresent()){Comment comment= optional.get();// 将查询结果进行缓存,并设置有效期为1天redisTemplate.opsForValue().set("comment_"+comment_id, comment,1, TimeUnit.DAYS);return comment;}else {return null;}}}public Comment updateComment(Comment comment){commentRepository.updateComment(comment.getAuthor(), comment.getId());// 更新数据后进行缓存更新redisTemplate.opsForValue().set("comment_"+comment.getId(),comment);return comment;}public void deleteComment(int comment_id){commentRepository.deleteById(comment_id);// 删除数据后进行缓存删除redisTemplate.delete("comment_"+comment_id);}
}

2.创建Web访问层类ApiCommentController

在类上加入了@RequestMapping(“/api”)注解用于窄化请求,

并通过@Autowired注解注入了新编写的ApiCommentService实例对象,

然后调用ApiCommentService中的相关方法进行数据查询、修改和删除。

@RestController
@RequestMapping("/api")  // 窄化请求路径
public class ApiCommentController {@Autowiredprivate ApiCommentService apiCommentService;@GetMapping("/get/{id}")public Comment findById(@PathVariable("id") int comment_id){Comment comment = apiCommentService.findById(comment_id);return comment;}@GetMapping("/update/{id}/{author}")public Comment updateComment(@PathVariable("id") int comment_id,@PathVariable("author") String author){Comment comment = apiCommentService.findById(comment_id);comment.setAuthor(author);Comment updateComment = apiCommentService.updateComment(comment);return updateComment;}@GetMapping("/delete/{id}")public void deleteComment(@PathVariable("id") int comment_id){apiCommentService.deleteComment(comment_id);}
}

3.基于API的Redis缓存实现的相关配置

  1. 基于API的Redis缓存实现不需要@EnableCaching注解开启基于注解的缓存支持。
  2. 基于API的Redis缓存实现需要在Spring Boot项目的pom.xml文件中引入Redis依赖启动器,并在配置文件中进行Redis服务连接配置,同时将进行数据存储的Comment实体类实现序列化接口。
  3. 缓存测试与基于注解的Redis缓存实现的测试基本一样,但访问路径加“/api”,如http://localhost:8080/api/get/2

4.做测试

http://localhost:8080/api/get/1

jdk序列化导致出现上图红色框的内容,无意义但会占据内存,所以要更改序列化方式

七、通过RedisTemplate自定义缓存序列化(通过api缓存数据)

1.Redis API 默认序列化机制

基于Redis API的Redis缓存实现是使用RedisTemplate模板进行数据缓存操作的,这里打开RedisTemplate类,查看源码可知:

@Override
public void afterPropertiesSet() {super.afterPropertiesSet();boolean defaultUsed = false;if (defaultSerializer == null) {defaultSerializer = new JdkSerializationRedisSerializer(classLoader != null ? classLoader : this.getClass().getClassLoader());
}

1.使用RedisTemplate进行Redis数据缓存操作时,内部默认使用的是JdkSerializationRedisSerializer序列化方式,所以进行数据缓存的实体类必须实现JDK自带的序列化接口(例如Serializable);

2.使用RedisTemplate进行Redis数据缓存操作时,如果自定义了缓存序列化方式defaultSerializer,那么将使用自定义的序列化方式。

2.自定义RedisTemplate序列化机制

分析:

在项目中引入Redis依赖后,Spring Boot提供的RedisAutoConfiguration自动配置会生效。

打开RedisAutoConfiguration类,查看内部源码中关于RedisTemplate的定义方式可知:

public class RedisAutoConfiguration {@Bean@ConditionalOnMissingBean(name = "redisTemplate")public RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {RedisTemplate<Object, Object> template = new RedisTemplate<>();template.setConnectionFactory(redisConnectionFactory);return template;}
  1. 在Redis自动配置类中,通过Redis连接工厂RedisConnectionFactory初始化了一个RedisTemplate;该类上方添加了@ConditionalOnMissingBean注解(顾名思义,当某个Bean不存在时生效),用来表明如果开发者自定义了一个名为redisTemplate的Bean,则该默认初始化的RedisTemplate会被覆盖。
  2. 如果想要使用自定义序列化方式的RedisTemplate进行数据缓存操作,可以参考上述核心代码创建一个名为redisTemplate的Bean组件,并在该组件中设置对应的序列化方式即可。

解决:

在项目中创建一个Redis自定义配置类RedisConfig,通过@Configuration注解定义了一个RedisConfig配置类,并使用@Bean注解注入了一个默认名称为方法名的redisTemplate组件(注意,该Bean组件名称必须是redisTemplate)。在定义的Bean组件中,自定义了一个RedisTemplate,使用自定义的Jackson2JsonRedisSerializer数据序列化方式;在定制序列化方式中,定义了一个ObjectMapper用于进行数据转换设置。

@Configuration   // 定义一个配置类
public class RedisConfig {@Beanpublic RedisTemplate<Object, Object> redisTemplate(RedisConnectionFactory redisConnectionFactory) {RedisTemplate<Object, Object> template = new RedisTemplate();template.setConnectionFactory(redisConnectionFactory);// 使用JSON格式序列化对象,对缓存数据key和value进行转换Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);// 解决查询缓存转换异常的问题ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);//过时om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);
//用下面的方法代替上面的方法
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL);jacksonSeial.setObjectMapper(om);// 设置RedisTemplate模板API的序列化方式为JSONtemplate.setDefaultSerializer(jacksonSeial);return template;}
}

3.效果测试

项目启动成功后,通过浏览器访问http://localhost:8080/api/get/3,并重复刷新浏览器查看同一条数据信息 ,数据库只执行了一次SQL语句

八、通过RedisCacheManager自定义缓存序列化(适用通过注解缓存数据)

1.Redis 注解默认序列化机制

1.Spring Boot整合Redis组件提供的缓存自动配置类RedisCacheConfiguration(org.springframework.boot.autoconfigure.cache),

其内部是通过Redis连接工厂RedisConnectionFactory定义了一个缓存管理器RedisCacheManager;

同时定义RedisCacheManager时,也默认使用了JdkSerializationRedisSerializer序列化方式。

@Configuration(proxyBeanMethods = false)

@ConditionalOnClass(RedisConnectionFactory.class)

@AutoConfigureAfter(RedisAutoConfiguration.class)

@ConditionalOnBean(RedisConnectionFactory.class)

@ConditionalOnMissingBean(CacheManager.class)

@Conditional(CacheCondition.class)

class RedisCacheConfiguration {

@Bean

RedisCacheManager cacheManager(CacheProperties cacheProperties, CacheManagerCustomizers cacheManagerCustomizers,

ObjectProvider<org.springframework.data.redis.cache.RedisCacheConfiguration> redisCacheConfiguration,

ObjectProvider<RedisCacheManagerBuilderCustomizer> redisCacheManagerBuilderCustomizers,

RedisConnectionFactory redisConnectionFactory, ResourceLoader resourceLoader) {

RedisCacheManagerBuilder builder = RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(

determineConfiguration(cacheProperties, redisCacheConfiguration, resourceLoader.getClassLoader()));

List<String> cacheNames = cacheProperties.getCacheNames();

if (!cacheNames.isEmpty()) {

builder.initialCacheNames(new LinkedHashSet<>(cacheNames));

}

redisCacheManagerBuilderCustomizers.orderedStream().forEach((customizer) -> customizer.customize(builder));

return cacheManagerCustomizers.customize(builder.build());

}

private org.springframework.data.redis.cache.RedisCacheConfiguration determineConfiguration(

CacheProperties cacheProperties,

ObjectProvider<org.springframework.data.redis.cache.RedisCacheConfiguration> redisCacheConfiguration,

ClassLoader classLoader) {

return redisCacheConfiguration.getIfAvailable(() -> createConfiguration(cacheProperties, classLoader));

}

private org.springframework.data.redis.cache.RedisCacheConfiguration createConfiguration(

CacheProperties cacheProperties, ClassLoader classLoader) {

Redis redisProperties = cacheProperties.getRedis();

org.springframework.data.redis.cache.RedisCacheConfiguration config = org.springframework.data.redis.cache.RedisCacheConfiguration

.defaultCacheConfig();

config = config.serializeValuesWith(

SerializationPair.fromSerializer(new JdkSerializationRedisSerializer(classLoader)));

if (redisProperties.getTimeToLive() != null) {

config = config.entryTtl(redisProperties.getTimeToLive());

}

if (redisProperties.getKeyPrefix() != null) {

config = config.prefixKeysWith(redisProperties.getKeyPrefix());

}

if (!redisProperties.isCacheNullValues()) {

config = config.disableCachingNullValues();

}

if (!redisProperties.isUseKeyPrefix()) {

config = config.disableKeyPrefix();

}

return config;

}

}

2.如果想要使用自定义序列化方式的RedisCacheManager进行数据缓存操作,可以创建一个名为cacheManager的Bean组件,并在该组件中设置对应的序列化方式即可

2.自定义RedisCacheManager

@Bean
public RedisCacheManager cacheManager(RedisConnectionFactory redisConnectionFactory) {// 分别创建String和JSON格式序列化对象,对缓存数据key和value进行转换RedisSerializer<String>strSerializer = new StringRedisSerializer();Jackson2JsonRedisSerializer jacksonSeial = new Jackson2JsonRedisSerializer(Object.class);// 解决查询缓存转换异常的问题ObjectMapper om = new ObjectMapper();om.setVisibility(PropertyAccessor.ALL, JsonAutoDetect.Visibility.ANY);//过时om.enableDefaultTyping(ObjectMapper.DefaultTyping.NON_FINAL);//用下面的方法代替上面的方法
om.activateDefaultTyping(LaissezFaireSubTypeValidator.instance,ObjectMapper.DefaultTyping.NON_FINAL);jacksonSeial.setObjectMapper(om);// 定制缓存数据序列化方式及时效RedisCacheConfiguration config = RedisCacheConfiguration.defaultCacheConfig().entryTtl(Duration.ofDays(1)).serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(strSerializer)).serializeValuesWith(RedisSerializationContext.SerializationPair.fromSerializer(jacksonSeial)).disableCachingNullValues();RedisCacheManager cacheManager = RedisCacheManager.builder(redisConnectionFactory).cacheDefaults(config).build();return cacheManager;
}

3.效果测试

项目启动成功后,通过浏览器访问http://localhost:8080/get/3,并重复刷新浏览器查看同一条数据信息 ,数据库只执行了一次SQL语句

缓存结果如下:

6-Spring Boot缓存管理相关推荐

  1. Spring Boot缓存管理

    Spring Boot缓存管理 Spring Boot默认缓存管理 基础环境搭建 Spring Boot默认缓存体验 Spring Boot缓存注解介绍 @EnableCaching注解 @Cache ...

  2. Spring Boot 缓存教程示例

    在本Spring Boot 教程中,学习如何从 Spring 框架缓存支持中轻松管理应用程序缓存.Spring 在缓存方面有一些很好的特性,Spring 缓存 API 的抽象使用起来非常简单. 1.缓 ...

  3. spring boot缓存_Spring Boot和缓存抽象

    spring boot缓存 缓存是大多数应用程序的主要组成部分,只要我们设法避免磁盘访问,缓存就会保持强劲. Spring对各种配置的缓存提供了强大的支持 . 您可以根据需要简单地开始,然后进行更多可 ...

  4. spring默认缓存管理器_使用Spring的缓存管理器缓存Web内容

    spring默认缓存管理器 在这篇文章中,我想向大家展示如何使用Spring的CacheManager,@ Cacheable和JMX批注来缓存和管理Web内容的缓存的基础知识. 想象一下一个网上商店 ...

  5. Spring boot 缓存学习笔记

    Spring boot 缓存 1. spring cache spring cache 是spring 3.1 引入的新技术, 核心思想:调用一个缓存方法时会把该方法参数和返回结果,作为一个键值存入缓 ...

  6. Spring Boot 线上项目管理

    Spring Boot 线上项目管理 在linux 中管理SpringBoot 项目如果是打包为Jar那么我们一般是使用shell 命令来管理, 如果有多个项目管理起来显得比较麻烦. 如果新增.更新. ...

  7. Spring 的缓存管理器 CacheManager

    Spring 的缓存管理器 在 spring 项目中提供了CacheManager接口来定义缓存管理器,这样各个不同的缓存就可以实现它来提供管理器的功能了,在spring-boot-starter-d ...

  8. java spring boot缓存_Springboot对缓存的支持

    该楼层疑似违规已被系统折叠 隐藏此楼查看此楼 缓存是依赖于org.springframework.cache.Cache和 org.springframework.cache.CacheManager ...

  9. Spring Boot 缓存应用实践

    点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 来源:cnblogs.com/jeffwongishan ...

最新文章

  1. python怎么知道用哪个库使用-dir、help 查看python 库 对应的方法 和使用
  2. P9 线性系统状态空间分析-《Matlab/Simulink与控制系统仿真》程序指令总结
  3. CS190.1x-ML_lab1_review_student
  4. 中国海上风力发电行业战略调研与投资风险分析报告2022-2028年
  5. Java中Socket通信-服务端和客户端双向传输字符串实现
  6. HTML 5入门学习,源码中全部使用HTML 5标签
  7. c语言串口通信_stm32 串口通信收发说明
  8. c 调用openoffice word转pdf_批量 Word 转 PDF 方法
  9. 多路平衡归并和败者树
  10. harbor安装_Harbor任意管理员注册漏洞(CVE-2019-1609) (附:批量利用poc)
  11. Vue指令之v-bind
  12. laravel nginx_在nginx上用FastCGI解析PHP
  13. 初始单片机 ----自学笔记
  14. 学会 Python 到底能干嘛?我们整理了 7 大工作方向 + 170 个项目课程给你
  15. 写专利的一点小小心得
  16. 短视频app源码开发,短视频录制的实现
  17. vim开启粘贴板功能
  18. java.lang.IllegalArgumentException: View=DecorView not attached to window manager(Android Dialog崩溃)
  19. iOS 绘制股票K线图
  20. 如何实现分库分表?怎么配置?

热门文章

  1. oracle:oracle学习案例(五):基础练习
  2. 关于/etc/shadow 文件的九段内容的详细解释
  3. 【Elasticsearch】es 插件类型
  4. 【Elasticsearch】elasticsearch 分片
  5. 【hadoop】ipc.Client: Retrying connect to server: xxx:8020. Already tried 37 time(s) RetryPolicy[Multi
  6. SpringBoot : BeanFactory
  7. drools 7.x 事件元数据
  8. Esper事件处理引擎 EPL语法-模式匹配
  9. 一个让程序员男友记住一辈子的 IntelliJ IDEA 插件!
  10. 教你用纯Java实现一个即时通讯系统(附源码)