Spring中实现HTTP缓存

本文翻译:吴嘉俊,叩丁狼高级讲师

缓存是HTTP协议中一个非常重要的特征。但是由于某些原因,在HTTP协议中,缓存常常只用来做图片,CSS样式表或者JS等静态文件缓存。其实,HTTP缓存不仅仅可以用来做静态资源的缓存,同样对动态的请求也同样有用。

仅仅只需要一些简单的工作,你就可以提高应用的响应速度,提高用户体验。在这篇文章中,你将会学到如何在Spring中使用内置的HTTP响应缓存机制来缓存controller的结果。

如何使用HTTP响应缓存?

你可以在应用中的各个层次做缓存。比如数据库可以用内存缓存存储,在应用层可以缓存业务数据,在web层当然也可以缓存和重用数据。

客户端和服务器依赖HTTP协议进行交流。缓存机制允许我们通过减少在客户端和服务器之间传输的数据量来优化网络传输压力。

那么我们能做哪些优化呢?

当一个资源不会频繁更新,并且我们非常准确的知道数据什么时候更新的时候,HTTP缓存就是一个不错的优化性能的方案。

一旦确定使用HTTP缓存策略,你就必须要选择一种合适的验证缓存的机制。HTTP协议提供了多种请求头和响应头参数,可以用来控制缓存。

选择使用哪种HTTP头取决于你想怎么优化缓存。但是,不管你怎么使用,我们都可以根据缓存使用在什么地方,来划分缓存的管理选项,可以在客户端验证,也可以再服务器端验证。

我们来看看具体的使用。

客户端缓存验证

当你知道一个请求的资源在一个指定的时间内不会发生改变,服务器端可以将这个时间信息通过响应头发送给客户端。根据这个缓存时效信息,客户端可以选择是否重新冲服务端请求信息,或者重用之前已经下载的信息。

有两个可选的头参数可以控制什么时候客户端需要重新从服务器端获取数据,什么时候删除缓存。我们在实战中来看看。

HTTP缓存固定时间

如果你想阻止客户端在一个指定时间内不要去请求服务器,你可以使用Cache-Control头信息,通过设置获取到的数据可以在多长时间之内重复使用。

或者你可以通过设置max-age=[seconds]信息来告诉客户端,在多少秒之内,资源可以缓存使用。缓存的有效性与请求的时间有关。

为了在Spring的控制器中控制HTTP头信息,我们需要返回ResponseEntity包装类型。下面是一个例子:

@GetMapping("/{id}")
ResponseEntity<Product> getProduct(@PathVariable long id) {
   // …
   CacheControl cacheControl = CacheControl.maxAge(30, TimeUnit.MINUTES);
   return ResponseEntity.ok()
           .cacheControl(cacheControl)
           .body(product);
}

在头中的信息应该是一个普通的字符串,但是在Spring中,专门为Cache-Control提供了一个builder类来辅助设置这个响应头信息。

HTTP缓存到指定的日期

在一些情况下,我们是知道一个资源什么时候会被更新,这种方式在定时发布数据的情况下是非常有用的,比如定时发布天气预报,或者定时发布昨天的股市行情信息等。在这种情况下,更适合告诉客户端具体的资源缓存到的日期/时间。

为了达到这个目的,我们可以使用Expires头信息。缓存到期的日期/时间需要按照以下标准格式设置:

Sun, 06 Nov 1994 08:49:37 GMT  ; RFC 822, updated by RFC 1123
Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
Sun Nov  6 08:49:37 1994       ; ANSI C's asctime() format

幸运的是,Java中提前定义好了第一种日期格式(RFC 1123)。下面给出一个例子,展示如何将缓存失效时间设定到今天的结束。

@GetMapping("/forecast")
ResponseEntity<Forecast> getTodaysForecast() {
   // ...
   ZonedDateTime expiresDate = ZonedDateTime.now().with(LocalTime.MAX);
   String expires = expiresDate.format(DateTimeFormatter.RFC_1123_DATE_TIME);
   return ResponseEntity.ok()
           .header(HttpHeaders.EXPIRES, expires)
           .body(weatherForecast);
}

注意一点的是,HTTP日期格式化需要明确指定时区。这就是为什么上面的例子中使用ZonedDateTime的原因。如果是使用LocalDateTime来代替的话,在运行的时候,会出现下面的错误:

