spring-projects / spring-projects/spring-framework

`AbstractAdvisingBeanPostProcessor` should gracefully skip `final` classes instead of throwing `IllegalArgumentException`

Open
#36,353 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

status: waiting-for-triage
Dominant language
Java
Stars
60.2k
Forks
38.8k
Avg merge
5d 2h
Merged PRs (30d)
27

Description

When MethodValidationPostProcessor (which extends AbstractAdvisingBeanPostProcessor) encounters a bean whose class is final — such as a Java Record — it attempts to create a CGLIB proxy and fails with:

java.lang.IllegalArgumentException: Cannot subclass final class com.example.MyConfigurationProperties

With proxyTargetClass=false, it falls back to a JDK dynamic proxy, which then causes injection failures downstream when the bean is injected by concrete class type:

The bean 'my-config-com.example.MyConfigurationProperties' could not be injected because it is a JDK dynamic proxy
Use case

A third-party library defines a @ConfigurationProperties class as a Java Record implementing an interface with @NotNull constraints:

@ConfigurationProperties("my.config")
@Validated
public record MyConfigurationProperties(List<Entry> entries) implements MyInterface {
    // ...
}

A service in the same library injects this bean by concrete type:

public class MyService {
    public MyService(MyConfigurationProperties config) { /* ... */ }
}

The consuming application registers a MethodValidationPostProcessor bean. Because the record has @Validated, the post-processor attempts to create an AOP proxy — which is impossible for a final class regardless of the proxy strategy:

  • proxyTargetClass=trueIllegalArgumentException: Cannot subclass final class
  • proxyTargetClass=false → JDK proxy created, but injection by concrete type fails
Current workaround

Override postProcessAfterInitialization to skip final classes:

@Bean
public static MethodValidationPostProcessor methodValidationPostProcessor() {
    return new MethodValidationPostProcessor() {
        @Override
        public Object postProcessAfterInitialization(Object bean, String beanName) {
            if (Modifier.isFinal(bean.getClass().getModifiers())) {
                return bean;
            }
            return super.postProcessAfterInitialization(bean, beanName);
        }
    };
}
Suggested improvement

AbstractAdvisingBeanPostProcessor.postProcessAfterInitialization (or AopUtils) should detect final classes and gracefully skip proxy creation — logging a debug/warning message instead of throwing an exception. This is especially relevant given that:

  • Spring Boot recommends Java Records for @ConfigurationProperties since 3.x
  • @Validated on @ConfigurationProperties is a common and documented pattern
  • Kotlin classes are final by default
  • The consuming application often cannot modify library classes

Spring Framework version: 6.2.15 (Spring Boot 3.5.10)

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start at AbstractAdvisingBeanPostProcessor.postProcessAfterInitialization and inspect the related proxy checks in AopUtils. Reproduce the final-class case described with MethodValidationPostProcessor, then determine how final classes should be skipped without causing the CGLIB exception or an unusable JDK proxy; done means the behavior is covered by appropriate tests.

Written by the indexing model from the issue text.

Assessment

Tech stack
java, spring
Domain
backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
38/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.