一些工具类库和相关注解的总结

269 阅读17分钟

Lombok&相关注解

lombok是一款在java开发中简洁化代码十分有用的插件工具

使用lombok注解,目的和作用就在于不用再去写经常反复去写的(如Getter,Setter,Constructor等)一些代码了。

简介

  • @Data 使用这个注解,就不用再去手写Getter,Setter,equals,canEqual,hasCode,toString等方法了,注解后在编译时会自动加进去。@Data和@Getter,@Setter的区别,@Data注解包含了@Getter@Setter,且有toString(),equals等方法
  • @AllArgsConstructor 使用后添加一个构造函数,该构造函数含有所有已声明字段属性参数
  • @NoArgsConstructor 使用后创建一个无参构造函数
  • @Builder关于Builder较为复杂一些,Builder的作用之一是为了解决在某个类有很多构造函数的情况,也省去写很多构造函数的麻烦,在设计模式中的思想是:用一个内部类去实例化一个对象,避免一个类出现过多构造函数,Builder 使用创建者模式又叫建造者模式。简单来说,就是一步步创建一个对象,它对用户屏蔽了里面构建的细节,但却可以精细地控制对象的构造过程。

基础使用

@Builder注释为你的类生成相对略微复杂的构建器API。@Builder可以让你以下面显示的那样调用你的代码,来初始化你的实例对象:

 Student.builder()
                .sno( "001" )
                .sname( "admin" )
                .sage( 18 )
                .sphone( "110" )
                .build();

@Builder可以放在类,构造函数或方法上。 虽然放在类上和放在构造函数上这两种模式是最常见的用例,但@Builder最容易用放在方法的用例来解释。

@Builder做了什么

  • 创建一个名为ThisClassBuilder的内部静态类,并具有和实体类形同的属性(称为构建器)。
  • 在构建器中:对于目标类中的所有的属性和未初始化的final字段,都会在构建器中创建对应属性。
  • 在构建器中:创建一个无参的default构造函数。
  • 在构建器中:对于实体类中的每个参数,都会对应创建类似于setter的方法,只不过方法名与该参数名相同。 并且返回值是构建器本身(便于链式调用),如上例所示。
  • 在构建器中:一个build()方法,调用此方法,就会根据设置的值进行创建实体对象。
  • 在构建器中:同时也会生成一个toString()方法。
  • 在实体类中:会创建一个builder()方法,它的目的是用来创建构建器。

实例

 @Builder
 public class User {
     private final Integer code = 200;
     private String username;
     private String password;
 }
 ​
 // 编译后:
 public class User {
     private String username;
     private String password;
     User(String username, String password) {
         this.username = username; this.password = password;
     }
     public static User.UserBuilder builder() {
         return new User.UserBuilder();
     }
 ​
   
     //ThisClassBuilder 内部静态类
     public static class UserBuilder {
         private String username;
         private String password;
         UserBuilder() {}
 ​
         public User.UserBuilder username(String username) {
             this.username = username;
             return this;
         }
         public User.UserBuilder password(String password) {
             this.password = password;
             return this;
         }
         public User build() {
             return new User(this.username, this.password);
         }
         public String toString() {
             return "User.UserBuilder(username=" + this.username + ", password=" + this.password + ")";
         }
     }
 }
 @Data //生成getter,setter等函数
 @AllArgsConstructor //生成全参数构造函数
 @NoArgsConstructor//生成无参构造函数
 @Builder
 public class test1 {
     String name;
     String age;
     String sex;
 }
 public static void main(String[] args) {
  //使用@Builder注解后,可以直接通过Builder设置字段参数
         test1 t1=new test1.test1Builder()
                 .name("wang")
                 .age("12")
                 .sex("man")
                 .build();
 ​
         System.out.println("name is"+t1.getName()+'\n'+"age is :"+t1.getAge());
 }

