spring-projects/spring-framework · error · AopConfigException

Could not generate CGLIB subclass of {advised.getTargetClass

Error message

Could not generate CGLIB subclass of {advised.getTargetClass()}: Common causes of this problem include using a final class or a non-visible class

What it means

Thrown by CglibAopProxy.buildProxy when the Enhancer raises CodeGenerationException or IllegalArgumentException during subclass generation. CGLIB creates a subclass of the target class, so it cannot subclass a final class, a class with no visible (non-private) constructor visible to the proxy, or classes the ClassLoader isolates. Spring wraps the cause and lists the two most common reasons in the message.

Source

Thrown at spring-aop/src/main/java/org/springframework/aop/framework/CglibAopProxy.java:236

			// fixedInterceptorMap only populated at this point, after getCallbacks call above
			ProxyCallbackFilter filter = new ProxyCallbackFilter(
					this.advised.getConfigurationOnlyCopy(), this.fixedInterceptorMap, this.fixedInterceptorOffset);
			enhancer.setCallbackFilter(filter);
			enhancer.setCallbackTypes(types);

			// Generate the proxy class and create a proxy instance.
			// ProxyCallbackFilter has method introspection capability with Advisor access.
			try {
				return (classOnly ? createProxyClass(enhancer) : createProxyClassAndInstance(enhancer, callbacks));
			}
			finally {
				// Reduce ProxyCallbackFilter to key-only state for its class cache role
				// in the CGLIB$CALLBACK_FILTER field, not leaking any Advisor state...
				filter.advised.reduceToAdvisorKey();
			}
		}
		catch (CodeGenerationException | IllegalArgumentException ex) {
			throw new AopConfigException("Could not generate CGLIB subclass of " + this.advised.getTargetClass() +
					": Common causes of this problem include using a final class or a non-visible class",
					ex);
		}
		catch (Throwable ex) {
			// TargetSource.getTarget() failed
			throw new AopConfigException("Unexpected AOP exception", ex);
		}
	}

	protected Class<?> createProxyClass(Enhancer enhancer) {
		enhancer.setInterceptDuringConstruction(false);
		return enhancer.createClass();
	}

	protected Object createProxyClassAndInstance(Enhancer enhancer, Callback[] callbacks) {
		enhancer.setInterceptDuringConstruction(false);
		enhancer.setCallbacks(callbacks);
		return (this.constructorArgs != null && this.constructorArgTypes != null ?

View on GitHub (pinned to e8729d0438)

Solutions

  1. Remove the final modifier from the target class (or mark Kotlin classes open / use the kotlin-spring/all-open plugin).
  2. If the class cannot be changed, switch to JDK dynamic proxies by having the target implement an interface and ensuring proxyTargetClass=false / @EnableAspectJAutoProxy(proxyTargetClass=false).
  3. Ensure the target class and its constructors are at least package-visible or public to Spring's classloader.
  4. Narrow the pointcut so it no longer matches the unproxyable final class.

Example fix

// before
@Service
@Transactional
public final class OrderService { ... }  // final -> CGLIB fails

// after
@Service
@Transactional
public class OrderService { ... }  // open for subclassing
Defensive patterns

Strategy: type-guard

Validate before calling

Class<?> targetClass = advised.getTargetClass();
int mods = targetClass.getModifiers();
if (java.lang.reflect.Modifier.isFinal(mods)) {
  throw new IllegalStateException(targetClass + " is final; remove final or switch to JDK interface proxy");
}

Type guard

static boolean isCglibProxyable(Class<?> c) {
  int m = c.getModifiers();
  return !java.lang.reflect.Modifier.isFinal(m)
      && java.lang.reflect.Modifier.isPublic(m) || c.getDeclaringClass() == null
      && java.util.Arrays.stream(c.getDeclaredConstructors())
          .anyMatch(ctor -> java.lang.reflect.Modifier.isPublic(ctor.getModifiers())
                         || java.lang.reflect.Modifier.isProtected(ctor.getModifiers()));
}

Try / catch

try {
  return pf.getProxy();
} catch (AopConfigException ex) {
  if (ex.getMessage().contains("Could not generate CGLIB subclass")) {
    // advise user: remove final or supply interfaces and use JDK proxy
  }
  throw ex;
}

Prevention

When it happens

Trigger: Proxying a final class with proxyTargetClass=true (or when no interfaces are supplied); proxying a class with only private constructors; a class not visible to Spring's classloader (module visibility / non-public class in another package); missing CGLIB on the classpath in older setups.

Common situations: Annotating a final class or Kotlin class not marked open with @Service/@Transactional and using CGLIB (default since Spring Boot 2.x); sealed classes (Java 17); third-party final utility classes being proxied accidentally by over-broad pointcuts; GraalVM native image CGLIB constraints.

Related errors


AI-assisted analysis of spring-projects/spring-framework@e8729d0438 (2026-08-04). Data as JSON: /data/errors/4cea8327a8463e5f.json. Report an issue: GitHub.