java.time.temporal.UnsupportedTemporalTypeException: Unsupported field: OffsetSeconds

如果Cache-Control和Expires两个头信息同时设置了,Cache-Control的优先级更高。

服务端缓存验证

在另一种情况,服务器端根据用户的输入动态生成内容,在这种情况下,服务器一般是不知道这个资源在客户端需要缓存多长时间。在这种情况下,缓存的方式就变成了:客户端首先需要请求服务器,刚才的数据是否为合法的缓存数据,如果是,可以继续使用之前的缓存数据,如果不是,则重新获取。

资源是否仍然有效?

如果你能够跟踪某个资源的修改时间,那么你可以这样处理服务器缓存:将资源的修改时间作为相应头信息返回给客户端,之后,每一次请求,客户端都会将这个时间作为请求头的一部分发回给服务端,服务端检查从上次请求截止到现在,资源是否被修改过,如果资源在这段时间没有被修改过,则不需要重新返回数据,只需要返回一个304状态码即可。

想要返回一个资源的修改时间,需要设置Last-Modified头信息。Spring提供的ResponseEntity提供了一个lastModified()方法能够使用特定格式的值来填充Last-Modified头信息。这个方法待会会在代码中展示。

但是当你在返回用户完整的响应之前,服务器端需要检查客户端的请求头中是否包含了If-Modified-Since头信息。如果客户端之前对这个资源请求的响应中,包含了Last-Modified这个响应头,并且这个资源的缓存没有被重置,那么在之后针对该资源的请求中就会自动设置这个值。

如果If-Modified-Since头信息包含的时间匹配资源的修改时间,即该资源在这个时间段内,并没有被修改过,则你可以直接返回一个空的响应。

同样,Spring提供了一个非常简单的方法来辅助判断这个修改时间。这个方法就是WebRequest包装对象中的checkNotModified(),这个WebRequest对象可以放在控制器方法的参数列表中,Spring会自动注入进来。

我们来完整的看一个实例代码,了解整个过程:

@GetMapping("/{id}")
ResponseEntity<Product> getProduct(@PathVariable long id, WebRequest request) {
   Product product = repository.find(id);
   long modificationDate = product.getModificationDate()
           .toInstant().toEpochMilli();

   if (request.checkNotModified(modificationDate)) {
       return null;// code
   }

   return ResponseEntity.ok()
           .lastModified(modificationDate)
           .body(product);
}

首先,我们通过请求的id获取Product,将Product的更新时间转换成1970年1月1日 GMT的毫秒数,这个格式是Spring用来对比Last-Modified的格式。

接着,我们对比request头中的If-Modified-Since,如果时间匹配,则直接返回一个空响应体。如果时间不匹配,则返回一个包含有最新Last-Modified头的完整的响应体。[注:在code这块地方,也可以通过return ResponseEntity.status(HttpStatus.NOT_MODIFIED).build(); 返回一个304响应]。

上面的这些知识,已经足够你应付大部分缓存的情况了。但是,还有一种非常重要的机制,你需要注意:

使用ETag标记资源版本

在上面的例子中,缓存失效的最小失效单位为1秒钟。那么,假如你需要一个比秒更精确的缓存控制精度呢?这就是ETag处理的问题。

ETag定义了一个唯一的字符串值,这个字符串值可以唯一的标记在一个确切的时间点的一个资源。通常情况下,ETag在服务端,使用要标记的资源相关联的属性来计算,或者,也可以使用更精确的资源修改时间。

使用ETag的情况下,服务器和客户端之间的通信流程和上面使用修改时间的方式是基本一致的,只是报头信息的名字和值不一样。

在服务器端,就是使用ETag这个名字设置响应头即可。当客户端再次请求这个资源的嘶吼,他会将这个ETag值放到请求头If-None-Match属性中,如果这个值和服务器端该资源最新的ETag匹配,则服务器端只需要返回一个空响应体的304响应即可。

在Spring中,你可以像这样设置ETag的值:

@GetMapping("/{id}")
ResponseEntity<Product> getProduct(@PathVariable long id, WebRequest request) {
   Product product = repository.find(id);
   String modificationDate = product.getModificationDate().toString();
   String eTag = DigestUtils.md5DigestAsHex(modificationDate.getBytes());

   if (request.checkNotModified(eTag)) {
       return null;
   }

   return ResponseEntity.ok()
           .eTag(eTag)
           .body(product);
}

