1.关于java工具类的工具工具理解 为什么java要提出个工具类的概念,按照面向对象的源码源码思想为什么不直接把工具
2.微服务下不同Websocket Java工具类,服务端和客户端的工具工具代码实现
3.Java后端项目亮点少?试试看模版方法+泛型+多态,一份代码适配n个相似业务流
4.跟着 Guava 学 Java 之 集合工具类
5.如何实现定时任务- Java Timer/TimerTask 源码解析
关于java工具类的源码源码理解 为什么java要提出个工具类的概念,按照面向对象的工具工具思想为什么不直接把工具
工具类其实就是对于String,Collection,源码源码ecplisejava源码IO等常用类的工具工具功能的扩展。比如IO读写文件。源码源码其实大多数时候我希望有一个文件路径,工具工具然后调个方法就直接得到文件内容(字符串或者字节数组形式)。源码源码
如果没有工具类,工具工具那么你每个读文件的源码源码地方都有一段重复的代码。所以,工具工具你肯定会被这个功能提取出来。源码源码那么,工具工具你提取出来放哪儿呀?要知道这个功能可是在任何类都能调用的。所以,我们就放在FileUtil里面了。
这样,你以后读取文件时,你就找FileUtil的read方法,直接调用即可。
为什么要将方法和变量都写成静态的。因为,这些方法我只是想调用一下,不需要牵扯工具类中的任何属性和变量,所以,我就没有必要实例化了(new)。既然不需要实例化了,那么就用静态就行了。
微服务下不同Websocket Java工具类,spark github源码下载服务端和客户端的代码实现
在程序开发中,WebSocket接口对于某些应用至关重要。本文将介绍四种常见的WebSocket实现方式:Springboot内置、Java-WebSocket、Okhttp3以及Jetty。这些工具适用于服务端和客户端的代码构建。1. Springboot内置WebSocket
内置websocket是最常用的选择,它提供了基本功能和sockJS支持。首先在pom.xml中添加相关starter,然后创建一个服务端点,并实现WebSocket服务。2. Java-WebSocket
Java-WebSocket库遵循Java API规范,适用于Java应用程序中的WebSocket通信,无论是服务器还是客户端,都易于集成。Java-WebSocket服务端
在pom.xml中引入相应组件,然后开始构建服务端逻辑。Java-WebSocket客户端
同样在pom.xml中添加依赖,实现客户端连接。3. Okhttp3
Okhttp3适用于Android开发,也支持WebSocket,但需要注意它不包含服务端创建功能,客户端实现更为常见。4. Jetty
Jetty是一个强大的HTTP服务器和Servlet容器,支持WebSocket功能。Jetty服务端
设置WebSocket端点并启动服务时启用。Jetty客户端
配置端点并连接到服务器。 以上四种工具提供了基础的WebSocket实现,但市场上还有更多优秀开源库可供选择。如果你发现有更实用的mvn 依赖源码编译工具,欢迎分享。感谢大家的探讨和使用!Java后端项目亮点少?试试看模版方法+泛型+多态,一份代码适配n个相似业务流
近期,我在工作中遇到了一个难题,那就是代码中存在大量相似的片段。为了解决这个问题,今天我想和大家探讨一下,如何利用设计模式来优雅地处理相似的业务流程代码。
通常,我们处理重复的代码,会通过提取公共方法或创建工具类的方式进行。比如,在处理时间问题时,可以使用反射来转换DTO和VO:通过BeanUtils.copyProperties()进行赋值。然而,对于许多业务,它们包含多个子业务,每个子业务都有相似的处理流程。以工单系统为例,面对不同类型的工单,查询思路大致相同,如果为每种工单类型单独编写代码,会产生大量相似的代码,这不仅影响了代码的维护和简洁性,还使得开发工作变得劳动密集,与追求自动化和精简工作量的原则相悖。
实际上,通过梳理业务流程,我们可以将其抽象为一系列算法骨架。电脑硬盘锁源码对于这些可以抽象的流程,我们可以采用设计模式中的模板方法来解决。模板方法模式是一种行为设计模式,它在超类中定义了一个算法的框架,允许子类在不修改结构的情况下重写算法的特定步骤。例如,在查询数据库时,不同的工单类型可能只是参数有所不同,这种情况下,我们可以使用泛型来处理。
以Java为例,我们可以通过模板方法+泛型的组合来简化代码。在基类中,我们可以定义一个通用的模板方法,并通过子类指定具体的类型参数来填充泛型。这样,只有第二点的代码需要根据子类进行修改,其他相似的代码都可以通过泛型和多态的方式插入模板方法参数中。
在项目和技术栈上,我们可以通过以下方式增加亮点:在技术栈中,我们可以添加熟悉模板方法模式、能制定算法骨架抽象相似业务流程等内容。在项目中,我们可以使用设计模式-模板方法+泛型,将业务流程的执行流收敛到模板方法中,并将特殊流程延迟到子类实现。这样,我们可以在减少大量代码的同时,实现一份代码适配多个相似的业务流程。
跟着 Guava 学 Java 之 集合工具类
跟着 Guava 学 Java 之 集合工具类
先来回顾一下 JDK 的layer7 源码Collections,java.util.Collections 提供了一系列静态方法,能更方便地操作各种集合。
Guava 沿着 Collections 的思路,提供了更多工具方法,适用于所有集合的静态方法,使之成为更强大的集合工具类。
Guava 提供的集合工具不止是对 Collections 的扩展和增强,还包括了其他集合类型的工具类。我们把工具类与特定集合接口的对应关系归纳如下:
静态构造器
在 JDK 7 之前,构造新的范型集合时要重复声明范型。JDK 7 以后,因为有了钻石操作符(Diamond Operator),可以自动推断参数类型,所以省事儿。用 Guava 可以这样写,你可能觉得这没什么牛的,跟 JDK7 以后没啥区别,人家还是原生的。
是的,没错,尤其是你用 JDK 9 以后的版本,JDK 从功能上跟 Guava 就基本一样了。比如带元素初始化:
上面这行是利用了 Guava 的 Lists,JDK 7 没有比这行代码更好的方法,但 JDK9 人家有,比如:
所以我们说,跟着 Guava 学 Java,随着版本的迭代,你觉得哪个好用,哪个适合你用哪个,重要的是把这里面的知识点讲清楚。
我们再来看个例子:创建集合时指定初始化集合大小。
你可能说,哥们,这 JDK 有啊,这不多此一举吗?是的,作用一样,但 Guava 的做法,可以利用工厂方法名称,提高代码的可读性,你不觉得虽然名字长了,但可读性比 JDK 那个好很多吗?代码不止是写给机器的,也是写给人看的,不能指望团队里所有人都是强者。代码可读性越高,越健壮越容易维护。
Iterables 类包含了一系列的静态方法,来操作或返回 Iterable 对象。看几个常用的方法:
对于上面这些常用的方法,可能你觉得使用 JDK8 以后的 Stream 一行也都搞定了,是的,还是那句话,Guava 是个工具,尤其在 JDK8 之前用来增强 API 很好用,但工具不止一个,Java 也在发展,有些东西就变成可选项了,看你的需求和习惯使用。Guava 也有对应的流式风格的工具类,比如 FluentIterable。
Lists 除了静态工厂方法和函数式编程方法,为 List 类型的对象提供了若干工具方法。
Sets 工具类包含了若干好用的方法。Sets 为我们提供了很多标准的集合运算(Set-Theoretic)方法,比如我们常用的集合的 “交、并、差集” 以及对称差集。
注意返回的都是 SetView,它可以:
笛卡儿积
Maps 有若干很酷的方法。
uniqueIndex 有一组对象,它们在某个属性上分别有独一无二的值,而我们希望能够按照这个属性值查找对象。
比方说,我们有一堆字符串,这些字符串的长度都是独一无二的,而我们希望能够按照特定长度查找字符串:
你可以想到了,我们常见的场景还有把一个 List 的对象集合转成 Map,map 的 key 通常是对象的 ID。用 uniqueIndex 方法可以这样写:
或者你用 Java8 的 Stream 也一样:
注意:key 要是唯一的,否则会报错。
difference 找不同,对比两个 map,告诉你哪里不同。
Maps.difference(Map, Map)用来比较两个 Map 以获取所有不同点。该方法返回 MapDifference 对象。
下面是 MapDifference 的一些方法:
看到这个你能想到什么?我举个场景:审计日志或操作日志,谁什么时间做了什么,数据从旧值变更为新值,这些要记录下来。是不是可以用上面这个 Maps 的方法?适合不适合你自己决定,这里是提供个思路。
“下面要介绍的工具类都是新集合类型的工具类,比如 MultiSet 和 MultiMap 之类的,有关这些 Guava 的新集合类型,在之前的文章《跟着 Guava 学 Java 之 新集合类型》都有介绍,有不清楚的可以再翻回去看一看。”
标准的 Collection 操作会忽略 Multiset 重复元素的个数,而只关心元素是否存在于 Multiset 中,如 containsAll 方法。为此,Multisets 提供了若干方法,以顾及 Multiset 元素的重复性:
举例来说:
Multisets 中的其他工具方法还包括:
Multimaps.index “Multimaps 的 index 方法跟前面介绍的 Maps.uniqueIndex 方法是兄弟方法。与 uniqueIndex 方法相反,通常针对的场景是:有一组对象,它们有共同的特定属性,我们希望按照这个属性的值查询对象,但属性值不一定是独一无二的。比方说,我们想把字符串按长度分组:”
invertFrom Multimap 可以把多个键映射到同一个值,也可以把一个键映射到多个值,反转 Multimap 也会很有用。Guava 提供了 invertFrom(Multimap toInvert, Multimap dest)做这个操作,并且你可以自由选择反转后的 Multimap 实现。
forMap 想在 Map 对象上使用 Multimap 的方法吗?forMap(Map)把 Map 包装成 SetMultimap。这个方法特别有用,例如,与 Multimaps.invertFrom 结合使用,可以把多对一的 Map 反转为一对多的 Multimap。
如何实现定时任务- Java Timer/TimerTask 源码解析
日常实现各种服务端系统时,我们一定会有一些定时任务的需求。比如会议提前半小时自动提醒,异步任务定时/周期执行等。那么如何去实现这样的一个定时任务系统呢? Java JDK提供的Timer类就是一个很好的工具,通过简单的API调用,我们就可以实现定时任务。
现在就来看一下java.util.Timer是如何实现这样的定时功能的。
首先,我们来看一下一个使用demo
基本的使用方法:
加入任务的API如下:
可以看到API方法内部都是调用sched方法,其中time参数下一次任务执行时间点,是通过计算得到。period参数为0的话则表示为一次性任务。
那么我们来看一下Timer内部是如何实现调度的。
内部结构
先看一下Timer的组成部分:
Timer有3个重要的模块,分别是 TimerTask, TaskQueue, TimerThread
那么,在加入任务之后,整个Timer是怎么样运行的呢?可以看下面的示意图:
图中所示是简化的逻辑,多个任务加入到TaskQueue中,会自动排序,队首任务一定是当前执行时间最早的任务。TimerThread会有一个一直执行的循环,从TaskQueue取队首任务,判断当前时间是否已经到了任务执行时间点,如果是则执行任务。
工作线程
流程中加了一些锁,用来避免同时加入TimerTask的并发问题。可以看到sched方法的逻辑比较简单,task赋值之后入队,队列会自动按照nextExecutionTime排序(升序,排序的实现原理后面会提到)。
从mainLoop的源码中可以看出,基本的流程如下所示
当发现是周期任务时,会计算下一次任务执行的时间,这个时候有两种计算方式,即前面API中的
优先队列
当从队列中移除任务,或者是修改任务执行时间之后,队列会自动排序。始终保持执行时间最早的任务在队首。 那么这是如何实现的呢?
看一下TaskQueue的源码就清楚了
可以看到其实TaskQueue内部就是基于数组实现了一个最小堆 (balanced binary heap), 堆中元素根据 执行时间nextExecutionTime排序,执行时间最早的任务始终会排在堆顶。这样工作线程每次检查的任务就是当前最早需要执行的任务。堆的初始大小为,有简单的倍增扩容机制。
TimerTask 任务有四种状态:
Timer 还提供了cancel和purge方法
常见应用
Java的Timer广泛被用于实现异步任务系统,在一些开源项目中也很常见, 例如消息队列RocketMQ的 延时消息/消费重试 中的异步逻辑。
上面这段代码是RocketMQ的延时消息投递任务 ScheduleMessageService 的核心逻辑,就是使用了Timer实现的异步定时任务。
不管是实现简单的异步逻辑,还是构建复杂的任务系统,Java的Timer确实是一个方便实用,而且又稳定的工具类。从Timer的实现原理,我们也可以窥见定时系统的一个基础实现:线程循环 + 优先队列。这对于我们自己去设计相关的系统,也会有一定的启发。