通过查看编译后的类,比较注解前后的代码,发现省去了很多代码的书写✍️

 public class test1 {
     String name;
     String age;
     String sex;
 ​
     public static test1.test1Builder builder() {
         return new test1.test1Builder();
     }
 ​
     public String getName() {
         return this.name;
     }
 ​
     public String getAge() {
         return this.age;
     }
 ​
     public String getSex() {
         return this.sex;
     }
 ​
     public void setName(String name) {
         this.name = name;
     }
 ​
     public void setAge(String age) {
         this.age = age;
     }
 ​
     public void setSex(String sex) {
         this.sex = sex;
     }
 ​
     public boolean equals(Object o) {
         if (o == this) {
             return true;
         } else if (!(o instanceof test1)) {
             return false;
         } else {
             test1 other = (test1)o;
             if (!other.canEqual(this)) {
                 return false;
             } else {
                 label47: {
                     Object this$name = this.getName();
                     Object other$name = other.getName();
                     if (this$name == null) {
                         if (other$name == null) {
                             break label47;
                         }
                     } else if (this$name.equals(other$name)) {
                         break label47;
                     }
 ​
                     return false;
                 }
 ​
                 Object this$age = this.getAge();
                 Object other$age = other.getAge();
                 if (this$age == null) {
                     if (other$age != null) {
                         return false;
                     }
                 } else if (!this$age.equals(other$age)) {
                     return false;
                 }
 ​
                 Object this$sex = this.getSex();
                 Object other$sex = other.getSex();
                 if (this$sex == null) {
                     if (other$sex != null) {
                         return false;
                     }
                 } else if (!this$sex.equals(other$sex)) {
                     return false;
                 }
 ​
                 return true;
             }
         }
     }
 ​
     protected boolean canEqual(Object other) {
         return other instanceof test1;
     }
 ​
     public int hashCode() {
         int PRIME = true;
         int result = 1;
         Object $name = this.getName();
         int result = result * 59 + ($name == null ? 43 : $name.hashCode());
         Object $age = this.getAge();
         result = result * 59 + ($age == null ? 43 : $age.hashCode());
         Object $sex = this.getSex();
         result = result * 59 + ($sex == null ? 43 : $sex.hashCode());
         return result;
     }
 ​
     public String toString() {
         return "test1(name=" + this.getName() + ", age=" + this.getAge() + ", sex=" + this.getSex() + ")";
     }
 ​
     @ConstructorProperties({"name", "age", "sex"})
     public test1(String name, String age, String sex) {
         this.name = name;
         this.age = age;
         this.sex = sex;
     }
 ​
     public test1() {
     }
 ​
     public static class test1Builder {
         private String name;
         private String age;
         private String sex;
 ​
         test1Builder() {
         }
 ​
         public test1.test1Builder name(String name) {
             this.name = name;
             return this;
         }
 ​
         public test1.test1Builder age(String age) {
             this.age = age;
             return this;
         }
 ​
         public test1.test1Builder sex(String sex) {
             this.sex = sex;
             return this;
         }
 ​
         public test1 build() {
             return new test1(this.name, this.age, this.sex);
         }
 ​
         public String toString() {
             return "test1.test1Builder(name=" + this.name + ", age=" + this.age + ", sex=" + this.sex + ")";
         }
     }
 }

Swagger

@ApiModel使用场景

在实体类上边使用,标记类时swagger的解析类

提供有关swagger模型的其它信息,类将在操作中用作类型时自动内省

属性

属性名称数据类型默认值说明
valueString类名为模型提供备用名称
descriptionString""提供详细的类描述
parentClass<?> parentVoid.class为模型提供父类以允许描述继承关系
discriminatoryString""支持模型继承和多态,使用鉴别器的字段的名称,可以断言需要使用哪个子类型
subTypesClass<?>[]{}从此模型继承的子类型数组
referenceString""指定对应类型定义的引用,覆盖指定的任何其他元数据

@ApiModelProperty

使用场景使用在被 @ApiModel 注解的模型类的属性上

添加和操作模型属性的数据

属性

