{"record":{"id":"592abd8547e8ffb3","repo":"chinabugotech/hutool","slug":"proxied-annotation-can-not-reset-attributes","errorCode":null,"errorMessage":"proxied annotation can not reset attributes","messagePattern":"proxied annotation can not reset attributes","errorType":"exception","errorClass":"UnsupportedOperationException","httpStatus":null,"severity":"error","filePath":"hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationProxy.java","lineNumber":114,"sourceCode":"\t\treturn Opt.ofNullable(methods.get(method.getName()))\n\t\t\t\t.map(m -> m.apply(method, args))\n\t\t\t\t.orElseGet(() -> ReflectUtil.invoke(annotation.getAnnotation(), method, args));\n\t}\n\n\t// ========================= 代理方法 =========================\n\n\tvoid loadMethods() {\n\t\t// 非用户属性\n\t\tmethods.put(\"toString\", (method, args) -> proxyToString());\n\t\tmethods.put(\"hashCode\", (method, args) -> proxyHashCode());\n\t\tmethods.put(\"getSynthesizedAnnotation\", (method, args) -> proxyGetSynthesizedAnnotation());\n\t\tmethods.put(\"getRoot\", (method, args) -> annotation.getRoot());\n\t\tmethods.put(\"getVerticalDistance\", (method, args) -> annotation.getVerticalDistance());\n\t\tmethods.put(\"getHorizontalDistance\", (method, args) -> annotation.getHorizontalDistance());\n\t\tmethods.put(\"hasAttribute\", (method, args) -> annotation.hasAttribute((String) args[0], (Class<?>) args[1]));\n\t\tmethods.put(\"getAttributes\", (method, args) -> annotation.getAttributes());\n\t\tmethods.put(\"setAttribute\", (method, args) -> {\n\t\t\tthrow new UnsupportedOperationException(\"proxied annotation can not reset attributes\");\n\t\t});\n\t\tmethods.put(\"getAttributeValue\", (method, args) -> annotation.getAttributeValue((String) args[0]));\n\t\tmethods.put(\"annotationType\", (method, args) -> annotation.annotationType());\n\n\t\t// 可以被合成的用户属性\n\t\tStream.of(ClassUtil.getDeclaredMethods(annotation.getAnnotation().annotationType()))\n\t\t\t.filter(m -> !methods.containsKey(m.getName()))\n\t\t\t.forEach(m -> methods.put(m.getName(), (method, args) -> proxyAttributeValue(method)));\n\t}\n\n\tprivate String proxyToString() {\n\t\tfinal String attributes = Stream.of(ClassUtil.getDeclaredMethods(annotation.getAnnotation().annotationType()))\n\t\t\t\t.filter(AnnotationUtil::isAttributeMethod)\n\t\t\t\t.map(method -> CharSequenceUtil.format(\n\t\t\t\t\t\t\"{}={}\", method.getName(), proxyAttributeValue(method))\n\t\t\t\t)\n\t\t\t\t.collect(Collectors.joining(\", \"));\n\t\treturn CharSequenceUtil.format(\"@{}({})\", annotation.annotationType().getName(), attributes);","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/chinabugotech/hutool/blob/8870454b2a0c29cc6ffd31dcf5667c8ceb2fc442/hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationProxy.java#L96-L132","documentation":"SynthesizedAnnotationProxy is an immutable view over a synthesized annotation; calling setAttribute on it is explicitly blocked because the proxy has no backing store to mutate. The lambda mapped to setAttribute always throws UnsupportedOperationException regardless of arguments. All other attribute access (getAttributeValue, hasAttribute, etc.) is supported.","triggerScenarios":"Obtaining a proxy via AnnotationSynthesizer.synthesize(...) (or any API returning a SynthesizedAnnotationProxied) and invoking setAttribute(name, value) on it. This typically happens when generic annotation-mutation code treats the synthesized result like a mutable AnnotationSynopsis.","commonSituations":"Frameworks that try to override annotation attributes at runtime for testing/config and receive a synthesized proxy instead of the raw synthesized annotation; misreading the API and assuming the proxy supports the full SynthesizedAnnotation mutation surface.","solutions":["Mutate the underlying SynthesizedAnnotation (annotation.setAttribute) before proxying, not the proxy itself.","If you need a modified annotation, build a new synthesized annotation with the changed value rather than calling setAttribute on the proxy.","Type-check: if the object is a SynthesizedAnnotationProxy, skip setAttribute calls."],"exampleFix":"// before\nObject proxy = synthesizer.synthesize(annotation);\n((SynthesizedAnnotation) proxy).setAttribute(\"k\", v); // throws\n// after\nSynthesizedAnnotation synth = synthesizer.getSynthesizedAnnotation(...);\nsynth.setAttribute(\"k\", v);\nObject proxy = synthesizer.synthesize(annotation);","handlingStrategy":"validation","validationCode":"if (proxy instanceof SynthesizedAnnotationProxy) throw new IllegalStateException(\"cannot setAttribute on immutable proxy\");","typeGuard":"static boolean isMutableSynthesis(Object o) { return o instanceof SynthesizedAnnotation && !(o instanceof SynthesizedAnnotationProxy); }","tryCatchPattern":"try { ((SynthesizedAnnotation)obj).setAttribute(k,v); } catch (UnsupportedOperationException e) { /* rebuild from underlying */ }","preventionTips":["Mutate the underlying SynthesizedAnnotation before proxying.","Do not treat a synthesized proxy as a mutable annotation."],"tags":["annotation","proxy","immutable","unsupported-operation"],"backgroundTag":null,"analyzedSha":"8870454b2a0c29cc6ffd31dcf5667c8ceb2fc442","analyzedAt":"2026-08-14T04:01:12.892Z","schemaVersion":2},"datasetVersion":"2026-08-14T05:17:29.042Z"}