skip to content

You have a bean MyServiceImpl implements MyService, and Spring AOP proxies it. Why does @Autowired MyServiceImpl (by concrete class) fail, and how do you fix it?

level: middleimportance: must knowfreq 65%

answer

  1. proxy implements interface, extends Proxy
  2. not instanceof MyServiceImpl
  3. NoSuchBeanDefinition / ClassCastException
  4. inject the interface
  5. proxyTargetClass=true for CGLIB

basics

~20 s

The JDK proxy implements the interface MyService, not the class MyServiceImpl, so it isn't a MyServiceImpl. Injecting by the concrete class finds no matching bean (or a class-cast failure). Fix it by injecting the interface MyService.

solid answer

~40 s

When Spring AOP applies a JDK dynamic proxy, the bean the container hands out is a runtime proxy that implements MyService but does NOT extend MyServiceImpl. So it's assignable to the interface but not to the concrete class. Autowiring by concrete type MyServiceImpl then fails — the proxy isn't of that type, so no matching candidate is found (you typically get a NoSuchBeanDefinitionException, or a ClassCastException if you cast). The correct fix is to depend on the interface: @Autowired MyService. That's the whole point of interface-based proxying and 'program to interfaces.' If you genuinely must inject the concrete class, you can force CGLIB proxying with @EnableAspectJAutoProxy(proxyTargetClass = true), since a CGLIB proxy is a subclass of MyServiceImpl and therefore assignable to it — but injecting the interface is the idiomatic answer.

code

java · 12 lines
java
// FAILS: proxy is not a MyServiceImpl
@Autowired
private MyServiceImpl service;      // NoSuchBeanDefinitionException

// WORKS: proxy implements MyService
@Autowired
private MyService service;          // gets the JDK dynamic proxy

// If you MUST have the concrete type, force CGLIB globally:
@Configuration
@EnableAspectJAutoProxy(proxyTargetClass = true)
class AopConfig { }

go deeper

for a junior

Recognize that injecting the interface is the safe choice.

for a middle

Explain the type-assignability reason and the exceptions you'd see.

for a senior

Contrast with CGLIB assignability and the proxyTargetClass escape hatch.

for a principal

Weigh interface discipline vs forcing CGLIB across the codebase and its side effects.

## The setup ``` interface MyService { void doWork(); } @Service class MyServiceImpl implements MyService { ... } ``` Suppose an aspect (e.g. `@Transactional` or a custom `@Aspect`) applies to `MyServiceImpl`. Spring AOP therefore wraps the real bean in a **JDK dynamic proxy**. ## Why concrete-type injection fails The JDK proxy is generated by `java.lang.reflect.Proxy`. Its class: - **extends** `java.lang.reflect.Proxy` (fixed superclass), and - **implements** `MyService` (the interface you asked for). Critically, it does **not** extend `MyServiceImpl`. In Java's type system the proxy `instanceof MyService` is `true`, but `instanceof MyServiceImpl` is `false`. So: ``` @Autowired MyServiceImpl svc; // no bean matches this concrete type ``` At injection time Spring looks for a bean assignable to `MyServiceImpl`. The only candidate — the proxy — is **not** assignable, so you get a **`NoSuchBeanDefinitionException`** ("expected at least 1 bean"). If instead you obtain the bean as `Object` and cast, `(MyServiceImpl) bean` throws a **`ClassCastException`**. ## The fix: program to interfaces ``` @Autowired MyService svc; // proxy IS a MyService -> works ``` Injecting the interface is idiomatic and always works whether the bean is proxied or not, JDK or CGLIB. ## Alternative: force CGLIB If you truly need the concrete type (legacy code, no interface method you can move), switch to subclass-based proxying: ``` @EnableAspectJAutoProxy(proxyTargetClass = true) // or property: spring.aop.proxy-target-class=true ``` A **CGLIB** proxy *extends* `MyServiceImpl`, so it **is** assignable to it and concrete-type injection succeeds. Note this is a global switch and has its own caveats (final methods/classes can't be proxied, constructor runs, etc.). ## Related gotchas - The same reason explains why you can't inject a JDK-proxied bean into a field typed as the impl and then call impl-only public methods — those methods aren't on the interface, so they're not reachable through the proxy at all. - `AopUtils.isJdkDynamicProxy(bean)` / `AopProxyUtils.ultimateTargetClass(bean)` help you inspect what you actually got. ## Bottom line JDK proxy = interface type only. Depend on the interface, or force CGLIB if you must have the class.

  • With proxyTargetClass=true, why does injecting the concrete class now work?
    CGLIB generates a runtime subclass of MyServiceImpl, so the proxy IS-A MyServiceImpl and is assignable to that type.
  • A public method exists on MyServiceImpl but not on MyService. Is it advised by the JDK proxy?
    No. Only interface-declared methods are exposed by the JDK proxy, so a class-only method can't be intercepted (and can't be called through the interface reference).

saying these in an interview costs you the question

  • Saying the JDK proxy is a subclass of MyServiceImpl.
  • Claiming @Autowired matches by name so concrete type is fine.
  • Thinking you can cast the proxy to MyServiceImpl safely.

context