属性名称数据类型默认值说明
valueString""属性简要说明
nameString""运行覆盖属性的名称。重写属性名称
allowableValuesString""限制参数可接收的值,三种方法,固定取值,固定范围
accessString""过滤属性,参阅:io.swagger.core.filter.SwaggerSpecFilter
notesString""目前尚未使用
dataTypeString""参数的数据类型,可以是类名或原始数据类型,此值将覆盖从类属性读取的数据类型
requiredbooleanBoolean是否为必传参数, false:非必传参数; true:必传参数
positionInt0允许在模型中显示排序属性
hiddenbooleanFalse隐藏模型属性,false:不隐藏; true:隐藏
exampleString""属性的示例值
readOnlybooleanFalse指定模型属性为只读,false:非只读; true:只读
referenceString""指定对应类型定义的引用,覆盖指定的任何其他元数据
allowEmptyValuebooleanFalse允许传空值,false:不允许传空值; true:允许传空值""

优雅的校验参数-javax.validation

很痛苦遇到大量的参数进行校验,在业务中还要抛出异常或者 不断的返回异常时的校验信息,在代码中相当冗长, 充满了if-else这种校验代码,spring的javax.validation 注解式参数校验可以很优雅的解决.

JSR303 是一套JavaBean参数校验的标准,它定义了很多常用的校验注解,我们可以直接将这些注解加在我们JavaBean的属性上面(面向注解编程的时代),就可以在需要校验的时候进行校验了,在SpringBoot中已经包含在starter-web中,再其他项目中可以引用依赖,并自行调整版本:

         <!--jsr 303-->
         <dependency>
             <groupId>javax.validation</groupId>
             <artifactId>validation-api</artifactId>
             <version>1.1.0.Final</version>
         </dependency>
         <!-- hibernate validator-->
         <dependency>
             <groupId>org.hibernate</groupId>
             <artifactId>hibernate-validator</artifactId>
             <version>5.2.0.Final</version>
         </dependency>
验证注解验证的数据类型说明
@NotNull任意类型验证注解的元素值不是null
@Null任意类型验证注解值是null
@AssertFalseBoolean、boolean验证注解的元素是false
@NotBlankCharSequence子类型验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的首位空格
@Email(regexp=正则表达式,flag=标志的模式)CharSequence子类型(如String)验证注解的元素值是Email,也可以通过regexp和flag指定自定义的email格式

Hutool 工具不糊涂

Hutool是项目中 util 包的友好替代,覆盖了 Java 开发底层的方方面面,既是大型项目中解决小问题的利器,也是小项目中的效率担当,它能让你专注业务,极大地提升开发效率,可以最大程度地避免自行封装出现的小问题、小 Bug。所以,学会如何高效、准确地使用项目开发过程中所需的工具方法非常有必要,今天结合使用场景给大家分享一些实用的工具类方法,没讲清楚以及未涉及之处,望大家见谅,具体详情可参见 Hutool 官网 (hutool.cn)

工具引入

Hutool 包含对文件、流、加密解密、转码、正则、线程、XML 等 JDK 方法进行封装,放在不同的模块组件当中,当你需要对 Excel 进行操作时,你可以单独引入 hutool-poi 模块,当然分不清个模块之间功能,图省事,也可以通过引入 hutool-all 方式引入所有模块。

Maven

 <dependency>
     <groupId>cn.hutool</groupId>
     <artifactId>hutool-all</artifactId>
     <version>5.7.16</version>
 </dependency>

Gradle

 implementation 'cn.hutool:hutool-all:5.7.16'

工具使用

Console 对象信息打印类

Hutool 中 的 Console 对象借鉴的就是 JS 中的语法糖。虽说是打印,和 System.out.println() / System.err.println() 还是有所不同的,最重要的是它支持 Slf4j 的字符串模版语法,会自动将对象(包括数组)转为字符串形式。

 ​
 Console.log("this is array: {}", new String[]{"Java", "JavaScript", "Python"});
 ​
 Console.error("I'm a red error message.");

这里的 {} 作为模版占位符,能够将逗号右边的变量值依次传入,从而以字符串的形式打印输出。

Convert 不同类型之间转换类

