loader = LazyFunLoader.on(BigObject::new);
+
+ // 若从未使用,则可以避免不必要的初始化
+ loader.ifInitialized(it -> {
+
+ Assert.fail();
+ it.destroy();
+ });
+
+ }
}
diff --git a/hutool-cron/src/main/java/cn/hutool/cron/pattern/CronPattern.java b/hutool-cron/src/main/java/cn/hutool/cron/pattern/CronPattern.java
index b026cdd3c..ce05317de 100644
--- a/hutool-cron/src/main/java/cn/hutool/cron/pattern/CronPattern.java
+++ b/hutool-cron/src/main/java/cn/hutool/cron/pattern/CronPattern.java
@@ -1,25 +1,10 @@
package cn.hutool.cron.pattern;
-import cn.hutool.core.date.DateUtil;
-import cn.hutool.core.util.StrUtil;
-import cn.hutool.cron.CronException;
-import cn.hutool.cron.pattern.matcher.AlwaysTrueValueMatcher;
-import cn.hutool.cron.pattern.matcher.DayOfMonthValueMatcher;
-import cn.hutool.cron.pattern.matcher.ValueMatcher;
-import cn.hutool.cron.pattern.matcher.ValueMatcherBuilder;
-import cn.hutool.cron.pattern.parser.DayOfMonthValueParser;
-import cn.hutool.cron.pattern.parser.DayOfWeekValueParser;
-import cn.hutool.cron.pattern.parser.HourValueParser;
-import cn.hutool.cron.pattern.parser.MinuteValueParser;
-import cn.hutool.cron.pattern.parser.MonthValueParser;
-import cn.hutool.cron.pattern.parser.SecondValueParser;
-import cn.hutool.cron.pattern.parser.ValueParser;
-import cn.hutool.cron.pattern.parser.YearValueParser;
+import cn.hutool.cron.pattern.matcher.MatcherTable;
+import cn.hutool.cron.pattern.parser.CronPatternParser;
-import java.util.ArrayList;
import java.util.Calendar;
import java.util.GregorianCalendar;
-import java.util.List;
import java.util.TimeZone;
/**
@@ -32,14 +17,14 @@ import java.util.TimeZone;
* 月 :范围:1~12,同时支持不区分大小写的别名:"jan","feb", "mar", "apr", "may","jun", "jul", "aug", "sep","oct", "nov", "dec"
* 周 :范围:0 (Sunday)~6(Saturday),7也可以表示周日,同时支持不区分大小写的别名:"sun","mon", "tue", "wed", "thu","fri", "sat","L" 表示周六
*
- *
+ *
* 为了兼容Quartz表达式,同时支持6位和7位表达式,其中:
*
*
* 当为6位时,第一位表示秒 ,范围0~59,但是第一位不做匹配
* 当为7位时,最后一位表示年 ,范围1970~2099,但是第7位不做解析,也不做匹配
*
- *
+ *
* 当定时任务运行到的时间匹配这些表达式后,任务被启动。
* 注意:
*
@@ -47,7 +32,7 @@ import java.util.TimeZone;
* 当isMatchSecond为{@code true}时才会匹配秒部分
* 默认都是关闭的
*
- *
+ *
* 对于每一个子表达式,同样支持以下形式:
*
* - * :表示匹配这个位置所有的时间
@@ -62,10 +47,10 @@ import java.util.TimeZone;
*
* 间隔(/) > 区间(-) > 列表(,)
*
- *
+ *
* 例如 2,3,6/3中,由于“/”优先级高,因此相当于2,3,(6/3),结果与 2,3,6等价
*
- *
+ *
* 一些例子:
*
* - 5 * * * * :每个点钟的5分执行,00:05,01:05……
@@ -77,36 +62,11 @@ import java.util.TimeZone;
*
*
* @author Looly
- *
*/
public class CronPattern {
- private static final ValueParser SECOND_VALUE_PARSER = new SecondValueParser();
- private static final ValueParser MINUTE_VALUE_PARSER = new MinuteValueParser();
- private static final ValueParser HOUR_VALUE_PARSER = new HourValueParser();
- private static final ValueParser DAY_OF_MONTH_VALUE_PARSER = new DayOfMonthValueParser();
- private static final ValueParser MONTH_VALUE_PARSER = new MonthValueParser();
- private static final ValueParser DAY_OF_WEEK_VALUE_PARSER = new DayOfWeekValueParser();
- private static final ValueParser YEAR_VALUE_PARSER = new YearValueParser();
-
private final String pattern;
-
- /** 秒字段匹配列表 */
- private final List secondMatchers = new ArrayList<>();
- /** 分字段匹配列表 */
- private final List minuteMatchers = new ArrayList<>();
- /** 时字段匹配列表 */
- private final List hourMatchers = new ArrayList<>();
- /** 每月几号字段匹配列表 */
- private final List dayOfMonthMatchers = new ArrayList<>();
- /** 月字段匹配列表 */
- private final List monthMatchers = new ArrayList<>();
- /** 星期字段匹配列表 */
- private final List dayOfWeekMatchers = new ArrayList<>();
- /** 年字段匹配列表 */
- private final List yearMatchers = new ArrayList<>();
- /** 匹配器个数,取决于复合任务表达式中的单一表达式个数 */
- private int matcherSize;
+ private final MatcherTable matcherTable;
/**
* 构造
@@ -115,14 +75,15 @@ public class CronPattern {
*/
public CronPattern(String pattern) {
this.pattern = pattern;
- parseGroupPattern(pattern);
+ this.matcherTable = CronPatternParser.create().parse(pattern);
}
// --------------------------------------------------------------------------------------- match start
+
/**
* 给定时间是否匹配定时任务表达式
*
- * @param millis 时间毫秒数
+ * @param millis 时间毫秒数
* @param isMatchSecond 是否匹配秒
* @return 如果匹配返回 {@code true}, 否则返回 {@code false}
*/
@@ -133,8 +94,8 @@ public class CronPattern {
/**
* 给定时间是否匹配定时任务表达式
*
- * @param timezone 时区 {@link TimeZone}
- * @param millis 时间毫秒数
+ * @param timezone 时区 {@link TimeZone}
+ * @param millis 时间毫秒数
* @param isMatchSecond 是否匹配秒
* @return 如果匹配返回 {@code true}, 否则返回 {@code false}
*/
@@ -147,12 +108,12 @@ public class CronPattern {
/**
* 给定时间是否匹配定时任务表达式
*
- * @param calendar 时间
+ * @param calendar 时间
* @param isMatchSecond 是否匹配秒
* @return 如果匹配返回 {@code true}, 否则返回 {@code false}
*/
public boolean match(GregorianCalendar calendar, boolean isMatchSecond) {
- final int second = calendar.get(Calendar.SECOND);
+ final int second = isMatchSecond ? calendar.get(Calendar.SECOND) : -1;
final int minute = calendar.get(Calendar.MINUTE);
final int hour = calendar.get(Calendar.HOUR_OF_DAY);
final int dayOfMonth = calendar.get(Calendar.DAY_OF_MONTH);
@@ -160,20 +121,7 @@ public class CronPattern {
final int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK) - 1; // 星期从0开始,0和7都表示周日
final int year = calendar.get(Calendar.YEAR);
- boolean eval;
- for (int i = 0; i < matcherSize; i++) {
- eval = ((false == isMatchSecond) || secondMatchers.get(i).match(second)) // 匹配秒(非秒匹配模式下始终返回true)
- && minuteMatchers.get(i).match(minute)// 匹配分
- && hourMatchers.get(i).match(hour)// 匹配时
- && isMatchDayOfMonth(dayOfMonthMatchers.get(i), dayOfMonth, month, calendar.isLeapYear(year))// 匹配日
- && monthMatchers.get(i).match(month) // 匹配月
- && dayOfWeekMatchers.get(i).match(dayOfWeek)// 匹配周
- && isMatch(yearMatchers, i, year);// 匹配年
- if (eval) {
- return true;
- }
- }
- return false;
+ return this.matcherTable.match(second, minute, hour, dayOfMonth, month, dayOfWeek, year);
}
// --------------------------------------------------------------------------------------- match end
@@ -181,114 +129,4 @@ public class CronPattern {
public String toString() {
return this.pattern;
}
-
- // -------------------------------------------------------------------------------------- Private method start
- /**
- * 是否匹配日(指定月份的第几天)
- *
- * @param matcher {@link ValueMatcher}
- * @param dayOfMonth 日
- * @param month 月
- * @param isLeapYear 是否闰年
- * @return 是否匹配
- */
- private static boolean isMatchDayOfMonth(ValueMatcher matcher, int dayOfMonth, int month, boolean isLeapYear) {
- return ((matcher instanceof DayOfMonthValueMatcher) //
- ? ((DayOfMonthValueMatcher) matcher).match(dayOfMonth, month, isLeapYear) //
- : matcher.match(dayOfMonth));
- }
-
- /**
- * 是否匹配指定的日期时间位置
- *
- * @param matchers 匹配器列表
- * @param index 位置
- * @param value 被匹配的值
- * @return 是否匹配
- * @since 4.0.2
- */
- private static boolean isMatch(List matchers, int index, int value) {
- return (matchers.size() <= index) || matchers.get(index).match(value);
- }
-
- /**
- * 解析复合任务表达式
- *
- * @param groupPattern 复合表达式
- */
- private void parseGroupPattern(String groupPattern) {
- List patternList = StrUtil.split(groupPattern, '|');
- for (String pattern : patternList) {
- parseSinglePattern(pattern);
- }
- }
-
- /**
- * 解析单一定时任务表达式
- *
- * @param pattern 表达式
- */
- private void parseSinglePattern(String pattern) {
- final String[] parts = pattern.split("\\s");
-
- int offset = 0;// 偏移量用于兼容Quartz表达式,当表达式有6或7项时,第一项为秒
- if (parts.length == 6 || parts.length == 7) {
- offset = 1;
- } else if (parts.length != 5) {
- throw new CronException("Pattern [{}] is invalid, it must be 5-7 parts!", pattern);
- }
-
- // 秒
- if (1 == offset) {// 支持秒的表达式
- try {
- this.secondMatchers.add(ValueMatcherBuilder.build(parts[0], SECOND_VALUE_PARSER));
- } catch (Exception e) {
- throw new CronException(e, "Invalid pattern [{}], parsing 'second' field error!", pattern);
- }
- } else {// 不支持秒的表达式,则第一位按照表达式生成时间的秒数赋值,表示整分匹配
- this.secondMatchers.add(ValueMatcherBuilder.build(String.valueOf(DateUtil.date().second()), SECOND_VALUE_PARSER));
- }
- // 分
- try {
- this.minuteMatchers.add(ValueMatcherBuilder.build(parts[offset], MINUTE_VALUE_PARSER));
- } catch (Exception e) {
- throw new CronException(e, "Invalid pattern [{}], parsing 'minute' field error!", pattern);
- }
- // 小时
- try {
- this.hourMatchers.add(ValueMatcherBuilder.build(parts[1 + offset], HOUR_VALUE_PARSER));
- } catch (Exception e) {
- throw new CronException(e, "Invalid pattern [{}], parsing 'hour' field error!", pattern);
- }
- // 每月第几天
- try {
- this.dayOfMonthMatchers.add(ValueMatcherBuilder.build(parts[2 + offset], DAY_OF_MONTH_VALUE_PARSER));
- } catch (Exception e) {
- throw new CronException(e, "Invalid pattern [{}], parsing 'day of month' field error!", pattern);
- }
- // 月
- try {
- this.monthMatchers.add(ValueMatcherBuilder.build(parts[3 + offset], MONTH_VALUE_PARSER));
- } catch (Exception e) {
- throw new CronException(e, "Invalid pattern [{}], parsing 'month' field error!", pattern);
- }
- // 星期几
- try {
- this.dayOfWeekMatchers.add(ValueMatcherBuilder.build(parts[4 + offset], DAY_OF_WEEK_VALUE_PARSER));
- } catch (Exception e) {
- throw new CronException(e, "Invalid pattern [{}], parsing 'day of week' field error!", pattern);
- }
- // 年
- if (parts.length == 7) {// 支持年的表达式
- try {
- this.yearMatchers.add(ValueMatcherBuilder.build(parts[6], YEAR_VALUE_PARSER));
- } catch (Exception e) {
- throw new CronException(e, "Invalid pattern [{}], parsing 'year' field error!", pattern);
- }
- } else {// 不支持年的表达式,全部匹配
- this.yearMatchers.add(new AlwaysTrueValueMatcher());
- }
- matcherSize++;
- }
- // -------------------------------------------------------------------------------------- Private method end
}
diff --git a/hutool-cron/src/main/java/cn/hutool/cron/pattern/matcher/BoolArrayValueMatcher.java b/hutool-cron/src/main/java/cn/hutool/cron/pattern/matcher/BoolArrayValueMatcher.java
index 4ffff81e1..a95fb3b67 100644
--- a/hutool-cron/src/main/java/cn/hutool/cron/pattern/matcher/BoolArrayValueMatcher.java
+++ b/hutool-cron/src/main/java/cn/hutool/cron/pattern/matcher/BoolArrayValueMatcher.java
@@ -1,5 +1,7 @@
package cn.hutool.cron.pattern.matcher;
+import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.lang.Assert;
import cn.hutool.core.util.StrUtil;
import java.util.Collections;
@@ -7,28 +9,50 @@ import java.util.List;
/**
* 将表达式中的数字值列表转换为Boolean数组,匹配时匹配相应数组位
- * @author Looly
*
+ * @author Looly
*/
-public class BoolArrayValueMatcher implements ValueMatcher{
-
+public class BoolArrayValueMatcher implements ValueMatcher {
+
+ /**
+ * 用户定义此字段的最小值
+ */
+ private final int minValue;
private final boolean[] bValues;
-
+
+ /**
+ * 构造
+ *
+ * @param intValueList 匹配值列表
+ */
public BoolArrayValueMatcher(List intValueList) {
+ Assert.isTrue(CollUtil.isNotEmpty(intValueList), "Values must be not empty!");
bValues = new boolean[Collections.max(intValueList) + 1];
+ int min = Integer.MAX_VALUE;
for (Integer value : intValueList) {
+ min = Math.min(min, value);
bValues[value] = true;
}
+ this.minValue = min;
}
@Override
public boolean match(Integer value) {
- if(null == value || value >= bValues.length){
+ if (null == value || value >= bValues.length) {
return false;
}
return bValues[value];
}
-
+
+ /**
+ * 获取表达式定义的最小值
+ *
+ * @return 最小值
+ */
+ public int getMinValue() {
+ return this.minValue;
+ }
+
@Override
public String toString() {
return StrUtil.format("Matcher:{}", new Object[]{this.bValues});
diff --git a/hutool-cron/src/main/java/cn/hutool/cron/pattern/matcher/MatcherTable.java b/hutool-cron/src/main/java/cn/hutool/cron/pattern/matcher/MatcherTable.java
new file mode 100644
index 000000000..9dc20ff02
--- /dev/null
+++ b/hutool-cron/src/main/java/cn/hutool/cron/pattern/matcher/MatcherTable.java
@@ -0,0 +1,119 @@
+package cn.hutool.cron.pattern.matcher;
+
+import java.time.Year;
+import java.util.ArrayList;
+import java.util.List;
+
+/**
+ * 时间匹配表,用于存放定时任务表达式解析后的结构信息
+ *
+ * @author looly
+ * @since 5.8.0
+ */
+public class MatcherTable {
+
+ /**
+ * 匹配器个数,取决于复合任务表达式中的单一表达式个数
+ */
+ public int matcherSize;
+ /**
+ * 秒字段匹配列表
+ */
+ public final List secondMatchers;
+ /**
+ * 分字段匹配列表
+ */
+ public final List minuteMatchers;
+ /**
+ * 时字段匹配列表
+ */
+ public final List hourMatchers;
+ /**
+ * 每月几号字段匹配列表
+ */
+ public final List dayOfMonthMatchers;
+ /**
+ * 月字段匹配列表
+ */
+ public final List monthMatchers;
+ /**
+ * 星期字段匹配列表
+ */
+ public final List dayOfWeekMatchers;
+ /**
+ * 年字段匹配列表
+ */
+ public final List yearMatchers;
+
+ /**
+ * 构造
+ *
+ * @param size 表达式个数,用于表示复合表达式中单个表达式个数
+ */
+ public MatcherTable(int size) {
+ matcherSize = size;
+ secondMatchers = new ArrayList<>(size);
+ minuteMatchers = new ArrayList<>(size);
+ hourMatchers = new ArrayList<>(size);
+ dayOfMonthMatchers = new ArrayList<>(size);
+ monthMatchers = new ArrayList<>(size);
+ dayOfWeekMatchers = new ArrayList<>(size);
+ yearMatchers = new ArrayList<>(size);
+ }
+
+ /**
+ * 给定时间是否匹配定时任务表达式
+ *
+ * @param second 秒数,-1表示不匹配此项
+ * @param minute 分钟
+ * @param hour 小时
+ * @param dayOfMonth 天
+ * @param month 月
+ * @param dayOfWeek 周几
+ * @param year 年
+ * @return 如果匹配返回 {@code true}, 否则返回 {@code false}
+ */
+ public boolean match(int second, int minute, int hour, int dayOfMonth, int month, int dayOfWeek, int year) {
+ for (int i = 0; i < matcherSize; i++) {
+ boolean eval = ((second < 0) || secondMatchers.get(i).match(second)) // 匹配秒(非秒匹配模式下始终返回true)
+ && minuteMatchers.get(i).match(minute)// 匹配分
+ && hourMatchers.get(i).match(hour)// 匹配时
+ && isMatchDayOfMonth(dayOfMonthMatchers.get(i), dayOfMonth, month, Year.isLeap(year))// 匹配日
+ && monthMatchers.get(i).match(month) // 匹配月
+ && dayOfWeekMatchers.get(i).match(dayOfWeek)// 匹配周
+ && isMatch(yearMatchers, i, year);// 匹配年
+ if (eval) {
+ return true;
+ }
+ }
+ return false;
+ }
+
+ /**
+ * 是否匹配日(指定月份的第几天)
+ *
+ * @param matcher {@link ValueMatcher}
+ * @param dayOfMonth 日
+ * @param month 月
+ * @param isLeapYear 是否闰年
+ * @return 是否匹配
+ */
+ private static boolean isMatchDayOfMonth(ValueMatcher matcher, int dayOfMonth, int month, boolean isLeapYear) {
+ return ((matcher instanceof DayOfMonthValueMatcher) //
+ ? ((DayOfMonthValueMatcher) matcher).match(dayOfMonth, month, isLeapYear) //
+ : matcher.match(dayOfMonth));
+ }
+
+ /**
+ * 是否匹配指定的日期时间位置
+ *
+ * @param matchers 匹配器列表
+ * @param index 位置
+ * @param value 被匹配的值
+ * @return 是否匹配
+ * @since 4.0.2
+ */
+ private static boolean isMatch(List matchers, int index, int value) {
+ return (matchers.size() <= index) || matchers.get(index).match(value);
+ }
+}
diff --git a/hutool-cron/src/main/java/cn/hutool/cron/pattern/matcher/ValueMatcherBuilder.java b/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/AbsValueParser.java
similarity index 58%
rename from hutool-cron/src/main/java/cn/hutool/cron/pattern/matcher/ValueMatcherBuilder.java
rename to hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/AbsValueParser.java
index 4cc06ccb0..a436e78de 100644
--- a/hutool-cron/src/main/java/cn/hutool/cron/pattern/matcher/ValueMatcherBuilder.java
+++ b/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/AbsValueParser.java
@@ -1,53 +1,111 @@
-package cn.hutool.cron.pattern.matcher;
+package cn.hutool.cron.pattern.parser;
import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.NumberUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.cron.CronException;
-import cn.hutool.cron.pattern.parser.DayOfMonthValueParser;
-import cn.hutool.cron.pattern.parser.ValueParser;
-import cn.hutool.cron.pattern.parser.YearValueParser;
+import cn.hutool.cron.pattern.matcher.AlwaysTrueValueMatcher;
+import cn.hutool.cron.pattern.matcher.BoolArrayValueMatcher;
+import cn.hutool.cron.pattern.matcher.ValueMatcher;
import java.util.ArrayList;
import java.util.List;
/**
- * {@link ValueMatcher} 构建器,用于构建表达式中每一项的匹配器
- * @author Looly
+ * 简易值转换器。将给定String值转为int,并限定最大值和最小值
+ * 此类同时识别{@code L} 为最大值。
*
+ * @author Looly
*/
-public class ValueMatcherBuilder {
-
+public abstract class AbsValueParser implements ValueParser {
+
+ /**
+ * 最小值(包括)
+ */
+ protected int min;
+ /**
+ * 最大值(包括)
+ */
+ protected int max;
+
+ /**
+ * 构造
+ *
+ * @param min 最小值(包括)
+ * @param max 最大值(包括)
+ */
+ public AbsValueParser(int min, int max) {
+ if (min > max) {
+ this.min = max;
+ this.max = min;
+ } else {
+ this.min = min;
+ this.max = max;
+ }
+ }
+
+ @Override
+ public int parse(String value) throws CronException {
+ if ("L".equalsIgnoreCase(value)) {
+ // L表示最大值
+ return max;
+ }
+
+ int i;
+ try {
+ i = Integer.parseInt(value);
+ } catch (NumberFormatException e) {
+ throw new CronException(e, "Invalid integer value: '{}'", value);
+ }
+ if (i < min || i > max) {
+ throw new CronException("Value {} out of range: [{} , {}]", i, min, max);
+ }
+ return i;
+ }
+
+ @Override
+ public int getMin() {
+ return this.min;
+ }
+
+ @Override
+ public int getMax() {
+ return this.max;
+ }
+
/**
* 处理定时任务表达式每个时间字段
* 多个时间使用逗号分隔
- *
+ *
* @param value 某个时间字段
- * @param parser 针对这个时间字段的解析器
* @return List
*/
- public static ValueMatcher build(String value, ValueParser parser) {
+ @Override
+ public ValueMatcher parseAsValueMatcher(String value) {
if (isMatchAllStr(value)) {
//兼容Quartz的"?"表达式,不会出现互斥情况,与"*"作用相同
return new AlwaysTrueValueMatcher();
}
- List values = parseArray(value, parser);
+ List values = parseArray(value);
if (values.size() == 0) {
throw new CronException("Invalid field: [{}]", value);
}
- if (parser instanceof DayOfMonthValueParser) {
- //考虑每月的天数不同,且存在闰年情况,日匹配单独使用
- return new DayOfMonthValueMatcher(values);
- }else if(parser instanceof YearValueParser){
- //考虑年数字太大,不适合boolean数组,单独使用列表遍历匹配
- return new YearValueMatcher(values);
- }else {
- return new BoolArrayValueMatcher(values);
- }
+ return buildValueMatcher(values);
}
-
+
+ /**
+ * 根据解析的数字值列表构建{@link ValueMatcher}
+ * 默认为{@link BoolArrayValueMatcher},如果有特殊实现,子类须重写此方法
+ *
+ * @param values 数字值列表
+ * @return {@link ValueMatcher}
+ */
+ protected ValueMatcher buildValueMatcher(List values){
+ return new BoolArrayValueMatcher(values);
+ }
+
/**
* 处理数组形式表达式
* 处理的形式包括:
@@ -56,15 +114,14 @@ public class ValueMatcherBuilder {
* a,b,c,d
*
* @param value 子表达式值
- * @param parser 针对这个字段的解析器
* @return 值列表
*/
- private static List parseArray(String value, ValueParser parser){
+ private List parseArray(String value){
final List values = new ArrayList<>();
-
+
final List parts = StrUtil.split(value, StrUtil.C_COMMA);
for (String part : parts) {
- CollUtil.addAllIfNotContains(values, parseStep(part, parser));
+ CollUtil.addAllIfNotContains(values, parseStep(part));
}
return values;
}
@@ -77,24 +134,23 @@ public class ValueMatcherBuilder {
* a/b 或 */b
* a-b/2
*
- *
+ *
* @param value 表达式值
- * @param parser 针对这个时间字段的解析器
* @return List
*/
- private static List parseStep(String value, ValueParser parser) {
+ private List parseStep(String value) {
final List parts = StrUtil.split(value, StrUtil.C_SLASH);
int size = parts.size();
-
+
List results;
if (size == 1) {// 普通形式
- results = parseRange(value, -1, parser);
+ results = parseRange(value, -1);
} else if (size == 2) {// 间隔形式
- final int step = parser.parse(parts.get(1));
+ final int step = parse(parts.get(1));
if (step < 1) {
throw new CronException("Non positive divisor for field: [{}]", value);
}
- results = parseRange(parts.get(0), step, parser);
+ results = parseRange(parts.get(0), step);
} else {
throw new CronException("Invalid syntax of field: [{}]", value);
}
@@ -110,21 +166,20 @@ public class ValueMatcherBuilder {
* 8-3
* 3-3
*
- *
+ *
* @param value 范围表达式
* @param step 步进
- * @param parser 针对这个时间字段的解析器
* @return List
*/
- private static List parseRange(String value, int step, ValueParser parser) {
+ private List parseRange(String value, int step) {
final List results = new ArrayList<>();
-
+
// 全部匹配形式
if (value.length() <= 2) {
//根据步进的第一个数字确定起始时间,类似于 12/3则从12(秒、分等)开始
- int minValue = parser.getMin();
+ int minValue = getMin();
if(false == isMatchAllStr(value)) {
- minValue = Math.max(minValue, parser.parse(value));
+ minValue = Math.max(minValue, parse(value));
}else {
//在全匹配模式下,如果步进不存在,表示步进为1
if(step < 1) {
@@ -132,7 +187,7 @@ public class ValueMatcherBuilder {
}
}
if(step > 0) {
- final int maxValue = parser.getMax();
+ final int maxValue = getMax();
if(minValue > maxValue) {
throw new CronException("Invalid value {} > {}", minValue, maxValue);
}
@@ -151,15 +206,15 @@ public class ValueMatcherBuilder {
List parts = StrUtil.split(value, '-');
int size = parts.size();
if (size == 1) {// 普通值
- final int v1 = parser.parse(value);
+ final int v1 = parse(value);
if(step > 0) {//类似 20/2的形式
- NumberUtil.appendRange(v1, parser.getMax(), step, results);
+ NumberUtil.appendRange(v1, getMax(), step, results);
}else {
results.add(v1);
}
} else if (size == 2) {// range值
- final int v1 = parser.parse(parts.get(0));
- final int v2 = parser.parse(parts.get(1));
+ final int v1 = parse(parts.get(0));
+ final int v2 = parse(parts.get(1));
if(step < 1) {
//在range模式下,如果步进不存在,表示步进为1
step = 1;
@@ -167,21 +222,21 @@ public class ValueMatcherBuilder {
if (v1 < v2) {// 正常范围,例如:2-5
NumberUtil.appendRange(v1, v2, step, results);
} else if (v1 > v2) {// 逆向范围,反选模式,例如:5-2
- NumberUtil.appendRange(v1, parser.getMax(), step, results);
- NumberUtil.appendRange(parser.getMin(), v2, step, results);
+ NumberUtil.appendRange(v1, getMax(), step, results);
+ NumberUtil.appendRange(getMin(), v2, step, results);
} else {// v1 == v2,此时与单值模式一致
- NumberUtil.appendRange(v1, parser.getMax(), step, results);
+ NumberUtil.appendRange(v1, getMax(), step, results);
}
} else {
throw new CronException("Invalid syntax of field: [{}]", value);
}
return results;
}
-
+
/**
* 是否为全匹配符
* 全匹配符指 * 或者 ?
- *
+ *
* @param value 被检查的值
* @return 是否为全匹配符
* @since 4.1.18
diff --git a/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/CronPatternParser.java b/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/CronPatternParser.java
new file mode 100644
index 000000000..052fb42cf
--- /dev/null
+++ b/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/CronPatternParser.java
@@ -0,0 +1,116 @@
+package cn.hutool.cron.pattern.parser;
+
+import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.cron.CronException;
+import cn.hutool.cron.pattern.matcher.AlwaysTrueValueMatcher;
+import cn.hutool.cron.pattern.matcher.MatcherTable;
+
+import java.util.List;
+
+/**
+ * 定时任务表达式解析器,用于将表达式字符串解析为{@link MatcherTable}
+ *
+ * @author looly
+ * @since 5.8.0
+ */
+public class CronPatternParser {
+
+ private static final ValueParser SECOND_VALUE_PARSER = new SecondValueParser();
+ private static final ValueParser MINUTE_VALUE_PARSER = new MinuteValueParser();
+ private static final ValueParser HOUR_VALUE_PARSER = new HourValueParser();
+ private static final ValueParser DAY_OF_MONTH_VALUE_PARSER = new DayOfMonthValueParser();
+ private static final ValueParser MONTH_VALUE_PARSER = new MonthValueParser();
+ private static final ValueParser DAY_OF_WEEK_VALUE_PARSER = new DayOfWeekValueParser();
+ private static final ValueParser YEAR_VALUE_PARSER = new YearValueParser();
+
+ /**
+ * 创建表达式解析器
+ *
+ * @return CronPatternParser
+ */
+ public static CronPatternParser create() {
+ return new CronPatternParser();
+ }
+
+ private MatcherTable matcherTable;
+
+ /**
+ * 解析表达式到匹配表中
+ *
+ * @param cronPattern 复合表达式
+ * @return {@link MatcherTable}
+ */
+ public MatcherTable parse(String cronPattern) {
+ parseGroupPattern(cronPattern);
+ return this.matcherTable;
+ }
+
+ /**
+ * 解析复合任务表达式,格式为:
+ *
+ * cronA | cronB | ...
+ *
+ *
+ * @param groupPattern 复合表达式
+ */
+ private void parseGroupPattern(String groupPattern) {
+ final List patternList = StrUtil.split(groupPattern, '|');
+ matcherTable = new MatcherTable(patternList.size());
+ for (String pattern : patternList) {
+ parseSinglePattern(pattern);
+ }
+ }
+
+ /**
+ * 解析单一定时任务表达式
+ *
+ * @param pattern 表达式
+ */
+ private void parseSinglePattern(String pattern) {
+ final String[] parts = pattern.split("\\s");
+
+ int offset = 0;// 偏移量用于兼容Quartz表达式,当表达式有6或7项时,第一项为秒
+ if (parts.length == 6 || parts.length == 7) {
+ offset = 1;
+ } else if (parts.length != 5) {
+ throw new CronException("Pattern [{}] is invalid, it must be 5-7 parts!", pattern);
+ }
+
+ // 秒,如果不支持秒的表达式,则第一位按照表达式生成时间的秒数赋值,表示整分匹配
+ final String secondPart = (1 == offset) ? parts[0] : String.valueOf(DateUtil.date().second());
+ parseToTable(SECOND_VALUE_PARSER, secondPart);
+
+ // 分
+ parseToTable(MINUTE_VALUE_PARSER, parts[offset]);
+
+ // 时
+ parseToTable(HOUR_VALUE_PARSER, parts[1 + offset]);
+
+ // 天
+ parseToTable(DAY_OF_MONTH_VALUE_PARSER, parts[2 + offset]);
+
+ // 月
+ parseToTable(MONTH_VALUE_PARSER, parts[3 + offset]);
+
+ // 周
+ parseToTable(DAY_OF_WEEK_VALUE_PARSER, parts[4 + offset]);
+
+ // 年
+ if (parts.length == 7) {// 支持年的表达式
+ parseToTable(YEAR_VALUE_PARSER, parts[6]);
+ } else {// 不支持年的表达式,全部匹配
+ matcherTable.yearMatchers.add(new AlwaysTrueValueMatcher());
+ }
+ }
+
+ /**
+ * 将表达式解析后加入到{@link #matcherTable}中
+ *
+ * @param valueParser 表达式解析器
+ * @param patternPart 表达式部分
+ */
+ private void parseToTable(ValueParser valueParser, String patternPart) {
+ valueParser.parseTo(this.matcherTable, patternPart);
+ }
+}
diff --git a/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/DayOfMonthValueParser.java b/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/DayOfMonthValueParser.java
index 2cac2a8b5..e3ddea83e 100644
--- a/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/DayOfMonthValueParser.java
+++ b/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/DayOfMonthValueParser.java
@@ -1,16 +1,24 @@
package cn.hutool.cron.pattern.parser;
import cn.hutool.cron.CronException;
+import cn.hutool.cron.pattern.matcher.DayOfMonthValueMatcher;
+import cn.hutool.cron.pattern.matcher.MatcherTable;
+import cn.hutool.cron.pattern.matcher.ValueMatcher;
+
+import java.util.List;
/**
* 每月的几号值处理
* 每月最多31天,32和“L”都表示最后一天
- *
+ *
* @author Looly
*
*/
-public class DayOfMonthValueParser extends SimpleValueParser {
+public class DayOfMonthValueParser extends AbsValueParser {
+ /**
+ * 构造
+ */
public DayOfMonthValueParser() {
super(1, 31);
}
@@ -23,4 +31,19 @@ public class DayOfMonthValueParser extends SimpleValueParser {
return super.parse(value);
}
}
+
+ @Override
+ public void parseTo(MatcherTable matcherTable, String pattern) {
+ try {
+ matcherTable.dayOfMonthMatchers.add(parseAsValueMatcher(pattern));
+ } catch (Exception e) {
+ throw new CronException(e, "Invalid pattern [{}], parsing 'day of month' field error!", pattern);
+ }
+ }
+
+ @Override
+ protected ValueMatcher buildValueMatcher(List values) {
+ //考虑每月的天数不同,且存在闰年情况,日匹配单独使用
+ return new DayOfMonthValueMatcher(values);
+ }
}
diff --git a/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/DayOfWeekValueParser.java b/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/DayOfWeekValueParser.java
index bad5fa9fc..8b608db44 100644
--- a/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/DayOfWeekValueParser.java
+++ b/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/DayOfWeekValueParser.java
@@ -1,23 +1,26 @@
package cn.hutool.cron.pattern.parser;
import cn.hutool.cron.CronException;
+import cn.hutool.cron.pattern.matcher.MatcherTable;
/**
* 星期值处理
- * 1表示星期一,2表示星期二,依次类推,0和7都可以表示星期日
- *
- * @author Looly
+ * 1表示星期一,2表示星期二,依次类推,0和7都可以表示星期日
+ * {@code L}表示周六
*
+ * @author Looly
*/
-public class DayOfWeekValueParser extends SimpleValueParser {
-
- /** Weeks aliases. */
- private static final String[] ALIASES = { "sun", "mon", "tue", "wed", "thu", "fri", "sat" };
+public class DayOfWeekValueParser extends AbsValueParser {
+
+ /**
+ * Weeks aliases.
+ */
+ private static final String[] ALIASES = {"sun", "mon", "tue", "wed", "thu", "fri", "sat"};
public DayOfWeekValueParser() {
super(0, 7);
}
-
+
/**
* 对于星期提供转换
* 1表示星期一,2表示星期二,依次类推,0和7都可以表示星期日
@@ -31,18 +34,28 @@ public class DayOfWeekValueParser extends SimpleValueParser {
}
}
+ @Override
+ public void parseTo(MatcherTable matcherTable, String pattern) {
+ try {
+ matcherTable.dayOfWeekMatchers.add(parseAsValueMatcher(pattern));
+ } catch (Exception e) {
+ throw new CronException(e, "Invalid pattern [{}], parsing 'day of week' field error!", pattern);
+ }
+ }
+
/**
* 解析别名
+ *
* @param value 别名值
* @return 月份int值
* @throws CronException 无效别名抛出此异常
*/
private int parseAlias(String value) throws CronException {
- if("L".equalsIgnoreCase(value)){
+ if ("L".equalsIgnoreCase(value)) {
//最后一天为星期六
return ALIASES.length - 1;
}
-
+
for (int i = 0; i < ALIASES.length; i++) {
if (ALIASES[i].equalsIgnoreCase(value)) {
return i;
diff --git a/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/HourValueParser.java b/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/HourValueParser.java
index 5041f3837..bc7b5159c 100644
--- a/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/HourValueParser.java
+++ b/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/HourValueParser.java
@@ -1,14 +1,26 @@
package cn.hutool.cron.pattern.parser;
+import cn.hutool.cron.CronException;
+import cn.hutool.cron.pattern.matcher.MatcherTable;
+
/**
- * 小时值处理
- * @author Looly
+ * 小时值处理
+ * 小时被限定在0-23
*
+ * @author Looly
*/
-public class HourValueParser extends SimpleValueParser{
-
+public class HourValueParser extends AbsValueParser {
+
public HourValueParser() {
super(0, 23);
}
+ @Override
+ public void parseTo(MatcherTable matcherTable, String pattern) {
+ try {
+ matcherTable.hourMatchers.add(parseAsValueMatcher(pattern));
+ } catch (Exception e) {
+ throw new CronException(e, "Invalid pattern [{}], parsing 'hour' field error!", pattern);
+ }
+ }
}
diff --git a/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/MinuteValueParser.java b/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/MinuteValueParser.java
index 474660477..f2f76768a 100644
--- a/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/MinuteValueParser.java
+++ b/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/MinuteValueParser.java
@@ -1,14 +1,29 @@
package cn.hutool.cron.pattern.parser;
+import cn.hutool.cron.CronException;
+import cn.hutool.cron.pattern.matcher.MatcherTable;
+
/**
- * 分钟值处理
- * @author Looly
+ * 分钟值处理
+ * 限定于0-59
*
+ * @author Looly
*/
-public class MinuteValueParser extends SimpleValueParser{
-
+public class MinuteValueParser extends AbsValueParser {
+
+ /**
+ * 构造
+ */
public MinuteValueParser() {
super(0, 59);
}
+ @Override
+ public void parseTo(MatcherTable matcherTable, String pattern) {
+ try {
+ matcherTable.minuteMatchers.add(parseAsValueMatcher(pattern));
+ } catch (Exception e) {
+ throw new CronException(e, "Invalid pattern [{}], parsing 'minute' field error!", pattern);
+ }
+ }
}
diff --git a/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/MonthValueParser.java b/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/MonthValueParser.java
index 2f2f47d52..e58a62bb9 100644
--- a/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/MonthValueParser.java
+++ b/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/MonthValueParser.java
@@ -1,22 +1,25 @@
package cn.hutool.cron.pattern.parser;
import cn.hutool.cron.CronException;
+import cn.hutool.cron.pattern.matcher.MatcherTable;
/**
- * 月份值处理
- *
- * @author Looly
+ * 月份值处理
+ * 限定于1-12,1表示一月,支持别名(忽略大小写),如一月是{@code jan}
*
+ * @author Looly
*/
-public class MonthValueParser extends SimpleValueParser {
+public class MonthValueParser extends AbsValueParser {
- /** Months aliases. */
- private static final String[] ALIASES = { "jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec" };
+ /**
+ * Months aliases.
+ */
+ private static final String[] ALIASES = {"jan", "feb", "mar", "apr", "may", "jun", "jul", "aug", "sep", "oct", "nov", "dec"};
public MonthValueParser() {
super(1, 12);
}
-
+
@Override
public int parse(String value) throws CronException {
try {
@@ -26,8 +29,18 @@ public class MonthValueParser extends SimpleValueParser {
}
}
+ @Override
+ public void parseTo(MatcherTable matcherTable, String pattern) {
+ try {
+ matcherTable.monthMatchers.add(parseAsValueMatcher(pattern));
+ } catch (Exception e) {
+ throw new CronException(e, "Invalid pattern [{}], parsing 'month' field error!", pattern);
+ }
+ }
+
/**
* 解析别名
+ *
* @param value 别名值
* @return 月份int值
* @throws CronException 无效月别名抛出此异常
diff --git a/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/SecondValueParser.java b/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/SecondValueParser.java
index b3697ccbc..084157a81 100644
--- a/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/SecondValueParser.java
+++ b/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/SecondValueParser.java
@@ -1,9 +1,22 @@
package cn.hutool.cron.pattern.parser;
+import cn.hutool.cron.CronException;
+import cn.hutool.cron.pattern.matcher.MatcherTable;
+
/**
- * 秒值处理
- * @author Looly
+ * 秒值处理
+ * 限定于0-59
*
+ * @author Looly
*/
-public class SecondValueParser extends MinuteValueParser{
+public class SecondValueParser extends MinuteValueParser {
+
+ @Override
+ public void parseTo(MatcherTable matcherTable, String pattern) {
+ try {
+ matcherTable.secondMatchers.add(parseAsValueMatcher(pattern));
+ } catch (Exception e) {
+ throw new CronException(e, "Invalid pattern [{}], parsing 'second' field error!", pattern);
+ }
+ }
}
diff --git a/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/SimpleValueParser.java b/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/SimpleValueParser.java
deleted file mode 100644
index 280e92290..000000000
--- a/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/SimpleValueParser.java
+++ /dev/null
@@ -1,61 +0,0 @@
-package cn.hutool.cron.pattern.parser;
-
-import cn.hutool.cron.CronException;
-
-/**
- * 简易值转换器。将给定String值转为int
- * @author Looly
- *
- */
-public class SimpleValueParser implements ValueParser {
-
- /** 最小值(包括) */
- protected int min;
- /** 最大值(包括) */
- protected int max;
-
- /**
- * 构造
- *
- * @param min 最小值(包括)
- * @param max 最大值(包括)
- */
- public SimpleValueParser(int min, int max) {
- if(min > max){
- this.min = max;
- this.max = min;
- }else{
- this.min = min;
- this.max = max;
- }
- }
-
- @Override
- public int parse(String value) throws CronException {
- if("L".equalsIgnoreCase(value)){
- // L表示最大值
- return max;
- }
-
- int i;
- try {
- i = Integer.parseInt(value);
- } catch (NumberFormatException e) {
- throw new CronException(e, "Invalid integer value: '{}'", value);
- }
- if (i < min || i > max) {
- throw new CronException("Value {} out of range: [{} , {}]", i, min, max);
- }
- return i;
- }
-
- @Override
- public int getMin() {
- return this.min;
- }
-
- @Override
- public int getMax() {
- return this.max;
- }
-}
diff --git a/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/ValueParser.java b/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/ValueParser.java
index 97c62fbf1..2e49f624f 100644
--- a/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/ValueParser.java
+++ b/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/ValueParser.java
@@ -1,5 +1,8 @@
package cn.hutool.cron.pattern.parser;
+import cn.hutool.cron.pattern.matcher.MatcherTable;
+import cn.hutool.cron.pattern.matcher.ValueMatcher;
+
/**
* 值处理接口
* 值处理用于限定表达式中相应位置的值范围,并转换表达式值为int值
@@ -8,6 +11,28 @@ package cn.hutool.cron.pattern.parser;
*/
public interface ValueParser {
+ /**
+ * 解析表达式后,加入到{@link MatcherTable}的对应列表中
+ *
+ * @param matcherTable {@link MatcherTable}
+ * @param pattern 对应时间部分的表达式
+ */
+ void parseTo(MatcherTable matcherTable, String pattern);
+
+ /**
+ * 解析表达式对应部分为{@link ValueMatcher},支持的表达式包括:
+ *
+ * - 单值或通配符形式,如 a 或 *
+ * - 数组形式,如 1,2,3
+ * - 间隔形式,如 a/b 或 */b
+ * - 范围形式,如 3-8
+ *
+ *
+ * @param pattern 对应时间部分的表达式
+ * @return {@link ValueMatcher}
+ */
+ ValueMatcher parseAsValueMatcher(String pattern);
+
/**
* 处理String值并转为int
* 转换包括:
@@ -15,7 +40,7 @@ public interface ValueParser {
* 数字字符串转为数字
* 别名转为对应的数字(如月份和星期)
*
- *
+ *
* @param value String值
* @return int
*/
@@ -23,14 +48,14 @@ public interface ValueParser {
/**
* 返回最小值
- *
+ *
* @return 最小值
*/
int getMin();
/**
* 返回最大值
- *
+ *
* @return 最大值
*/
int getMax();
diff --git a/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/YearValueParser.java b/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/YearValueParser.java
index 13222c3e6..484a1cf4b 100644
--- a/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/YearValueParser.java
+++ b/hutool-cron/src/main/java/cn/hutool/cron/pattern/parser/YearValueParser.java
@@ -1,14 +1,36 @@
package cn.hutool.cron.pattern.parser;
+import cn.hutool.cron.CronException;
+import cn.hutool.cron.pattern.matcher.MatcherTable;
+import cn.hutool.cron.pattern.matcher.ValueMatcher;
+import cn.hutool.cron.pattern.matcher.YearValueMatcher;
+
+import java.util.List;
+
/**
- * 年值处理
- * @author Looly
+ * 年值处理
+ * 年的限定在1970-2099年
*
+ * @author Looly
*/
-public class YearValueParser extends SimpleValueParser{
-
+public class YearValueParser extends AbsValueParser {
+
public YearValueParser() {
super(1970, 2099);
}
+ @Override
+ public void parseTo(MatcherTable matcherTable, String pattern) {
+ try {
+ matcherTable.yearMatchers.add(parseAsValueMatcher(pattern));
+ } catch (Exception e) {
+ throw new CronException(e, "Invalid pattern [{}], parsing 'year' field error!", pattern);
+ }
+ }
+
+ @Override
+ protected ValueMatcher buildValueMatcher(List values) {
+ //考虑年数字太大,不适合boolean数组,单独使用列表遍历匹配
+ return new YearValueMatcher(values);
+ }
}
diff --git a/hutool-crypto/src/main/java/cn/hutool/crypto/digest/HMac.java b/hutool-crypto/src/main/java/cn/hutool/crypto/digest/HMac.java
index e6c916b6c..7625b9444 100644
--- a/hutool-crypto/src/main/java/cn/hutool/crypto/digest/HMac.java
+++ b/hutool-crypto/src/main/java/cn/hutool/crypto/digest/HMac.java
@@ -163,7 +163,8 @@ public class HMac implements Serializable {
* @return 摘要
*/
public String digestBase64(String data, Charset charset, boolean isUrlSafe) {
- return Base64.encodeStr(digest(data, charset), false, isUrlSafe);
+ final byte[] digest = digest(data, charset);
+ return isUrlSafe ? Base64.encodeUrlSafe(digest) : Base64.encode(digest);
}
/**
diff --git a/hutool-http/src/main/java/cn/hutool/http/GlobalHeaders.java b/hutool-http/src/main/java/cn/hutool/http/GlobalHeaders.java
index 44c12c40a..3dabe6288 100644
--- a/hutool-http/src/main/java/cn/hutool/http/GlobalHeaders.java
+++ b/hutool-http/src/main/java/cn/hutool/http/GlobalHeaders.java
@@ -23,7 +23,7 @@ public enum GlobalHeaders {
/**
* 存储头信息
*/
- Map> headers = new HashMap<>();
+ final Map> headers = new HashMap<>();
/**
* 构造
@@ -43,6 +43,10 @@ public enum GlobalHeaders {
// https://stackoverflow.com/questions/9096987/how-to-overwrite-http-header-host-in-a-httpurlconnection/9098440
System.setProperty("sun.net.http.allowRestrictedHeaders", "true");
+ //解决server certificate change is restricted during renegotiation问题
+ System.setProperty("jdk.tls.allowUnsafeServerCertChange", "true");
+ System.setProperty("sun.security.ssl.allowUnsafeRenegotiation", "true");
+
if (isReset) {
this.headers.clear();
}
diff --git a/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelUtil.java b/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelUtil.java
index 17860c8ae..8db7e1ced 100644
--- a/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelUtil.java
+++ b/hutool-poi/src/main/java/cn/hutool/poi/excel/ExcelUtil.java
@@ -345,7 +345,7 @@ public class ExcelUtil {
* @return {@link BigExcelWriter}
* @since 4.1.13
*/
- public static ExcelWriter getBigWriter() {
+ public static BigExcelWriter getBigWriter() {
try {
return new BigExcelWriter();
} catch (NoClassDefFoundError e) {
@@ -362,7 +362,7 @@ public class ExcelUtil {
* @return {@link BigExcelWriter}
* @since 4.1.13
*/
- public static ExcelWriter getBigWriter(int rowAccessWindowSize) {
+ public static BigExcelWriter getBigWriter(int rowAccessWindowSize) {
try {
return new BigExcelWriter(rowAccessWindowSize);
} catch (NoClassDefFoundError e) {
diff --git a/hutool-poi/src/main/java/cn/hutool/poi/excel/sax/Excel07SaxReader.java b/hutool-poi/src/main/java/cn/hutool/poi/excel/sax/Excel07SaxReader.java
index b50034cb3..2c4ec4b71 100644
--- a/hutool-poi/src/main/java/cn/hutool/poi/excel/sax/Excel07SaxReader.java
+++ b/hutool-poi/src/main/java/cn/hutool/poi/excel/sax/Excel07SaxReader.java
@@ -3,6 +3,7 @@ package cn.hutool.poi.excel.sax;
import cn.hutool.core.io.IORuntimeException;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.poi.excel.sax.handler.RowHandler;
import cn.hutool.poi.exceptions.POIException;
@@ -127,13 +128,9 @@ public class Excel07SaxReader implements ExcelSaxReader {
}
// 获取共享字符串表
- try {
- this.handler.sharedStrings = xssfReader.getSharedStringsTable();
- } catch (IOException e) {
- throw new IORuntimeException(e);
- } catch (InvalidFormatException e) {
- throw new POIException(e);
- }
+ // POI-5.2.0开始返回值有所变更,导致实际使用时提示方法未找到,此处使用反射调用,解决不同版本返回值变更问题
+ //this.handler.sharedStrings = xssfReader.getSharedStringsTable();
+ this.handler.sharedStrings = ReflectUtil.invoke(xssfReader, "getSharedStringsTable");
return readSheets(xssfReader, idOrRidOrSheetName);
}