在逛 programcreek 的时候,我发现了一些专注基础但不容忽视的主题。比如说:Java 的可变参数究竟是怎么一回事?像这类灵魂拷问的主题,非常值得深入地研究一下。
灵魂拷问:Java 如何获取数组和字符串的长度?length 还是 length()?
限时 1 秒钟给出答案,来来来,听我口令:“Java 如何获取数组和字符串的长度?length 还是 length()?”
面试官问:HTTP 的负载均衡你了解么?你不是说了你们用的Nginx么?说一下把。
之前我讲了关于 HTTP 的安全性问题,本篇文章将会从一个朋友的面试题入手,来说一下关于 HTTP 的重定向和负载均衡。
带你涨姿势的提升一下 kafka 内功
如果你只追一个妹子并且对这个妹子特别用心的话,知道的人一定会说你是个好男人;如果你只是浅尝辄止并且追了大部分妹子的话,知道的人一定会骂你渣男。
带你深入理解HTTP的秘密(密钥)
之前的文章说了一下关于 Cookie 的内容,但是也就引出来了一些问题,比如 HTTP 是怎么进行安全处理的?来了,本文给大家讲述 HTTP 的安全问题。
原生线程池这么强大,Tomcat 为何还需扩展线程池?
前言
Tomcat/Jetty 是目前比较流行的 Web 容器,两者接受请求之后都会转交给线程池处理,这样可以有效提高处理的能力与并发度。JDK 提高完整线程池实现,但是 Tomcat/Jetty 都没有直接使用。Jetty 采用自研方案,内部实现 QueuedThreadPool
线程池组件,而 Tomcat 采用扩展方案,踩在 JDK 线程池的肩膀上,扩展 JDK 原生线程池。
JDK 原生线程池可以说功能比较完善,使用也比较简单,那为何 Tomcat/Jetty 却不选择这个方案,反而自己去动手实现那?
Flink 基础学习(七) 窗口 Window
1 前言
前面讲了时间 Time
的概念和实际解决问题后,本篇来看下经常搭配使用的另一个关键工具:窗口 Window
。
窗口也有三种类型可供选择使用:
- Tumbling Windows:滚动窗口
- Sliding Windows:滑动窗口
- Session Windows:会话窗口
友情提示,本篇主要翻译自官网以及参考了 wuchong
大神的博客,内容比较干货,介绍这三种窗口的概念以及使用场景,希望看完能对 Flink
的窗口概念加深理解。
真的,关于 Kafka 入门看这一篇就够了
Kafka 系列的阶段性总结(万字长文,做好准备)
【集合系列】- 深入浅出分析 PriorityQueue
PriorityQueue 一个特殊的优先级队列,今天咱们一起来揭开它的面纱!
【集合系列】- 深入浅出分析 ArrayDeque
ArrayDeque 一个循环数组,诞生于 JDK 1.6,今天小编想和大家一起来揭开它的面纱!
Flink 基础学习(六)时间 Time 和 Watermark
前面的例子中有出现过 时间窗口 TimeWindow
这个词语,其实是两个概念,时间 Time
和窗口 Window
。
本篇文章比较干货,主要翻译自官网(参考资料一), 来讲下关于 Time
的学习、理解以及配套的概念 Watermark
。
Watermark
有两种译法:水位线、水印。由于个人暂时分不清,所以后面一律以英文 Watermark
出现
这篇文章我们来聊聊什么是程序的局部性原理
01、前言
作为有追求的程序员,我们日常在写代码的时候往往都会运用很多奇技淫巧,不单单是为了炫耀我们的技术,更是为了追求更高的效率。了解局部性原理,可以有效的帮助我们理解和写出更好的代码,对于局部性原理可能有的小伙伴知道,有的小伙伴不知道,知道的小伙伴就当做复习知识点,不知道的小伙伴也没关系,接着往下看就知道了。
kafka 消费者
Flink 基础学习(五)数据存储 DataSink
1 前言
先来回顾一下 Flink
基础的三兄弟:
- 数据来源
DataSource
- 数据转换
Transaformation
- 数据存储
DataSink
前面两篇笔记已经写了数据来源和转换如何使用,那么这篇当然就到了数据存储,接下来将会从以下角度介绍一下(喜闻乐见的 What
/ Why
/ How
)~:
- 1 为什么要用
Sink
- 2
DataSink
是什么 - 3 如何使用(进阶使用,滑动时间窗口例子)
Java 又双叒叕发布新版本,这么多版本如何灵活管理?
前言
不知不觉 JDK13 发布已有两个月,不知道各位有没有下载学习体验一番?每次下载安装之后,需要重新配置一下 Java 环境变量。等到运行平时的项目又需要切回之前 JDK 版本,这又需要重新环境变量。这么重复配置显然非常低效,又不能灵活切换版本。
所幸通过万能 Google 找到解决方案,使用 jenv 管理 JDK 版本。
你还拿着培训班教给你的 Cookie 去面试?确定不要进来看一下 Cookie 到底是什么样子的?
说实话,之前面试都是直接去背诵的面试题,关于 Cookie 的一些内容,比如说,记录浏览器端的数据信息啦, Cookie 的生命周期啦,这些内容,也从来没有研究过 Cookie 到底是怎么工作的,今天我们就来了解一下 Cookie 到底是怎么工作的。
羞,Spring Bean 初始化/销毁竟然有这么多姿势
一、前言
日常开发过程有时需要在应用启动之后加载某些资源,或者在应用关闭之前释放资源。Spring 框架提供相关功能,围绕 Spring Bean
生命周期,可以在 Bean
创建过程初始化资源,以及销毁 Bean
过程释放资源。Spring 提供多种不同的方式初始化/销毁 Bean
,如果同时使用这几种方式,Spring 如何处理这几者之间的顺序?
有没有觉得标题很熟悉,没错标题模仿二哥 「@沉默王二」 文章羞,Java 字符串拼接竟然有这么多姿势。
二、姿势剖析
首先我们先来回顾一下 Spring 初始化/销毁 Bean
几种方式,分别为:
init-method/destroy-method
InitializingBean/DisposableBean
@PostConstruct/@PreDestroy
ContextStartedEvent/ContextClosedEvent
PS: 其实还有一种方式,就是继承 Spring
Lifecycle
接口。不过这种方式比较繁琐,这里就不再分析。
2.1、init-method/destroy-method
这种方式在配置文件文件指定初始化/销毁方法。XML 配置如下
1 |
|
或者也可以使用注解方式配置:
1 |
|
还记得刚开始接触学习 Spring 框架,使用就是这种方式。
2.2、InitializingBean/DisposableBean
这种方式需要继承 Spring 接口 InitializingBean/DisposableBean
,其中 InitializingBean
用于初始化动作,而 DisposableBean
用于销毁之前清理动作。使用方式如下:
1 |
|
2.3、@PostConstruct/@PreDestroy
这种方式相对于上面两种方式来说,使用方式最简单,只需要在相应的方法上使用注解即可。使用方式如下:
1 |
|
这里踩过一个坑,如果使用 JDK9 之后版本 ,
@PostConstruct/@PreDestroy
需要使用 maven 单独引入javax.annotation-api
,否者注解不会生效。
2.4、ContextStartedEvent/ContextClosedEvent
这种方式使用 Spring 事件机制,日常业务开发比较少见,常用与框架集成中。Spring 启动之后将会发送 ContextStartedEvent
事件,而关闭之前将会发送 ContextClosedEvent
事件。我们需要继承 Spring ApplicationListener
才能监听以上两种事件。
1 |
|
也可以使用 @EventListener
注解,使用方式如下:
1 |
|
PS:只有调用
ApplicationContext#start
才会发送ContextStartedEvent
。若不想这么麻烦,可以监听ContextRefreshedEvent
事件代替。一旦 Spring 容器初始化完成,就会发送ContextRefreshedEvent
。
三、综合使用
回顾完上面几种方式,这里我们综合使用上面的四种方式,来看下 Spring 内部的处理顺序。在看结果之前,各位读者大人可以猜测下这几种方式的执行顺序。
1 |
|
xml 配置方式如下:
1 |
|
应用启动方法如下:
1 |
|
程序输出结果如下所示:
最后采用图示说明总结以上结果:
四、源码解析
不知道各位读者有没有猜对这几种方式的执行顺序,下面我们就从源码角度解析 Spring 内部处理的顺序。
4.1、初始化过程
使用 ClassPathXmlApplicationContext
启动 Spring 容器,将会调用 refresh
方法初始化容器。初始化过程将会创建 Bean
。最后当一切准备完毕,将会发送 ContextRefreshedEvent
。当容器初始化完毕,调用 context.start()
就发送 ContextStartedEvent
事件。
refresh
方法源码如下:
1 |
|
一路跟踪 finishBeanFactoryInitialization
源码,直到 AbstractAutowireCapableBeanFactory#initializeBean
,源码如下:
1 |
|
BeanPostProcessor
将会起着拦截器的作用,一旦 Bean 符合条件,将会执行一些处理。这里带有 @PostConstruct
注解的 Bean
都将会被 CommonAnnotationBeanPostProcessor
类拦截,内部将会触发 @PostConstruct
标注的方法。
接着执行 invokeInitMethods
,方法如下:
1 |
|
如果 Bean
继承 InitializingBean
接口,将会执行 afterPropertiesSet
方法,另外如果在 XML 中指定了 init-method
,也将会触发。
上面源码其实都是围绕着 Bean
创建的过程,当所有 Bean
创建完成之后,调用 context#start
将会发送 ContextStartedEvent
。这里源码比较简单,如下:
1 |
|
4.2、销毁过程
调用 ClassPathXmlApplicationContext#close
方法将会关闭容器,具体逻辑将会在 doClose
方法执行。
doClose
这个方法首先发送 ContextClosedEvent
,然再后开始销毁 Bean
。
灵魂拷问:如果我们颠倒上面两者顺序,结果会一样吗?
doClose
源码如下:
1 |
|
destroyBeans
最终将会执行 DisposableBeanAdapter#destroy
,@PreDestroy
、DisposableBean
、destroy-method
三者定义的方法都将会在内部被执行。
首先执行 DestructionAwareBeanPostProcessor#postProcessBeforeDestruction
,这里方法类似与上面 BeanPostProcessor
。
@PreDestroy
注解将会被 CommonAnnotationBeanPostProcessor
拦截,这里类同时也继承了 DestructionAwareBeanPostProcessor
。
最后如果 Bean
为 DisposableBean
的子类,将会执行 destroy
方法,如果在 xml 定义了 destroy-method
方法,该方法也会被执行。
1 |
|
五、总结
init-method/destroy-method
这种方式需要使用 XML 配置文件或单独注解配置类,相对来说比较繁琐。而InitializingBean/DisposableBean
这种方式需要单独继承 Spring 的接口实现相关方法。@PostConstruct/@PreDestroy
这种注解方式使用方式简单,代码清晰,比较推荐使用这种方式。
另外 ContextStartedEvent/ContextClosedEvent
这种方式比较适合在一些集成框架使用,比如 Dubbo 2.6.X 优雅停机就是用改机制。
六、Spring 历史文章推荐
1、Spring 注解编程之注解属性别名与覆盖
2、Spring 注解编程之 AnnotationMetadata
3、Spring 注解编程之模式注解
4、缘起 Dubbo ,讲讲 Spring XML Schema 扩展机制
一文看懂 Redis 的内存回收策略和 Key 过期策略
01、前言
Redis 作为当下最热门的 Key-Value 存储系统,在大大小小的系统中都扮演着重要的角色,不管是 session 存储还是热点数据的缓存,亦或是其他场景,我们都会使用到 Redis。在生产环境我们偶尔会遇到 Redis 服务器内存不够的情况,那对于这种情况 Redis 的内存是如何回收处理的呢?另外对于带有过期时间的 Key Redis 又是如何处理的呢?
Flink 基础学习(四)转换 Transformation
前言
前面写了如何使用 Flink
读取常用的数据源,也简单介绍了如何进行自定义扩展数据源,本篇介绍它的下一步:数据转换 Transformation
,其中数据处理用到的函数,叫做算子 Operator
,下面是算子的官方介绍。
算子将一个或多个
DataStream
转换为新的DataStream
。程序可以将多种转换组合成复杂的数据流拓扑。
程序员,别再迷恋多线程工作了
我刚刚尝试了一下,一边用 iPad 看“Java 极客技术”自制的 SpringBoot 视频(1.2X 倍速),一边在 iMac 上回复博客上读者的留言。过了一会,视频上讲了什么,我完全没有印象了;而回复的内容也写得乱七八糟。