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

  1. Mutate the underlying SynthesizedAnnotation (annotation.setAttribute) before proxying, not the proxy itself.
  2. If you need a modified annotation, build a new synthesized annotation with the changed value rather than calling setAttribute on the proxy.
  3. 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

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


AI-assisted analysis of chinabugotech/hutool@8870454b2a (2026-08-14). Data as JSON: /api/errors/592abd8547e8ffb3. Report an issue: GitHub.