该类中封装了针对 Java 常见类型的转换,用于简化类型转换,同时对转换失败的异常捕获有着很好的封装,能够帮你减轻业务代码的臃肿,提升代码的优雅性,该工具类还是非常常用和实用的,必须安利一波୧(๑•̀◡•́๑)૭

 ​
 // 转换为字符串
 int number = 10;
 Console.log("转换成字符串后:{}", Convert.toStr(number));
 ​
 int[] arr = {1, 2, 3, 4, 5};
 Console.log(Convert.toStr(arr, "Convert to string fail! This is default value."));
 ​
 // 转换为指定类型的数组
 String[] letters = {"12", "122", "1223", "1211"};
 Console.log(Convert.toShortArray(letters));
 Console.log(Convert.toStrArray(arr));
 ​
 float[] floatArr = {1.2F, 1.3F, 2.0F, 3.14f};
 Integer[] integerArr = Convert.toIntArray(floatArr);
 Console.log(integerArr); // [1, 1, 2, 3]
 ​
 // 转换为日期对象
 String strDate1 = "2021-10-11 12:03:29";
 // toDate
 Console.log("The string of data convert to Date: {}", Convert.toDate(strDate1)); // 2021-10-11 12:03:29
 // toLocalDateTime
 Console.log("The string of data convert to LocalDateTime: {}", Convert.toLocalDateTime(strDate1)); // 2021-10-11T12:03:29
 ​
 // 转换成集合
 String[] langs = {"Java", "JavaScript", "Python", "C++", "GoLang", "TypeScript", "Kotlin"};
 ​
 // 通过 Convert.convert(Class<T>, Object) 方法可以将任意类型转换为指定类型
 ArrayList arrayList = Convert.convert(ArrayList.class, langs);
 // 也可以这样转集合
 List<String> langList = Convert.toList(String.class, langs);
 Console.log("String array is converted to ArrayList: {}", arrayList);
 Console.log("String array is converted to List: {}", langList);
 // 转成指定类型的集合
 Object[] objArr = {"a", "你", "好", "", 1};
 // TypeReference 对象可以对嵌套泛型进行类型转换
 List<String> strList = Convert.convert(new TypeReference<List<String>>() {
 }, objArr);
 Console.log("使用 TypeReference 对象可以对嵌套泛型进行类型转换:{}", strList);

字符串转为 16 进制(Hex)和 Unicode

 ​
 // 字符串转为 16 进制(Hex)
 String desc = "大家好,我的名字叫 HUALEI !";
 // 因为字符串牵涉到编码问题,因此必须传入编码对象
 String hexStr = Convert.toHex(desc, CharsetUtil.CHARSET_UTF_8);
 Console.log("将字符串转为 Hex 字符串:{}", hexStr); // e5a4a7e5aeb6e5a5bdefbc8ce68891e79a84e5908de5ad97e58fab204855414c454920efbc81
 ​
 // Hex 字符串转换回去
 String originStr = Convert.hexToStr(hexStr, CharsetUtil.CHARSET_UTF_8);
 Console.log("将 Hex 字符串转换回原先字符串:{}", originStr);
 ​
 /*
     如果把各种文字编码形容为各地的方言,那么 Unicode 就是世界各国合作开发的一种语言。
 ​
     字符串转为 Unicode(统一码 / 万国码 / 单一码),它是为了解决传统字符编码方案弊端(繁杂不统一,导致编码格式不一致引发的乱码问题)
 ​
         Unicode 为每种语言中的每个字符设定了 统一 并且 唯一 的 二进制编码
         以满足 跨语言、跨平台 进行 文本转换、处理 的需求
 */
 // 将字符串转换成 Unicode
 String unicodeStr = Convert.strToUnicode(desc);
 Console.log("字符串转换成 Unicode 串:{}", unicodeStr); // \u5927\u5bb6\u597d\uff0c\u6211\u7684\u540d\u5b57\u53eb HUALEI \uff01