是不是和Last-Modified的例子很像?

是的,这个例子几乎和上面那个修改时间检查的例子完全一样。我们只是使用了另外的一个值交给checkNotModified去比较(我们将修改时间使用MD5加密来生成对应的ETag值)。注意,checkNotModified方法针对ETags有重载方法,这里是传入一个字符串,Last-Modified的例子中是传入一个long值。

既然Last-Modified和ETag几乎相同,为什么这两种策略会同时存在呢?

Last-Modified VS ETag

在上面的文字中我已经提到过,Last-Modified策略在精度上是有一定限制的,因为最小精度是秒。如果需要更高的缓存控制精度,就需要选择ETag。

如果资源不能使用修改时间来标记,那么也需要使用ETag。服务端可以通过资源的其他属性来计算ETag值,比如对象的hashcode值(甚至是对象的序列化版本号)。

如果一个资源有自己的修改时间,并且1秒钟的精度足够使用,那么我建议使用Last-Modified头。因为ETag的计算成本较高。

当然,在HTTP协议中,并没有强行要求ETag的计算算法,所以,当我们在选择ETag算法的时候,请注意考虑算法的速度。

这篇文章的主题是GET请求下的缓存,但是你可以了解一下,ETag在处理同步update操作的时候,是非常有用的,当然,这可以另开一篇文章了。

Spring ETag Filter

因为ETag只是根据响应内容表述的一段字符串而已,所以,服务器完全可以不依赖于响应体中具体的内容来获取ETag值,可以通过整个响应体的值来计算ETag值。这意味着,我们完全可以使用统一的方法为任何一个响应添加ETag值。

猜猜,万能的Spring会做什么?

是的,Spring框架提供了一个ETag响应过滤器实现,允许你直接通过过滤器,对响应直接计算并设置ETag值。所有你需要做的,仅仅只是配置一个过滤器而已。

最简单的配置方式,就是通过FilterRegistrationBean配置即可:

@Bean
public FilterRegistrationBean filterRegistrationBean () {
   ShallowEtagHeaderFilter eTagFilter = new ShallowEtagHeaderFilter();
   FilterRegistrationBean registration = new FilterRegistrationBean();
   registration.setFilter(eTagFilter);
   registration.addUrlPatterns("/*");
   return registration;
}

在这种情况下,我们也可以通过addUrlPatterns()方法来匹配你需要的URL模式。我这里仅仅只是一个演示,所以我把所有的请求都使用ETag进行验证了。

除了正常生成ETag,这个过滤器还能在缓存有效的情况下正确的返回HTTP304状态码和空响应体。但是再次强调一点,ETag的计算是非常昂贵的,所以,在有些应用中,使用这个过滤器甚至会带来更大的性能损耗,所以,请一定要思考清楚。

原文地址:https://www.javacodegeeks.com/2018/10/http-cache-spring-examples.html

 

相关推荐

运行时数据区

本文作者:李敏,叩丁狼高级讲师。原创文章,转载请注明出处。前言为什么要了解虚拟机如何操作内存?java与c/c++之间有一堵由内存动态分配和垃圾收集技术所围成的"高墙",墙外面的人想进去,墙里面的人却想出来.对于java程序员来说,在虚拟机自动内存管理机制的帮助下,不再需要为每一个new操作去写配对的delete/free代码,不容易出现内存泄漏和内存溢出.有虚拟机管理内存,这一切看起...

wolfcode_cn的博客 1015

使用Mycat实现数据库读写分离

本文作者:蓝雄威,叩丁狼高级讲师。原创文章,转载请注明出处。01前言在上一篇文章中,我们基于CentOS7实现了MySQL的主从复制,我们需要把DML操作放在MySQL的主节点执行,需要把DQL操作放在MySQL的从节点执行.那在应用程序中我们怎么控制这个逻辑呢?下面是一种可行的方案:1.在项目中配置两个数据源(一主一从),分别是A(主)和B(从)。2.在程序中判断是不是select...

wolfcode_cn的博客 1342

线程基础(三十三)

