skip to content

What kind of proxy does Spring Data create for a repository, and which class actually builds it?

level: middleimportance: should knowfreq 55%

answer

  1. Interface target => JDK dynamic proxy
  2. RepositoryFactorySupport / JpaRepositoryFactory
  3. ProxyFactory + advisors
  4. QueryExecutorMethodInterceptor is the core advice
  5. Not CGLIB (no subclassing needed)

basics

~20 s

A JDK dynamic proxy — because repositories are interfaces, no subclassing (CGLIB) is needed. The proxy is assembled by RepositoryFactorySupport (the JPA subclass is JpaRepositoryFactory) using Spring AOP's ProxyFactory, which adds interceptors before creating the interface-based proxy.

solid answer

~40 s

Because a repository is always an interface, Spring Data uses a **JDK dynamic proxy** (interface-based), not a CGLIB class proxy. The builder is `RepositoryFactorySupport`, with JPA's concrete subclass `JpaRepositoryFactory`. In `getRepository(...)` it creates the base target (a `SimpleJpaRepository` instance / repository fragments), then uses Spring AOP's `ProxyFactory` to assemble the proxy: it runs registered `RepositoryProxyPostProcessor`s to add advice (transactions, exception translation), adds the core `QueryExecutorMethodInterceptor`, and finally `getProxy()` returns a JDK proxy implementing your interface. So the proxy is Spring AOP-based under the hood, and since the target type is an interface, the JDK `Proxy`/`InvocationHandler` mechanism is used rather than bytecode subclassing.

code

java · 25 lines
java
// Conceptual sketch of what RepositoryFactorySupport#getRepository does.
// (Real code lives in org.springframework.data.repository.core.support.RepositoryFactorySupport)

public <T> T getRepository(Class<T> repositoryInterface,
                           RepositoryComposition.RepositoryFragments fragments) {

    RepositoryInformation information = getRepositoryInformation(/* metadata */);

    // 1. Base implementation target (SimpleJpaRepository) + custom fragments
    Object target = getTargetRepository(information);

    // 2. Spring AOP proxy around the interface
    ProxyFactory result = new ProxyFactory();
    result.setTarget(target);
    result.setInterfaces(repositoryInterface, Repository.class, TransactionalProxy.class);

    // 3. Let post-processors add advice (transactions, exception translation, ...)
    postProcessors.forEach(pp -> pp.postProcess(result, information));

    // 4. Core dispatcher: routes each call to query/base/custom/default
    result.addAdvice(new QueryExecutorMethodInterceptor(information, /* ... */));

    // 5. Interface target -> JDK dynamic proxy is produced
    return (T) result.getProxy(classLoader);
}

go deeper

for a junior

Enough to know a proxy is generated; the JDK-vs-CGLIB detail is a stretch.

for a middle

Should state JDK dynamic proxy + name RepositoryFactorySupport/ProxyFactory.

for a senior

Explains the ProxyFactory assembly with post-processors and QueryExecutorMethodInterceptor.

for a principal

Ties it to Spring AOP internals and why interface-based proxying is the correct default.

**Two proxy technologies in the Spring world.** - *JDK dynamic proxy*: built into the JVM (`java.lang.reflect.Proxy` + `InvocationHandler`). It can only proxy *interfaces* — it creates a class implementing the given interfaces and routes every call through one `invoke` method. - *CGLIB proxy*: generates a *subclass* of a concrete class at runtime. Needed when you must proxy a class that has no interface. **Why Spring Data uses JDK proxies.** A Spring Data repository is *by definition* an interface (`UserRepository extends JpaRepository<…>`). Since there is a clean interface to implement, the JDK dynamic-proxy mechanism is the natural fit and CGLIB subclassing is unnecessary. This is why repository proxies are JDK-based. **Who builds it — the class chain.** - `RepositoryFactorySupport` is the abstract engine in `spring-data-commons` that produces repository instances. Its central method is `getRepository(Class<T> repositoryInterface, RepositoryComposition.RepositoryFragments fragments)`. - `JpaRepositoryFactory` (in `spring-data-jpa`) is the concrete subclass that knows how to build the JPA base implementation (`SimpleJpaRepository`) and JPA query methods. **Inside getRepository (conceptually):** 1. Build **RepositoryInformation / metadata** — inspect the interface to learn the domain type, id type, which methods are base (CRUD) vs. custom vs. query methods. 2. Create the **target**: the base implementation instance (`SimpleJpaRepository`) plus any custom fragments (your `SomethingRepositoryImpl`) combined into a `RepositoryComposition`. 3. Create a Spring AOP **`ProxyFactory`** around that target, exposing the repository interface (and `Repository`, `TransactionalProxy`, etc.). 4. Run each registered **`RepositoryProxyPostProcessor`** — these add AOP `Advice`/`Advisor`s (e.g. transaction interception, JPA exception translation, method-metadata exposure). 5. Add the **`QueryExecutorMethodInterceptor`** — the advice that, at call time, decides whether an invoked method is a store query method, a custom fragment method, a base method, or a default method, and dispatches accordingly. 6. Call `proxyFactory.getProxy(classLoader)` → returns the **JDK dynamic proxy** implementing your interface. **Terms.** - *Advice / Advisor / interceptor*: Spring AOP building blocks that wrap method calls to add cross-cutting behavior. - *ProxyFactory*: Spring AOP's programmatic API to build a proxy around a target with a set of advisors. - *RepositoryComposition / fragment*: the model (since Spring Data 2.0) where a repository is a composition of the base implementation plus custom implementation fragments plus interface default methods. **Gotchas / edge cases.** - Even though it is a JDK proxy, it is a *Spring AOP* proxy, so it participates in the AOP infrastructure (that's how `@Transactional` semantics get layered in via a post-processor). - People sometimes assume CGLIB is involved because Spring uses CGLIB elsewhere (e.g. `@Configuration` classes, proxying concrete beans). For repositories it is JDK because the target is an interface. - The base `SimpleJpaRepository` bean is *not* itself a Spring-managed singleton you can inject; it is instantiated by the factory as the proxy's target.

  • Why JDK dynamic proxy and not CGLIB here, when Spring uses CGLIB for @Configuration classes?
    CGLIB subclasses a concrete class and is used when there is no interface to implement. A repository is always an interface, so the JDK Proxy/InvocationHandler mechanism suffices and is preferred; no bytecode subclassing is required.
  • Is the repository proxy a Spring AOP proxy or a raw JDK proxy?
    It is a Spring AOP proxy built via ProxyFactory; because the target is an interface, Spring AOP realizes it using the JDK dynamic-proxy backend. That's how advice like transaction interception gets layered in.

saying these in an interview costs you the question

  • Claiming repositories use CGLIB subclass proxies
  • Saying the proxy contains hand-generated method bodies rather than dispatching via an interceptor
  • Thinking RepositoryFactoryBean itself builds the proxy (it delegates to RepositoryFactorySupport)

context