字符编码方式转换(编码与解码)

 ​
 String a = "我不是乱码";
 // 使用 UTF8 字符编码解码为 ISO_8859_1 编码方式的字符串
 String mess = Convert.convertCharset(a, CharsetUtil.UTF_8, CharsetUtil.ISO_8859_1);
 Console.log("ISO_8859_1 编码方式的中文乱码:{}", mess); // 转换后 mess 为乱码 => 敕丕敯乱砕
 ​
 // 使用 ISO_8859_1 字符编码解码为原先的 UTF8 编码方式的字符串,将乱码转为正确的编码方式:
 String raw = Convert.convertCharset(mess, CharsetUtil.ISO_8859_1, "UTF-8");
 Console.log("将乱码转为正确的编码后:{}", raw); // 我不是乱码

中文大/小写数字、金额相关转换

 ​
 // 金额转中文表达(最多保留到小数点后两位)
 double money = 18.88D;
 String chineseWord = Convert.digitToChinese(money);
 Console.log("金额转中文表达:{}", chineseWord); // 壹拾捌元捌角捌分
 ​
 // 金额转英文表达(最多保留到小数点后两位)
 String englishWord = Convert.numberToWord(money);
 Console.log("金额转英文表达:{}", englishWord); // EIGHTEEN AND CENTS EIGHTY EIGHT ONLY
 ​
 double amount = 102389942.32D;
 // 数字转中文大写(例:壹仟,最多保留到小数点后两位) isUseTraditional => true
 String traditionalChinese = Convert.numberToChinese(amount, true);
 // 数字转中文小写(例:一千) isUseTraditional => false
 String nonTraditionalChinese = Convert.numberToChinese(amount, false);
 Console.log("数字转中文大写,传统:{};非传统:{}", traditionalChinese, nonTraditionalChinese); // 传统:壹亿零贰佰叁拾捌万玖仟玖佰肆拾贰点叁贰;非传统:一亿零二百三十八万九千九百四十二点三二
 ​
 // 中文大写壹仟叁佰转成纯数字
 Console.log("中文大写壹仟叁佰转成数字:{}", Convert.chineseToNumber("壹仟叁佰")); // 1300
 ​
 // 数字简化 1000 => 1k; 10000 => 1w(最多保留到小数点后两位)
 String simple = Convert.numberToSimple(amount);
 Console.log("{} 简化为:{}", amount, simple); // 10238.99w

convertTime(目标值,目标值时间单位,转换后的时间单位) 方法主要用于转换时长单位,比如一个很大的毫秒,我想获得这个毫秒数换算成多少天:

 ​
 // 毫秒数
 long ms = 100000000L;
 long msToDays = Convert.convertTime(ms, TimeUnit.MILLISECONDS, TimeUnit.DAYS);
 Console.log("{} 毫秒约等于 {} 天", ms, msToDays); // 100000000 毫秒约等于 1 天

原始类型和包装类型之间的转换

 ​
 // Integer 包装类
 Class<Integer> wrapClass = Integer.class;
 ​
 // Integer 去除包装类 => int
 Class<?> unWraped = Convert.unWrap(wrapClass); // int
 Console.log("Integer 包装类去包装化:{}", unWraped);
 ​
 // 原始类
 Class<?> primitiveClass = long.class;
 ​
 // 将原始类型转换成包装类
 Class<?> wraped = Convert.wrap(primitiveClass); // class java.lang.Long
 Console.log("long 原始类包装化:{}", wraped);

DateUtil DateTime 日期时间工具类

对于 java.util.Date 对象是不是还停留在被 Thu Nov dd HH:mm:ss CST yyyy 支配的恐惧中?数字英文单词缩写的混杂让人看起来非常不舒服,如果你想将其转换成 yyyy-MM-dd / yyyy-MM-dd HH:mm:ss 时间格式字符串,你是不是得先 new 一个 SimpleDateFormat,通过 pattern 参数进行初始化格式化器,时间格式不熟练的可能还要百度一下,甚是麻烦。

