diff --git a/docs/zh-cn/doc/omni-constructor.md b/docs/zh-cn/doc/omni-constructor.md index bad85cc..571a3fb 100644 --- a/docs/zh-cn/doc/omni-constructor.md +++ b/docs/zh-cn/doc/omni-constructor.md @@ -103,9 +103,9 @@ OmniAccessor.set(parent, "children[2]/*/value", 100); > **你真的需要用到`OmniAccessor`吗?** > -> `OmniAccessor`具有基于Fail-Fast机制的防代码重构能力,当用户提供的访问路径无法匹配到任何成员时,`OmniAccessor`将立即抛出异常,使单元测试提前终止。然而相比常规的成员访问方式,`OmniAccessor`在IDE重构方面的支持依然偏弱。 +> `OmniAccessor`具有基于Fail-Fast机制的防代码重构能力,当用户提供的访问路径无法匹配到任何成员时,`OmniAccessor`将立即抛出`NoSuchMemberError`错误,使单元测试提前终止。然而相比常规的成员访问方式,`OmniAccessor`在IDE重构方面的支持依然偏弱。 > > 对于复杂对象的内容赋值,大多数情况下,我们更推荐使用[构造者模式](https://developer.aliyun.com/article/705058),或者暴露Getter/Setter方法实现。这些常规手段虽然稍显笨拙(尤其在需要为许多相似的成员批量赋值的时候),但对业务逻辑的封装和重构都更加友好。 -> 仅当原类型不适合改造,且没有其它可访问目标成员的方法时,`OmniAccessor`才是最后的终极工具。 +> 仅当原类型不适合改造,且没有其它可访问目标成员的方法时,`OmniAccessor`才是最后的终极手段。 > -> 出于相同的原因,虽然技术上可行,但我们并不推荐在除单元测试之外的场景使用`OmniAccessor`方式来读写业务类的成员字段。 +> 出于相同的原因,我们并不推荐在除单元测试之外的场景使用`OmniAccessor`方式来读写业务类的成员字段(虽然技术上可行)。 diff --git a/testable-core/src/main/java/com/alibaba/testable/core/error/NoSuchMemberError.java b/testable-core/src/main/java/com/alibaba/testable/core/error/NoSuchMemberError.java new file mode 100644 index 0000000..3e22b9b --- /dev/null +++ b/testable-core/src/main/java/com/alibaba/testable/core/error/NoSuchMemberError.java @@ -0,0 +1,12 @@ +package com.alibaba.testable.core.error; + +/** + * @author flin + */ +public class NoSuchMemberError extends Error { + + public NoSuchMemberError(String message) { + super(message); + } + +} diff --git a/testable-core/src/main/java/com/alibaba/testable/core/tool/OmniAccessor.java b/testable-core/src/main/java/com/alibaba/testable/core/tool/OmniAccessor.java index efc7d22..74eb903 100644 --- a/testable-core/src/main/java/com/alibaba/testable/core/tool/OmniAccessor.java +++ b/testable-core/src/main/java/com/alibaba/testable/core/tool/OmniAccessor.java @@ -1,5 +1,6 @@ package com.alibaba.testable.core.tool; +import com.alibaba.testable.core.error.NoSuchMemberError; import com.alibaba.testable.core.util.CollectionUtil; import com.alibaba.testable.core.util.FixSizeMap; import com.alibaba.testable.core.util.TypeUtil; @@ -67,6 +68,9 @@ public class OmniAccessor { } } } + if (values.isEmpty()) { + throw new NoSuchMemberError("Query \"" + queryPath + "\"" + " does not match any member!"); + } return values; } @@ -86,8 +90,9 @@ public class OmniAccessor { List parent = getByPath(target, toParent(memberPath), toParent(queryPath)); if (!parent.isEmpty()) { for (Object p : parent) { - setByPathSegment(p, toChild(memberPath), toChild(queryPath), value); - count++; + if (setByPathSegment(p, toChild(memberPath), toChild(queryPath), value)) { + count++; + } } } } catch (NoSuchFieldException e) { @@ -97,6 +102,9 @@ public class OmniAccessor { } } } + if (count == 0) { + throw new NoSuchMemberError("Query \"" + queryPath + "\"" + " does not match any member!"); + } return count; } @@ -237,21 +245,26 @@ public class OmniAccessor { return fullQuerySegments; } - private static void setByPathSegment(Object target, String memberSegment, String querySegment, Object value) + private static boolean setByPathSegment(Object target, String memberSegment, String querySegment, Object value) throws IllegalAccessException { String name = extraNameFromMemberRecord(memberSegment); int nth = extraIndexFromQuery(querySegment); + boolean isFieldMatch = false; if (target.getClass().isArray()) { for (int i = 0; i < Array.getLength(target); i++) { - setFieldByName(Array.get(target, i), name, nth, value); + isFieldMatch |= setFieldByName(Array.get(target, i), name, nth, value); } } else { - setFieldByName(target, name, nth, value); + isFieldMatch = setFieldByName(target, name, nth, value); } + return isFieldMatch; } - private static void setFieldByName(Object target, String name, int nth, Object value) throws IllegalAccessException { + private static boolean setFieldByName(Object target, String name, int nth, Object value) throws IllegalAccessException { Field field = TypeUtil.getFieldByName(target.getClass(), name); + if (field == null) { + return false; + } field.setAccessible(true); if (field.getType().isArray()) { Object f = field.get(target); @@ -265,6 +278,7 @@ public class OmniAccessor { } else { field.set(target, value); } + return true; } }