diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/BeanPath.java b/hutool-core/src/main/java/cn/hutool/core/bean/BeanPath.java
index 808c7f1d2..ce8e3e58c 100644
--- a/hutool-core/src/main/java/cn/hutool/core/bean/BeanPath.java
+++ b/hutool-core/src/main/java/cn/hutool/core/bean/BeanPath.java
@@ -10,12 +10,7 @@ import cn.hutool.core.util.ArrayUtil;
import cn.hutool.core.util.CharUtil;
import java.io.Serializable;
-import java.util.ArrayList;
-import java.util.Collection;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-import java.util.Objects;
+import java.util.*;
/**
* Bean路径表达式,用于获取多层嵌套Bean中的字段值或Bean对象
@@ -119,11 +114,14 @@ public class BeanPath implements Serializable {
public void set(final Object bean, final Object value) {
Objects.requireNonNull(bean);
- Object subBean = bean, previousBean;
+ Object subBean = bean;
+ Object previousBean = null;
boolean isFirst = true;
String patternPart;
// 尝试找到倒数第二个子对象, 最终需要设置它的字段值
final int length = patternParts.size() - 1;
+
+ // 填充父字段缺失的对象
for (int i = 0; i < length; i++) {
patternPart = patternParts.get(i);
// 保存当前操作的bean, 以便subBean不存在时, 可以用来填充缺失的子对象
@@ -145,8 +143,13 @@ public class BeanPath implements Serializable {
}
}
}
- // 设置最终的字段值
- BeanUtil.setFieldValue(subBean, patternParts.get(length), value);
+
+ // 设置最终的(当前)字段值
+ final Object newSubBean = BeanUtil.setFieldValue(subBean, patternParts.get(length), value);
+ if(newSubBean != subBean && null != previousBean){
+ // 对象变更,重新加入
+ BeanUtil.setFieldValue(previousBean, patternParts.get(length - 1), newSubBean);
+ }
}
@Override
@@ -166,7 +169,7 @@ public class BeanPath implements Serializable {
private Object get(final List patternParts, final Object bean) {
Object subBean = bean;
boolean isFirst = true;
- for (String patternPart : patternParts) {
+ for (final String patternPart : patternParts) {
subBean = getFieldValue(subBean, patternPart);
if (null == subBean) {
// 支持表达式的第一个对象为Bean本身(若用户定义表达式$开头,则不做此操作)
diff --git a/hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java b/hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java
index 2a6dedea5..6a3b29ae7 100755
--- a/hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/bean/BeanUtil.java
@@ -118,7 +118,7 @@ public class BeanUtil {
if (method.getParameterCount() == 0) {
final String name = method.getName();
if (name.startsWith("get") || name.startsWith("is")) {
- if(false == "getClass".equals(name)){
+ if (false == "getClass".equals(name)) {
return true;
}
}
@@ -311,24 +311,32 @@ public class BeanUtil {
/**
* 设置字段值,通过反射设置字段值,并不调用setXXX方法
- * 对象同样支持Map类型,fieldNameOrIndex即为key
+ * 对象同样支持Map类型,fieldNameOrIndex即为key,支持:
+ *
+ * - Map
+ * - List
+ * - Bean
+ *
*
* @param bean Bean
* @param fieldNameOrIndex 字段名或序号,序号支持负数
* @param value 值
+ * @return bean,当为数组时,返回一个新的数组
*/
@SuppressWarnings({"unchecked", "rawtypes"})
- public static void setFieldValue(final Object bean, final String fieldNameOrIndex, final Object value) {
+ public static Object setFieldValue(final Object bean, final String fieldNameOrIndex, final Object value) {
if (bean instanceof Map) {
((Map) bean).put(fieldNameOrIndex, value);
} else if (bean instanceof List) {
ListUtil.setOrPadding((List) bean, Convert.toInt(fieldNameOrIndex), value);
} else if (ArrayUtil.isArray(bean)) {
- ArrayUtil.setOrAppend(bean, Convert.toInt(fieldNameOrIndex), value);
+ // issue#3008,追加产生新数组,此处返回新数组
+ return ArrayUtil.setOrAppend(bean, Convert.toInt(fieldNameOrIndex), value);
} else {
// 普通Bean对象
FieldUtil.setFieldValue(bean, fieldNameOrIndex, value);
}
+ return bean;
}
/**
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/IoUtil.java b/hutool-core/src/main/java/cn/hutool/core/io/IoUtil.java
index c87f806da..26645b862 100755
--- a/hutool-core/src/main/java/cn/hutool/core/io/IoUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/IoUtil.java
@@ -701,7 +701,7 @@ public class IoUtil extends NioUtil {
* @return {@link InputStream}
* @since 4.0.9
*/
- public static InputStream toMarkSupportStream(final InputStream in) {
+ public static InputStream toMarkSupport(final InputStream in) {
if (null == in) {
return null;
}
@@ -711,6 +711,23 @@ public class IoUtil extends NioUtil {
return in;
}
+ /**
+ * 将{@link Reader}转换为支持mark标记的Reader
+ * 若原Reader支持mark标记,则返回原Reader,否则使用{@link BufferedReader} 包装之
+ *
+ * @param reader {@link Reader}
+ * @return {@link Reader}
+ */
+ public static Reader toMarkSupport(final Reader reader) {
+ if (null == reader) {
+ return null;
+ }
+ if (false == reader.markSupported()) {
+ return new BufferedReader(reader);
+ }
+ return reader;
+ }
+
/**
* 获得{@link PushbackReader}
* 如果是{@link PushbackReader}强转返回,否则新建
diff --git a/hutool-core/src/main/java/cn/hutool/core/io/ReaderWrapper.java b/hutool-core/src/main/java/cn/hutool/core/io/ReaderWrapper.java
index f196d41b9..4c279b4c4 100755
--- a/hutool-core/src/main/java/cn/hutool/core/io/ReaderWrapper.java
+++ b/hutool-core/src/main/java/cn/hutool/core/io/ReaderWrapper.java
@@ -53,6 +53,31 @@ public class ReaderWrapper extends Reader implements Wrapper {
return raw.read(buffer, off, len);
}
+ @Override
+ public boolean markSupported() {
+ return this.raw.markSupported();
+ }
+
+ @Override
+ public void mark(final int readAheadLimit) throws IOException {
+ this.raw.mark(readAheadLimit);
+ }
+
+ @Override
+ public long skip(final long n) throws IOException {
+ return this.raw.skip(n);
+ }
+
+ @Override
+ public boolean ready() throws IOException {
+ return this.raw.ready();
+ }
+
+ @Override
+ public void reset() throws IOException {
+ this.raw.reset();
+ }
+
@Override
public void close() throws IOException {
raw.close();
diff --git a/hutool-core/src/main/java/cn/hutool/core/net/NetUtil.java b/hutool-core/src/main/java/cn/hutool/core/net/NetUtil.java
index c48be93b4..480bbc0c5 100755
--- a/hutool-core/src/main/java/cn/hutool/core/net/NetUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/net/NetUtil.java
@@ -624,7 +624,8 @@ public class NetUtil {
}
/**
- * 获取主机名称,一次获取会缓存名称
+ * 获取主机名称,一次获取会缓存名称
+ * 注意此方法会触发反向DNS解析,导致阻塞,阻塞时间取决于网络!
*
* @return 主机名称
* @since 5.4.4
diff --git a/hutool-core/src/main/java/cn/hutool/core/reflect/FieldUtil.java b/hutool-core/src/main/java/cn/hutool/core/reflect/FieldUtil.java
index 9257c7e69..4da535792 100644
--- a/hutool-core/src/main/java/cn/hutool/core/reflect/FieldUtil.java
+++ b/hutool-core/src/main/java/cn/hutool/core/reflect/FieldUtil.java
@@ -266,16 +266,17 @@ public class FieldUtil {
}
/**
- * 设置字段值
+ * 设置字段值,如果值类型必须与字段类型匹配,会自动转换对象类型
*
* @param obj 对象,如果是static字段,此参数为null
* @param field 字段
- * @param value 值,值类型必须与字段类型匹配,不会自动转换对象类型
+ * @param value 值,类型不匹配会自动转换对象类型
* @throws UtilException UtilException 包装IllegalAccessException异常
*/
public static void setFieldValue(final Object obj, final Field field, Object value) throws UtilException {
Assert.notNull(field, "Field in [{}] not exist !", obj);
+ // 值类型检查和转换
final Class> fieldType = field.getType();
if (null != value) {
if (false == fieldType.isAssignableFrom(value.getClass())) {
@@ -290,6 +291,18 @@ public class FieldUtil {
value = ClassUtil.getDefaultValue(fieldType);
}
+ setFieldValueExact(obj, field, value);
+ }
+
+ /**
+ * 设置字段值,传入的字段值必须和字段类型一致,否则抛出异常
+ *
+ * @param obj 对象,如果是static字段,此参数为null
+ * @param field 字段
+ * @param value 值,值类型必须与字段类型匹配
+ * @throws UtilException UtilException 包装IllegalAccessException异常
+ */
+ public static void setFieldValueExact(final Object obj, final Field field, final Object value) throws UtilException {
ReflectUtil.setAccessible(field);
try {
field.set(obj instanceof Class ? null : obj, value);
diff --git a/hutool-core/src/test/java/cn/hutool/core/bean/BeanPathTest.java b/hutool-core/src/test/java/cn/hutool/core/bean/BeanPathTest.java
index e28a14541..807069988 100644
--- a/hutool-core/src/test/java/cn/hutool/core/bean/BeanPathTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/bean/BeanPathTest.java
@@ -3,6 +3,8 @@ package cn.hutool.core.bean;
import cn.hutool.core.lang.test.bean.ExamInfoDict;
import cn.hutool.core.lang.test.bean.UserInfoDict;
import cn.hutool.core.map.Dict;
+import cn.hutool.core.util.ArrayUtil;
+import lombok.Data;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
@@ -152,4 +154,36 @@ public class BeanPathTest {
BeanPath.of("aa.bb").set(dict, "BB");
Assert.assertEquals("{aa={bb=BB}}", dict.toString());
}
+
+ @Test
+ public void appendArrayTest(){
+ // issue#3008@Github
+ final MyUser myUser = new MyUser();
+ BeanPath.of("hobby[0]").set(myUser, "LOL");
+ BeanPath.of("hobby[1]").set(myUser, "KFC");
+ BeanPath.of("hobby[2]").set(myUser, "COFFE");
+
+ Assert.assertEquals("[LOL, KFC, COFFE]", ArrayUtil.toString(myUser.getHobby()));
+ }
+
+ @Test
+ public void appendArrayTest2(){
+ // issue#3008@Github
+ final MyUser2 myUser = new MyUser2();
+ BeanPath.of("myUser.hobby[0]").set(myUser, "LOL");
+ BeanPath.of("myUser.hobby[1]").set(myUser, "KFC");
+ BeanPath.of("myUser.hobby[2]").set(myUser, "COFFE");
+
+ Assert.assertEquals("[LOL, KFC, COFFE]", ArrayUtil.toString(myUser.getMyUser().getHobby()));
+ }
+
+ @Data
+ static class MyUser {
+ private String[] hobby;
+ }
+
+ @Data
+ static class MyUser2 {
+ private MyUser myUser;
+ }
}
diff --git a/hutool-core/src/test/java/cn/hutool/core/net/NetUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/net/NetUtilTest.java
index fceb10d56..f7b2bf917 100755
--- a/hutool-core/src/test/java/cn/hutool/core/net/NetUtilTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/net/NetUtilTest.java
@@ -78,10 +78,17 @@ public class NetUtilTest {
}
@Test
+ @Ignore
public void getLocalHostNameTest() {
+ // 注意此方法会触发反向DNS解析,导致阻塞,阻塞时间取决于网络!
Assert.assertNotNull(NetUtil.getLocalHostName());
}
+ @Test
+ public void getLocalHostTest() {
+ Assert.assertNotNull(NetUtil.getLocalhost());
+ }
+
@Test
public void pingTest(){
Assert.assertTrue(NetUtil.ping("127.0.0.1"));
diff --git a/hutool-extra/src/main/java/cn/hutool/extra/compress/CompressUtil.java b/hutool-extra/src/main/java/cn/hutool/extra/compress/CompressUtil.java
index 6f3e49b07..0a62e95d4 100644
--- a/hutool-extra/src/main/java/cn/hutool/extra/compress/CompressUtil.java
+++ b/hutool-extra/src/main/java/cn/hutool/extra/compress/CompressUtil.java
@@ -74,7 +74,7 @@ public class CompressUtil {
* @return {@link CompressorOutputStream}
*/
public static CompressorInputStream getIn(String compressorName, InputStream in) {
- in = IoUtil.toMarkSupportStream(in);
+ in = IoUtil.toMarkSupport(in);
try {
if(StrUtil.isBlank(compressorName)){
compressorName = CompressorStreamFactory.detect(in);
diff --git a/hutool-json/src/main/java/cn/hutool/json/InternalJSONUtil.java b/hutool-json/src/main/java/cn/hutool/json/InternalJSONUtil.java
index 71a5b2693..5c8bb7ce2 100755
--- a/hutool-json/src/main/java/cn/hutool/json/InternalJSONUtil.java
+++ b/hutool-json/src/main/java/cn/hutool/json/InternalJSONUtil.java
@@ -21,9 +21,7 @@ import cn.hutool.json.serialize.JSONStringer;
import cn.hutool.json.writer.GlobalValueWriterMapping;
import cn.hutool.json.writer.JSONValueWriter;
-import java.io.IOException;
-import java.io.StringWriter;
-import java.io.Writer;
+import java.io.*;
import java.lang.reflect.Type;
import java.math.BigDecimal;
import java.sql.SQLException;
@@ -316,7 +314,7 @@ public final class InternalJSONUtil {
*/
public static Writer quote(final String str, final Writer writer, final boolean isWrap) throws IORuntimeException {
try {
- return doQuote(str, writer, isWrap);
+ return _quote(str, writer, isWrap);
} catch (final IOException e) {
throw new IORuntimeException(e);
}
@@ -422,7 +420,7 @@ public final class InternalJSONUtil {
* @throws IOException IO异常
* @since 3.3.1
*/
- private static Writer doQuote(final String str, final Writer writer, final boolean isWrap) throws IOException {
+ private static Writer _quote(final String str, final Writer writer, final boolean isWrap) throws IOException {
if (StrUtil.isEmpty(str)) {
if (isWrap) {
writer.write("\"\"");
diff --git a/hutool-json/src/main/java/cn/hutool/json/JSONArray.java b/hutool-json/src/main/java/cn/hutool/json/JSONArray.java
index 5b54acf21..7ea21621d 100755
--- a/hutool-json/src/main/java/cn/hutool/json/JSONArray.java
+++ b/hutool-json/src/main/java/cn/hutool/json/JSONArray.java
@@ -8,7 +8,7 @@ import cn.hutool.core.lang.mutable.MutableEntry;
import cn.hutool.core.lang.mutable.MutableObj;
import cn.hutool.core.text.StrJoiner;
import cn.hutool.core.util.ObjUtil;
-import cn.hutool.json.mapper.ArrayMapper;
+import cn.hutool.json.mapper.JSONArrayMapper;
import cn.hutool.json.writer.JSONWriter;
import java.io.StringWriter;
@@ -148,7 +148,7 @@ public class JSONArray implements JSON, JSONGetter, List