为了便捷,Hutool 工具使用了一个 DateTime 类来替代之,继承自 Date ,重写了 toString() 方法,直接放回 yyyy-MM-dd HH:mm:ss 形式的字符串,方便在输出时的调用(例如日志记录等),提供了众多便捷的方法对日期对象操作。

 ​
 // 计时器,计算被其包裹的代码片段执行时间
 final TimeInterval timeInterval = DateUtil.timer();
 ​
 // 根据 birthday 计算现在的年龄
 String dateStr = "2000年05月26日";
 Date birthday = Convert.toDate(dateStr);
 // 可以通过 DatePattern 指定 dateStr 日期时间格式进行解析,如果没有第二个参数,则会自动寻找合适的格式(支持多种格式)进行解析
 // DateTime dateTime = new DateTime(dateStr, DatePattern.CHINESE_DATE_PATTERN);
 int age = DateUtil.ageOfNow(birthday); // 或 DateUtil.ageOfNow(dateTime);
 Console.log("{} 出生的小伙子,如今已经 {} 岁了!", dateStr, age);
 ​
 // 获取当前时间,格式:yyyy-MM-dd HH:mm:ss => DateTime 类型
 Date currentDate = DateUtil.date(); // 2021-11-02 08:57:54
 // 等价于 DateTime currentDateTime = new DateTime();
 // 不同于 new Date() => Tue Nov 02 08:57:54 CST 2021
 Console.log("DateUtil.date() 得到当前时间(yyyy-MM-dd HH:mm:ss):", currentDate);
 ​
 // 将特殊的日期字符串转换为 DateTime 对象
 String strDate2 = "2021年11月02日 12时03分29秒";
 // 根据格式化模版将字符串日期转换为 Date 对象
 Console.log("Use DateUtil.parse() method convert to Date: {}", DateUtil.parse(strDate2, "yyyy年MM月dd日 HH时mm分ss秒")); // 2021-11-02 12:03:29
 // 等价于 new DateTime(strDate2);
 ​
 // 方式一:当前毫秒数转换为 DateTime 时间,格式:yyyy-MM-dd HH:mm:ss
 Date msToDateTime1 = DateUtil.date(Calendar.getInstance()); // 2021-11-02 08:57:54
 Console.log("当前毫秒数转换为 DateTime 时间,方式一:", msToDateTime1);
 ​
 // 方式二:当前毫秒数转换成 DateTime 时间,格式:yyyy-MM-dd HH:mm:ss
 Date msToDateTime2 = DateUtil.date(System.currentTimeMillis()); // 2021-11-02 08:57:54
 Console.log("当前毫秒数转换为 DateTime 时间,方式二:", msToDateTime2);
 ​
 // 当前日期字符串,格式:yyyy-MM-dd
 String today= DateUtil.today(); // 2021-11-02
 Console.log("当前日期字符串,格式:yyyy-MM-dd:{}", today);
 ​
 // 将 Date 对象格式化为日期字符串,格式:yyyy-MM-dd
 String formatDateStr = DateUtil.format(new Date(), "yyyy-MM-dd");
 Console.log("Use DateUtil.format() method convert to Date: {}", formatDateStr);
 ​
 // 获取日期时间(DateTime)的 Time 字符串,格式:HH:mm:ss
 String formatTimeStr = DateUtil.formatTime(new DateTime());
 Console.log("Use DateUtil.formatTime() method only covert Date to HH:mm:ss:{}", formatTimeStr);
 ​
 // 星座(zodiac sign)
 String zodiac = DateUtil.getZodiac(DateUtil.month(birthday), ((int) DateUtil.betweenDay(DateUtil.beginOfMonth(birthday), birthday, false)));
 // 属相(chinese zodiac)
 String chineseZodiac = DateUtil.getChineseZodiac(DateUtil.year(birthday));
 Console.log("{} 生日的人,星座是:{},属 {} 的。", birthday, zodiac, chineseZodiac);
 ​
 // 判断今年是不是平闰年
 boolean isLeapYear = DateUtil.isLeapYear(DateUtil.year(new Date()));
 Console.log("{}年是闰年吗?{}", DateUtil.year(new Date()), isLeapYear);
 ​
 // 得到一个美化过后的花费时间,即加上中文单位
 String spendTimePretty = timeInterval.intervalPretty();
 // 运行时间转换为秒为单位
 long sec = timeInterval.intervalSecond();
 ​
 // 返回花费时间,并重置开始时间
 long spendTimeAndRestart = timeInterval.intervalRestart();
 ​
 Console.log("测试运行时间(无单位不美化):", spendTimeAndRestart);
 Console.log("测试运行时间(有单位带美化):", spendTimePretty);
 Console.log("测试运行时间,转换为秒:{}", sec);

