chinabugotech/hutool · error · UnsupportedOperationException
proxied annotation can not reset attributes
Error message
proxied annotation can not reset attributes
What it means
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.
Source
Thrown at hutool-core/src/main/java/cn/hutool/core/annotation/SynthesizedAnnotationProxy.java:114
return Opt.ofNullable(methods.get(method.getName()))
.map(m -> m.apply(method, args))
.orElseGet(() -> ReflectUtil.invoke(annotation.getAnnotation(), method, args));
}
// ========================= 代理方法 =========================
void loadMethods() {
// 非用户属性
methods.put("toString", (method, args) -> proxyToString());
methods.put("hashCode", (method, args) -> proxyHashCode());
methods.put("getSynthesizedAnnotation", (method, args) -> proxyGetSynthesizedAnnotation());
methods.put("getRoot", (method, args) -> annotation.getRoot());
methods.put("getVerticalDistance", (method, args) -> annotation.getVerticalDistance());
methods.put("getHorizontalDistance", (method, args) -> annotation.getHorizontalDistance());
methods.put("hasAttribute", (method, args) -> annotation.hasAttribute((String) args[0], (Class<?>) args[1]));
methods.put("getAttributes", (method, args) -> annotation.getAttributes());
methods.put("setAttribute", (method, args) -> {
throw new UnsupportedOperationException("proxied annotation can not reset attributes");
});
methods.put("getAttributeValue", (method, args) -> annotation.getAttributeValue((String) args[0]));
methods.put("annotationType", (method, args) -> annotation.annotationType());
// 可以被合成的用户属性
Stream.of(ClassUtil.getDeclaredMethods(annotation.getAnnotation().annotationType()))
.filter(m -> !methods.containsKey(m.getName()))
.forEach(m -> methods.put(m.getName(), (method, args) -> proxyAttributeValue(method)));
}
private String proxyToString() {
final String attributes = Stream.of(ClassUtil.getDeclaredMethods(annotation.getAnnotation().annotationType()))
.filter(AnnotationUtil::isAttributeMethod)
.map(method -> CharSequenceUtil.format(
"{}={}", method.getName(), proxyAttributeValue(method))
)
.collect(Collectors.joining(", "));
return CharSequenceUtil.format("@{}({})", annotation.annotationType().getName(), attributes);View on GitHub (pinned to 8870454b2a)
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.
Example fix
// before
Object proxy = synthesizer.synthesize(annotation);
((SynthesizedAnnotation) proxy).setAttribute("k", v); // throws
// after
SynthesizedAnnotation synth = synthesizer.getSynthesizedAnnotation(...);
synth.setAttribute("k", v);
Object proxy = synthesizer.synthesize(annotation); Defensive patterns
Strategy: validation
Validate before calling
if (proxy instanceof SynthesizedAnnotationProxy) throw new IllegalStateException("cannot setAttribute on immutable proxy"); Type guard
static boolean isMutableSynthesis(Object o) { return o instanceof SynthesizedAnnotation && !(o instanceof SynthesizedAnnotationProxy); } Try / catch
try { ((SynthesizedAnnotation)obj).setAttribute(k,v); } catch (UnsupportedOperationException e) { /* rebuild from underlying */ } Prevention
- Mutate the underlying SynthesizedAnnotation before proxying.
- Do not treat a synthesized proxy as a mutable annotation.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- No constructor provided
- No constructor provided
- Cache values Iterator is not support to modify.
- No method for alias: [{}]
- attribute [{}] cannot mirror for [{}], because it's already
AI-assisted analysis of chinabugotech/hutool@8870454b2a (2026-08-14).
Data as JSON: /api/errors/592abd8547e8ffb3.
Report an issue: GitHub.