接上篇,本篇讲解线程另外一个设计模式:Producer-Consumer Pattern.概念Producer是生产,生产者的意思, 指生产数据的线程, Consumer 则是消费,消费者的意思. 指使用数据的线程.Producer-Consumer 模式主要目标:生产者生产数据能安全得交给消费者进行处理.当Producer 跟 Consumer 都为一个线程时, Producer-Con...

wolfcode_cn的博客 963

线程基础(三十二)

接上篇,本篇讲解线程另外一个设计模式:Balking Pattern.概念Balking 是停止并返回意思. 如果当前线程不适合(不满足守护条件)执行某个操作直接返回,这就是Balking 模式.Balking 模式跟之前的Guarded Suspension模式有点类似, 区别:Guarded Suspension模式不满足守护条件,线程需要挂起等待, 而Balking 模型不满足守...

wolfcode_cn的博客 804

线程基础(三十一)

本文作者:王一飞,叩丁狼高级讲师。原创文章,转载请注明出处。接上篇,本篇讲解线程另外一个设计模式:Guarded Suspension Pattern.概念Guarded是被守护,被保卫,被保护的意思, Suspension 则是暂停的意思. 如果不满足执行条件,先让当前处理的线程暂停.这就是Guarded Suspension模式.参与角色Guarded Suspension模式参...

wolfcode_cn的博客 1017

刚进职场的程序员,和工作了2、3年的程序员到底有什么不一样?

“老师,我们刚进职场,和工作了2、3年的程序员到底有什么不一样?”这是很多同学在进入职场后,问过我最多的问题。不妨让我来翻译一下:老师,我到底有哪些表现让我在职场显得特别“嫩”,没办法获得领导、同事更多的认可和信任?于是我总结分析了工作中常见的三个误区,如果在工作中有意识地规避这些误区,也许就能帮助你快速成为“职场老司机”——01 不敢争取【情景】公司有一个做大项目的机会,经理问你是否愿意参与,但你没有十足的把握,最后权衡来权衡去还是拒绝了。【解析】这里面有两个层次的问题。第一,还是自信.

wolfcode_cn的博客 4942

『面试篇』注意面试中常见的三大误区,大幅度提升你被录用的机会

​先分享一个真实案例:之前我发现有一个学员把简历上面的项目经验删除了三分之二,我问他为什么?他说因为在一场面试中HR说不相信这些项目是他做的,没有给他解释机会就结束了面试。于是他认为找不到工作是因为自己做的项目经验太多了,于是在接下来的面试中,不仅删除了自己做过的项目,还在面试环节装作不懂的样子。这是很明显的心态和认知都被面试官影响的例子。大家好,我是叩丁狼的就业老师刘梓华。这些年帮助过上千个学员找到工作,不管是学员的面试问题还是工作期间遇到的非技术问题,我都有一些想和大家分享的经验。针.

wolfcode_cn的博客 1121

初级程序员应该怎么选电脑?来自从业5年维修工程师的建议

大家好,我是叩丁狼的彬辉老师。因为工作原因,经我手修好的电脑没有一千也有八百,关于编程开发对硬件的要求,也是了然于心。不管你是什么级别的程序员,选配置也是一件头疼的事,这里我给大家整理一些简单的配置参考,希望能帮到大家——其实电脑配置远不止这些,还有散热、主板、处理器、电源等等,这次主要提到对电脑性能影响较大的四个核心参数,方便同学们更快更精确地挑选合适的电脑,如果你对电脑已经有一定的了解,欢迎来办公室找我交流~至于选择笔记本还是台式,完全看个人喜好:同等配置下,台式机性价比更高,可以定制化.

wolfcode_cn的博客 8286

互联网最大谣言:程序员35岁必淘汰?今天我就来击碎他!

朋友,只要你是程序员,你一定知道996和“程序员35岁必死”的言论。这两个话题在互联网上的讨论一次比一次激烈。996工作制,众所周知,每天早上9点到岗,一直待到晚上9点,每周工作6天,很多互联网公司在招聘前甚至就会私下告诉你公司实行996制度。尽管很多年轻程序员在工作之前已经做了一定的心理准备,而一旦真正直面这些问题,心里肯定会打怵:“我现在年轻,还可以接受加班,可等我成家了、年纪大了、身体吃不消怎么办?”今天我就从个人的角度跟大家分享一下我的看法。程序员最爱给自己挖坑本人从事过开发工作,现在