获取指定天/周/月/季度/年的开始/结束时间,返回 DateTime,格式为:yyyy-MM-dd HH:mm:ss

 ​
 DateTime beginOfDayTime = DateUtil.beginOfDay(new Date());
 Console.log("今天开始时间:{}", beginOfDayTime); // 2021-11-03 00:00:00
 ​
 DateTime beginOfYearTime = DateUtil.beginOfYear(new Date());
 Console.log("今年开始时间:{}", beginOfYearTime); // 2021-01-01 00:00:00
 ​
 DateTime endOfYearTime = DateUtil.endOfYear(new Date());
 Console.log("今年结束时间:{}", endOfYearTime); // 2021-12-31 23:59:59

日期时间偏移:即对某个日期增减分、小时、天等等

 ​
 // 将当前时间增加半个小时
 DateTime halfHourLater = DateUtil.offset(currentDate, DateField.MINUTE, 30);
 // 等同于:DateUtil.offsetHour(currentDate, 30);
 Console.log("当前时间是 {},半个小时后是:{}", currentDate, halfHourLater);
 // 五天后的现在时间
 DateTime fiveDaysFromNow = DateUtil.offsetDay(currentDate, 5);
 ​
 Console.log("当前时间是 {},五天后是:{}", currentDate, fiveDaysFromNow);
 // 针对当前时间,提供了更为简化的偏移方法
 Console.log("昨天的现在是:{}", DateUtil.yesterday());
 Console.log("明天的现在是:{}", DateUtil.tomorrow());
 Console.log("上周天的现在是:{}", DateUtil.lastWeek());
 Console.log("下周的现在是:{}", DateUtil.nextWeek());
 Console.log("上个月的现在是:{}", DateUtil.lastMonth());
 Console.log("下个月的现在是:{}", DateUtil.nextMonth());

计算两个时间的间隔

 ​
 // 当前时间
 DateTime currentDateTime = new DateTime();
 ​
 String dateTimeStrStart = "1949/10/01";
 Date startTime = Convert.toDate(dateTimeStrStart);
 long betweenDays = DateUtil.between(startTime, new DateTime(), DateUnit.DAY);
 Console.log("距离新中国成立,已经过了 {} 天", betweenDays);
 ​
 // 下班时间(当天 17:00:00 )
 Date getOffTime = DateUtil.offsetHour(DateUtil.beginOfDay(currentDateTime), 17);
 Console.log("下班时间是:{}", getOffTime);
 long distance = DateUtil.between(getOffTime, currentDateTime, DateUnit.SECOND, true);
 ​
 // 间隔时间设置为秒
 Console.log("距离下班还剩 {} 秒", distance);
 // 格式化间隔时间
 Console.log("距离下班还剩 {} 精确到秒", DateUtil.formatBetween(getOffTime, currentDateTime, BetweenFormatter.Level.SECOND));

SpringBoot技术实践-整合Quartz任务调度

任务调度

Quartz是一个定时任务框架,基础核心使用可以参考官网

Quartz源码:github.com/quartz-sche…

Quartz官网地址:www.quartz-scheduler.org/documentati…

项目源码地址:gitee.com/tianxincoor…

1.2 Scheduler

  1. Scheduler为quartz中的调度器,Quartz通过调度器来注册、暂停、删除Trigger和JobDetail
  2. Scheduler拥有SchedulerContext,顾名思义就是上下文,通过SchedulerContext可以获取到触发器和任务的一些信息

