diff --git a/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java b/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java index 5a81bb816..d56e01966 100755 --- a/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java +++ b/hutool-core/src/main/java/cn/hutool/core/util/ReflectUtil.java @@ -9,6 +9,7 @@ import cn.hutool.core.exceptions.InvocationTargetRuntimeException; import cn.hutool.core.exceptions.UtilException; import cn.hutool.core.lang.Assert; import cn.hutool.core.lang.Filter; +import java.lang.reflect.Modifier; import cn.hutool.core.lang.reflect.MethodHandleUtil; import cn.hutool.core.map.MapUtil; import cn.hutool.core.map.WeakConcurrentMap; @@ -303,6 +304,7 @@ public class ReflectUtil { * 设置字段值
* 若值类型与字段类型不一致,则会尝试通过 {@link Convert} 进行转换
* 若字段类型是原始类型而传入的值是 null,则会将字段设置为对应原始类型的默认值(见 {@link ClassUtil#getDefaultValue(Class)}) + * 如果是final字段,setFieldValue,调用这可以先调用 {@link ReflectUtil#removeFinalModify(Field)}方法去除final修饰符
* * @param obj 对象,static字段则此处传Class * @param fieldName 字段名 @@ -321,7 +323,8 @@ public class ReflectUtil { /** * 设置字段值
* 若值类型与字段类型不一致,则会尝试通过 {@link Convert} 进行转换
- * 若字段类型是原始类型而传入的值是 null,则会将字段设置为对应原始类型的默认值(见 {@link ClassUtil#getDefaultValue(Class)}) + * 若字段类型是原始类型而传入的值是 null,则会将字段设置为对应原始类型的默认值(见 {@link ClassUtil#getDefaultValue(Class)})
+ * 如果是final字段,setFieldValue,调用这可以先调用 {@link ReflectUtil#removeFinalModify(Field)}方法去除final修饰符 * * @param obj 对象,如果是static字段,此参数为null * @param field 字段 @@ -1108,6 +1111,58 @@ public class ReflectUtil { return accessibleObject; } + /** + * 设置final的field字段可以被修改 + *

+ * 只要不会被编译器内联优化的 final 属性就可以通过反射有效的进行修改 -- 修改后代码中可使用到新的值; + *
+ *

以下属性,编译器会内联优化,无法通过反射修改:

+ * + *

以下属性,可以通过反射修改:

+ * + *

+ * + * //示例,移除final修饰符 + * class JdbcDialects {private static final List dialects = new ArrayList<>();} + * Field field = ReflectUtil.getField(JdbcDialects.class, fieldName); + * ReflectUtil.removeFinalModify(field); + * ReflectUtil.setFieldValue(JdbcDialects.class, fieldName, dialects); + * + * @param field 被修改的field,不可以为空 + * @throws UtilException IllegalAccessException等异常包装 + * @since 5.8.8 + * @author dazer + */ + public static void removeFinalModify(Field field) { + if (field != null) { + if (ModifierUtil.hasModifier(field, ModifierUtil.ModifierType.FINAL)) { + //将字段的访问权限设为true:即去除private修饰符的影响 + if (!field.isAccessible()) { + field.setAccessible(true); + } + try { + //去除final修饰符的影响,将字段设为可修改的 + Field modifiersField = Field.class.getDeclaredField("modifiers"); + //Field 的 modifiers 是私有的 + modifiersField.setAccessible(true); + //& :位与运算符,按位与; 运算规则:两个数都转为二进制,然后从高位开始比较,如果两个数都为1则为1,否则为0。 + //~ :位非运算符,按位取反;运算规则:转成二进制,如果位为0,结果是1,如果位为1,结果是0. + modifiersField.setInt(field, field.getModifiers() & ~Modifier.FINAL); + } catch (NoSuchFieldException | IllegalAccessException e) { + //内部,工具类,基本不抛出异常 + throw new UtilException(e, "IllegalAccess for {}.{}", field.getDeclaringClass(), field.getName()); + } + } + } + } + /** * 获取方法的唯一键,结构为: *
diff --git a/hutool-core/src/test/java/cn/hutool/core/util/ReflectUtilTest.java b/hutool-core/src/test/java/cn/hutool/core/util/ReflectUtilTest.java
index 7792804a8..6170ec61b 100755
--- a/hutool-core/src/test/java/cn/hutool/core/util/ReflectUtilTest.java
+++ b/hutool-core/src/test/java/cn/hutool/core/util/ReflectUtilTest.java
@@ -13,7 +13,9 @@ import org.junit.Test;
 
 import java.lang.reflect.Field;
 import java.lang.reflect.Method;
+import java.util.Arrays;
 import java.util.Collection;
+import java.util.List;
 import java.util.Map;
 
 /**
@@ -268,4 +270,26 @@ public class ReflectUtilTest {
 		int[] intArray = ReflectUtil.newInstanceIfPossible(int[].class);
 		Assert.assertArrayEquals(new int[0], intArray);
 	}
+
+	public static class JdbcDialects {
+		private static final List DIALECTS =
+				Arrays.asList(1L, 2L, 3L);
+	}
+
+	@Test
+	public void setFieldValueTest() {
+		String fieldName = "DIALECTS";
+		final List dialects =
+				Arrays.asList(
+						1,
+						2,
+						3,
+						99
+				);
+		Field field = ReflectUtil.getField(JdbcDialects.class, fieldName);
+		ReflectUtil.removeFinalModify(field);
+		ReflectUtil.setFieldValue(JdbcDialects.class, fieldName, dialects);
+
+		Assert.assertEquals(dialects, ReflectUtil.getFieldValue(JdbcDialects.class, fieldName));
+	}
 }