wolfcode_cn的博客 1万+

用了这么久开源社区,还搞不明白免费/自由/开源软件?

在开源社区中,有很多人经常把免费软件、自由软件、开源软件这三个概念混淆在一起。今天就来和大家谈谈这三个软件到底有什么不一样。免费软件用户可以免费使用该软件,但通常会有其他权限的限制:1.源码不一定会公开/不能修改软件源码;2.限制拷贝的代码数量和段落;3.不得用于商业用途、不能以该软件牟利、仅供个人使用、仅供学习交流使用等等,如果有人未经作者允许则随意散布、传播,作者有权进行追责;4.限制使用者研究、修改和再散布的权利;免费软件免费的目的无非出于:先提供免费版本软件扩大市场占有率,当

wolfcode_cn的博客 1635

中国开发者现状报告:最想学Java语言、金融业高收入比例大、仅2%的人不安排学习时间......

​在《2019-2020 年中国开发者调查报告》中,我们从「开发者现状」中有最新的发现:6成开发者在使用Java语言;在一线城市,30%开发者月薪超过1.7万元;互联网、软件、IT制造三个技术领域涵盖了国内84%以上的开发者;开发者学习热情高涨,31%接受了软件开发的在职培训…*以下数据报告来源:优快云软件开发工具使用特点分析01Java长盛:使用最多、最想学60%的开发者使用最多的编程语言是Java,Java、Python、Javascript是开发者最想学的三种语言。左图为开

wolfcode_cn的博客 1093

如何在百度中脱颖而出——前端与SEO

SEO:译为搜索引擎优化,搜索引擎优化技术现在随着百度,谷歌,搜狗等搜索引擎的兴起,越来越火爆,那今天,我们一起来看看,SEO和前端有着怎样千丝万缕的联系~SEO的由来:早在90年代国外便有了SEO的概念,曾经由于谷歌的SEO,导致很多媒体行业的不满。后来大约03年,由于一款游戏(传奇私服),各种私服产业恶性竞争,慢慢的,SEO在中国逐渐商业化。SEO分类:SEO主要分为白帽SEO和黑帽SEO,就像白帽黑客和黑帽黑客一样。白帽SEO:改良网站设计规范,使网站对搜索引擎和用户更加友好黑帽SEO:利

wolfcode_cn的博客 989

28岁程序员郭宇财务自由退休,都说他运气爆棚,但我看未必...

今年6月,一封退休告别信最近引爆了微博、知乎等各大平台,一位名叫郭宇的程序员走进了大众的视野。郭宇是谁?他的微博简介是这么写的:株式会社山月夜代表取缔役社长/住过150+个日本温泉乡/100万公里飞行进度86.5%/程序员/原字节跳动资深技术专家。说到程序员,大家都会想到996、过劳、无休止地加班、没有自由…而这位程序员颠覆了大众的认知,不仅在28岁成功实现财务自由从字节跳动退休,并在日本开了一家温泉旅馆,当起了作家。面对这一系列的“暴击”,让不少互联网行业人直呼:“我酸了!”01 28岁财

wolfcode_cn的博客 3901

疫情让程序员活得更难吗?2020年程序员薪资满意度、跳槽状况、裁员补偿数据大公开!

自从网传程序员到了35岁之后必须要转行,现在又有人传言:“疫情之下,程序员今年要过苦日子了,降薪裁员是大趋势。”不是,我就不明白了,你们怎么就看不得程序员好呢?天天巴望着程序员降薪、转行、裁员…我们不可否认,从去年年底开始,疫情就导致了很多企业陷入危机,继而停发薪资、变相裁员,在如此“难搞”的大环境下,对程序员来说究竟意味着什么?在疫情期间,程序员客栈对北京、上海、广东、浙江等全国29个省、直辖市、特别行政区的近40万程序员进行了一次详细调查。我想这份详细调查数据足以吊打网络上传出莫名其妙的“程序

wolfcode_cn的博客 2173

程序员和码农有什么不同?从这三个单词就看得出来