1.3 Trigger

  1. Trigger为触发器,通过cron表达式或日历指定任务执行的周期
  2. 系统时间走到触发器指定的时间时,触发器就会触发任务的执行

1.4 JobDetail

  1. Job接口是真正需要执行的任务
  2. JobDetail核心调度实现了Job类的任务类,Trigger和Scheduler实际用到的都是JobDetail

1.5 Job

  1. 完成任务的最小实现类,如果需要被定时调度的类都需要实现此接口

SpringBoot整合

pom

 <?xml version="1.0" encoding="UTF-8"?>
 <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
          xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
     <modelVersion>4.0.0</modelVersion>
     <parent>
         <groupId>org.springframework.boot</groupId>
         <artifactId>spring-boot-starter-parent</artifactId>
         <version>2.2.0.RELEASE</version>
         <relativePath/> <!-- lookup parent from repository -->
     </parent>
     <groupId>com.codecoord</groupId>
     <artifactId>springboot-quartz</artifactId>
     <version>1.0</version>
     <name>springboot-quartz</name>
 ​
     <properties>
         <java.version>1.8</java.version>
         <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
         <maven.compiler.source>1.8</maven.compiler.source>
         <maven.compiler.target>1.8</maven.compiler.target>
     </properties>
 ​
     <dependencies>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-web</artifactId>
         </dependency>
 ​
         <!-- 公共依赖 -->
         <dependency>
             <groupId>com.alibaba</groupId>
             <artifactId>fastjson</artifactId>
             <version>1.2.75</version>
         </dependency>
 ​
         <!-- quartz依赖 -->
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-quartz</artifactId>
         </dependency>
         <dependency>
             <groupId>mysql</groupId>
             <artifactId>mysql-connector-java</artifactId>
             <version>6.0.6</version>
         </dependency>
         <dependency>
             <groupId>org.springframework.boot</groupId>
             <artifactId>spring-boot-starter-jdbc</artifactId>
         </dependency>
     </dependencies>
 ​
     <build>
         <plugins>
             <plugin>
                 <groupId>org.springframework.boot</groupId>
                 <artifactId>spring-boot-maven-plugin</artifactId>
             </plugin>
         </plugins>
     </build>
 </project>
  1. 配置数据源相关链接

    • quartz需要单据的数据库,所以需要单据创建一个库来给quartz使用,此处为quartz_config
    • 其他相关配置可以参考官网:官网配置参考
 server:
   port: 8888
 spring:
   datasource:
     driver-class-name: com.mysql.cj.jdbc.Driver
     url: jdbc:mysql://127.0.0.1:3306/source?useSSL=false
     username: root
     password: tianxin
   # 定时配置
   quartz:
     # 相关属性配置
     properties:
       org:
         quartz:
           # 数据源
           dataSource:
             globalJobDataSource:
               # URL必须大写
               URL: jdbc:mysql://127.0.0.1:3306/quartz_config?useUnicode=true&characterEncoding=utf-8&useSSL=false
               driver: com.mysql.cj.jdbc.Driver
               maxConnections: 5
               username: root
               password: tianxin
               # 必须指定数据源类型
               provider: hikaricp
           scheduler:
             instanceName: globalScheduler
             # 实例id
             #instanceId: AUTO
             type: com.alibaba.druid.pool.DruidDataSource
           jobStore:
             # 数据源
             dataSource: globalJobDataSource
             # JobStoreTX将用于独立环境,提交和回滚都将由这个类处理
             class: org.quartz.impl.jdbcjobstore.JobStoreTX
             # 驱动配置
             driverDelegateClass: org.quartz.impl.jdbcjobstore.StdJDBCDelegate
             # 表前缀
             tablePrefix: QRTZ_
           # 线程池配置
           threadPool:
             class: org.quartz.simpl.SimpleThreadPool
             # 线程数
             threadCount: 10
             # 优先级
             threadPriority: 5
 

原文参考1

原文参考2

原文参考3

原文参考4(优雅的校验参数-javax.validation)

Hutool工具不糊涂

Hutool API

任务调度参考