How Spring Data Generates Repository Implementations
Fast recall
Quick revision
Core answer
CrudRepository/JpaRepository, builds a runtime proxy for each (backed by `SimpleJpaRepository`), and registers it as a bean. There are no generated `.class` files — it's pure in-memory proxying. Derived method names (e.g. findByEmail) are parsed into queries at startup; the proxy holds an EntityManager to run them.Whether you understand Spring's runtime proxy mechanism rather than imagining compile-time code generation
Believing Spring generates .java/.class files at build time, or writing a full Impl class for the whole interface
Name SimpleJpaRepository as the backing class, @EnableJpaRepositories as the trigger, and the proxy self-invocation caveat for @Transactional
Interview-ready guide
Interview answer
Spring Data JPA generates repository implementations at runtime using proxies.
When the app starts, it scans for any interface that extends CrudRepository or JpaRepository. For each one, Spring creates a proxy object in memory that implements my interface, and that proxy delegates all calls to a class called SimpleJpaRepository, which contains the real database logic using Hibernate and JPA. The proxy is then registered as a bean so I can inject it anywhere.
It's important that this happens at runtime, not during compilation — Spring doesn't write any Java or class files into my target folder. The whole thing is triggered by the EnableJpaRepositories annotation, which Spring Boot auto-configures. When I call repository dot save, I'm really calling a proxy method that forwards to SimpleJpaRepository dot save, which talks to the EntityManager to run the SQL.
For derived queries, Spring parses the method name at startup — so findByEmailAndStatus becomes a JPQL query — and it validates the field names against my entity, which is why a typo in the method name fails fast at startup. The common misconception is that I need to write the implementation class myself. I don't.
The only time I write implementation code is for a custom method Spring can't derive, and even then I implement just that one method in a separate fragment, not the whole interface. One caveat: because transactions also go through the proxy, calling a transactional repository or service method from inside the same class bypasses the proxy.
How Spring Data Generates Repository Implementations
Direct Answer
At startup, Spring Data scans interfaces extending CrudRepository/JpaRepository, builds a runtime proxy for each (backed by SimpleJpaRepository), and registers it as a bean. There are no generated .class files — it's pure in-memory proxying. Derived method names (e.g. findByEmail) are parsed into queries at startup; the proxy holds an EntityManager to run them.
Deep Dive & Architecture
CrudRepository<T, ID> is Spring Data's base interface providing the core CRUD methods: save(), findById(), findAll(), count(), delete(), and existsById().
You only declare the interface with your entity type T and primary-key type ID — Spring generates the implementation at runtime (a dynamic proxy backed by SimpleJpaRepository), so there is no DAO boilerplate.
Note the return types: findById() returns Optional<T> and findAll() returns Iterable<T>, not List<T>.
Writing a CustomerRepositoryImpl class by hand (causing a bean conflict), confusing CrudRepository with JpaRepository, or assuming findAll returns a List
Code Example
// You write just this:
public interface OrderRepository extends JpaRepository<Order, Long> {
List<Order> findByStatus(String status); // parsed into JPQL at startup
}
@Service
public class OrderService {
private final OrderRepository repository; // injected proxy bean
OrderService(OrderRepository r) { this.repository = r; }
public Order place(Order o) {
return repository.save(o); // proxy -> SimpleJpaRepository.save() -> EntityManager
}
}
// No OrderRepositoryImpl exists — check target/classes, there's nothing there.
Common Mistakes & Pitfalls
- Writing a full
Implclass —OrderRepositoryImplfor the whole interface causes bean conflicts; implement only custom fragments. - Expecting compile-time code — no
.java/.classfiles are generated; it's runtime proxies. - Self-invocation surprises —
@Transactionalworks through the same proxy, so same-class internal calls bypass it. - Mistyping a derived method —
findByEmialfails at startup; Spring validates names against entity fields, so you catch the typo immediately.
Check your understanding
Answer these without looking back at the article.
- What is CrudRepository in Spring Data and what methods does it provide?
- What does findById return and why does it return Optional?
- What is the difference between CrudRepository, JpaRepository, and PagingAndSortingRepository?
Looking for a structured roadmap?
Every module on this page is also part of the full Java backend interview track.