码农这个词很早就有了,但我以为这只是程序员偶尔自黑的幽默,直到论坛、博客上越来越多人正儿八经地自称“码农”、“码畜”、“搬砖的”…有人认为“码农”、“程序员”、“工程师”等等这些称谓大可不必过于在意,名称和真实情况是两回事。不过我也比较介意别人叫我码农,可能本身并无恶意,但是感觉这个称呼实在配不上自己的工作创意与工作程度,就像我可以说自己是屌丝,但别人这么叫自己肯定不舒服。举个对比的例子:设计师VS美工艺人VS唱歌的心理咨询师VS路边算卦财务计算VS收银新媒体运营VS小编…程序员不是码

wolfcode_cn的博客 6987

CSS3绘制各种形状

本文为叩丁狼高级讲师原创文章,转载请注明出处。现在的CSS功能非常强大了,特别是CSS3的属性,例如转换属性,过渡属性,动画属性,能做的效果非常多。在网页开发中,会经常遇到一些小符号或者形状,在以前就只能通过切图来实现,切图这种方式是用起来方便,但是会增加请求。而现在浏览器对CSS3的兼容基本没有什么问题,所以在网页开发的时候遇到符号或者形状,能写的都是用CSS来书写了。而这篇文章就是收...

wolfcode_cn的博客 1114

强大而好用的选择器:focus-within

本文为叩丁狼高级讲师原创文章,转载请注明出处。伪类和伪元素在开发网页样式中,选择器必不可少,而且选择器也是在开发css中非常重要的内容,包括常用的类选择器,id选择,同时还有伪类,伪类选择器最大的特点就是冒号开头。平时也经常会有小伙伴问到,在使用的时候有伪类,也有伪元素,有些添加单冒号,有些添加双冒号,有没有什么区别?在网上总结的比较多的一个特点就是,其实在使用的时候,无论是伪类还是伪...

wolfcode_cn的博客 1046

JavaScript-DOM操作

本文为叩丁狼高级讲师原创文章,转载请注明出处。在之前的文章中,学习过了DOM的访问方式,其中也把DOM相关的概念进行了学习,其实DOM的内容还有很多,所以本文将会继续学习DOM操作相关的内容,DOM操作涉及到的操作有:增、删、改、查,而查这方面的内容其实就是上一次所写的“访问方式”。##DOM的增加DOM操作中增指的是增加节点,分为两部分:创建节点和插入节点。###创建节点创建节...

wolfcode_cn的博客 682

超简单自动编译sass扩展和工具

本文为叩丁狼高级讲师原创文章,转载请注明出处。在之前的文章中,学习过了DOM的访问方式,其中也把DOM相关的概念进行了学习,其实DOM的内容还有很多,所以本文将会继续学习DOM操作相关的内容,DOM操作涉及到的操作有:增、删、改、查,而查这方面的内容其实就是上一次所写的“访问方式”。##DOM的增加DOM操作中增指的是增加节点,分为两部分:创建节点和插入节点。###创建节点创建节...

wolfcode_cn的博客 684

Css、less和Sass(SCSS)

本文为叩丁狼高级讲师原创文章,转载请注明出处。背景CSS(层叠样式表)是一门非程序式语言,入门学习使用非常直观方便,但是队友一些比较复杂或者重用性比较强的项目来说,因为CSS没有变量、函数、SCOPE(作用域),需要书写大量看似没有逻辑的代码,不方便维护及扩展,不利于复用,尤其对于非前端开发工程师来讲,往往会因为缺少 CSS 编写经验而很难写出组织良好且易于维护的 CSS 代码。为了方便前...

wolfcode_cn的博客 1642

面试题:如何在10亿个随机整数中找出前1000个最大的数

本文作者:梁开权,叩丁狼高级讲师。原创文章,转载请注明出处。我们知道排序算法有很多:冒泡算法:通过两层for循环,外层第一次循环找到数组中最大的元素放置在倒数第一个位置,第二次循环找到第二大的元素放置在倒数第二个位置。。。循环N次就可以找到TopN。缺点:冒泡排序内层循环需要大量交换元素。复杂度介于O(n)和O(n^2)之间。快速排序:选一个基准元素,每次排序可以将这个基准元素...

wolfcode_cn的博客 1631

MyBaits-Plus

