๐ด The Error You're Seeing
Confirm this matches your console output. If it does, you're in the right place.
java.lang.ClassCastException: class com.sun.proxy.$Proxy123 cannot be cast to class com.devinhyderabad.service.MyServiceImpl
at com.devinhyderabad.controller.MyController.handleRequest(MyController.java:20)โก Quick Fix Works 80% of the time
Inject the interface, not the concrete class, or enforce CGLIB proxies.
// Bad: Injecting concrete class
@Autowired
private MyServiceImpl myService;
// Good: Injecting interface
@Autowired
private MyService myService;๐ง Why this Happens
Tap to expand the deep technical explanation
Spring uses AOP proxies to apply aspects like @Transactional or @Async. By default, it uses JDK Dynamic Proxies, which implement the interface but don't extend the concrete class. When you try to cast the proxy to your concrete implementation, Java throws a ClassCastException.
The HITEC City Parking Spot Analogy:
Imagine a stunt double (Proxy) for an actor. The stunt double can do the action scenes (Interface methods), but if the director tries to interview the stunt double as if they are the actual actor (Concrete Class), the director is making a mistake.
๐ How to Reproduce Confirm this is your error
Inject the concrete implementation class (MyServiceImpl) instead of its interface into a controller while Spring AOP proxies the bean. Casting $Proxy to the class throws ClassCastException.
๐ ๏ธ Solutions (3 Ways to Fix)
Inject the Interface, not the Implementation
๐ Use this when your class implements an interface.
By default, Spring uses JDK Dynamic Proxies, which create a proxy that implements the interface, but does NOT extend the concrete class. You must inject the interface type.
@Service
public class MyServiceImpl implements MyService { ... }
@RestController
public class MyController {
@Autowired
private MyService myService; // Inject interface, not MyServiceImpl
}Enable CGLIB Proxies
๐ Use this if you must inject the concrete class.
CGLIB creates a proxy by subclassing the concrete class, allowing you to cast to the implementation.
# application.properties
# Spring Boot 2.x:
spring.aop.proxy-target-class=true
# Spring Boot 3.x (enabled by default, but if disabled):
spring.aop.proxy-target-class=trueAvoid self-invocation
๐ Use this if you are calling a method on 'this' instead of the proxy.
If you call this.method(), you bypass the proxy entirely. If you try to cast 'this' to a proxied interface, it fails.
// Inject the bean into itself to call via proxy
@Autowired
@Lazy
private MyService self;
public void doWork() {
self.otherMethod(); // Calls proxy
}๐ Version Notes
JDK proxies by default unless proxy-target-class=true.
CGLIB proxies are the default. But JDK proxies are used if an interface is present and CGLIB is disabled.
๐ก๏ธ How to Prevent This Next Time
Always program to interfaces. Inject interfaces, not concrete implementations. This makes your code decoupled and proxy-safe.