本文作者:范佳龙,叩丁狼高级讲师。原创文章,转载请注明出处。MyBaits-PlusMyBatis的优点Mybatis 是一款优秀的持久层框架,它支持定制化 SQL、存储过程以及高级映射。MyBatis 避免了几乎所有的 JDBC 代码和手动设置参数以及获取结果集。MyBatis 可以使用简单的 XML 或注解来配置和映射原生信息,将接口和 Java 的 POJOs(Plain Old ...

wolfcode_cn的博客 1217

线程基础(三十四)-Read-Write Lock Pattern

本文作者:王一飞,叩丁狼高级讲师。原创文章,转载请注明出处。接上篇,本篇讲解线程另外一个设计模式:Read-Write Lock Pattern.概念Read是读, 指获取/查询数据的线程, Write是写,指操作(增删改)数据的线程.Read-Write Lock 模式要求:1>在读模式时,多个线程可以同时执行读操作,但不允许写操作2>在写模式时,只允许一个线程执行写...

wolfcode_cn的博客 509

一起来看源代码-01TreeMap添加操作

本文作者:黄海燕,叩丁狼高级讲师。原创文章,转载请注明出处。##前言之前很多小伙伴问我怎么看源代码,还有就是越来越多的程序员都想要看源代码,搞懂底层原理,但是感觉源代码非常的晦涩难懂,不够直接和清晰,所以我希望这篇文章能够快速带同学们看懂java源码,更加深入的学习java,帮助小伙伴们节约学习的时间成本.##1.树的介绍什么是树结构?其实就是一个节点下面有多个子节点,我们称之为...

wolfcode_cn的博客 483

MongoDB With Jav

本文作者:杨龙,叩丁狼高级讲师。原创文章,转载请注明出处。准备工作要使用 Java 代码操作 MongoDB 数据库,创建 Maven 项目则先添加 MongoDB 的 Java 驱动依赖.<!-- MongoDB 的 Java 驱动 --><dependency> <groupId>org.mongodb</groupId>...

wolfcode_cn的博客 623

垃圾回收器和内存分配策略

本文作者:李敏,叩丁狼高级讲师。原创文章,转载请注明出处。4. 垃圾回收器和内存分配策略**GC(Garbage Collection)**的历史比java久远.1960年诞生于MIT的Lisp是第一门真正使用内存动态分配和垃圾收集技术的语言.GC一直致力于解决的问题:哪些内存需要回收(what)?什么时候回收(when)?如何回收(how)?在虚拟机运行时数据区中,最频繁使用的是...

wolfcode_cn的博客 419

Sharding JDBC-分库分表

本文作者:梁开权,叩丁狼高级讲师。原创文章,转载请注明出处。环境准备pom.xml<parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <ver...

wolfcode_cn的博客 558

Sharding JDBC-读写分离

本文作者:梁开权,叩丁狼高级讲师。原创文章,转载请注明出处。读写分离接着上一篇的分表分库我们继续来说读写分离案例模型让数据库的读和写功能分开,其中master负责所有的写操作以及在特殊情况下负责少量的读操作,slave在任何情况下都负责所有的读操作,不负责任何写操作,同样完成这个操作我们需要至少需要2个连接池,具体选择哪个连接池来操作取决于内部的一个叫路由的组件,该组件具有SQL的识别...

wolfcode_cn的博客 466

线程基础(三十)

本文作者:王一飞,叩丁狼高级讲师。原创文章,转载请注明出处。接上篇,本篇讲解线程另外一个设计模式:Immutable Pattern. 不可变模式.概念Immutable Pattern:不可变模式, 设计思路非常简单, 要求共享资源是一个不可改变对象, 线程只能访问资源,而不允许对资源修改.并发环境下即使不使用加锁操作,也可保证线程操作安全.知识补充敞开讲述前,先来科普一下不可对象...

wolfcode_cn的博客 282

线程基础(二十八)

本文作者:王一飞,叩丁狼高级讲师。原创文章,转载请注明出处。#####List集合######SynchronizedList :一个由普通list转换而来的线程安全集合List list = Collections.synchronizedList(new LinkedList(…));######CopyOnWriteArrayList :一种读写安全的ArrayList集合Co...

wolfcode_cn的博客 279
上一篇: 基于CentOS7搭建GitLab
下一篇: Netty版Helloworld
叩丁狼
叩丁狼 企业官方账号 企业官方账号
博客等级 码龄9年 914粉丝 349原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值