skip to content

What is @NoRepositoryBean and when must you use it?

level: seniorimportance: must knowfreq 50%

answer

  1. skip proxy creation for this interface
  2. put on shared/generic base interfaces
  3. framework base interfaces all use it
  4. NOT inherited — only the annotated interface
  5. never on concrete repos (breaks injection)

basics

~20 s

@NoRepositoryBean marks a repository interface as a base/intermediate interface that Spring Data should NOT create a bean/proxy for. You put it on shared base interfaces you extend but never use directly, so Spring doesn't try to instantiate them.

solid answer

~50 s

@NoRepositoryBean tells Spring Data's repository scanning to skip proxy creation for a given repository interface. Spring Data creates a bean for every interface extending Repository — but sometimes you define an intermediate base interface that other repositories extend (e.g. a MyBaseRepository<T, ID> exposing shared custom methods across all your repositories). That base isn't tied to a concrete entity and should never be instantiated on its own. Without @NoRepositoryBean, Spring Data would try to create a proxy for it and either fail or register a useless/ambiguous bean. Annotating the base with @NoRepositoryBean excludes it from instantiation while still letting concrete sub-interfaces inherit its methods. This is exactly why the framework's own base interfaces — Repository, CrudRepository, PagingAndSortingRepository, JpaRepository — are all annotated @NoRepositoryBean. It is not inherited: only the specific interface it's placed on is skipped.

code

java · 17 lines
java
// Shared base — extended by all repos, never instantiated itself.
@NoRepositoryBean
public interface BaseRepository<T, ID> extends JpaRepository<T, ID> {
    default T getByIdOrThrow(ID id) {
        return findById(id).orElseThrow(() ->
                new EntityNotFoundException("Not found: " + id));
    }
}

// Concrete repository — NO @NoRepositoryBean; this one gets a proxy bean.
public interface UserRepository extends BaseRepository<User, Long> {
    Optional<User> findByEmail(String email);
}

// Injected normally; getByIdOrThrow + findByEmail both available.
// If BaseRepository lacked @NoRepositoryBean, startup would fail trying
// to build a proxy for the generic, entity-less base interface.

go deeper

for a junior

Know it stops Spring from creating a bean for a base interface you only extend.

for a middle

Explain the custom-base-repository use case and that framework interfaces like CrudRepository use it.

for a senior

Know it's not inherited, the failure modes of misuse, and pairing it with repositoryBaseClass for shared implementations.

for a principal

Design cross-cutting repository behavior (soft delete, auditing, tenant scoping) via a @NoRepositoryBean base plus custom base class, weighing it against fragment interfaces.

## The mechanism it controls Spring Data's repository infrastructure scans for interfaces extending `Repository` and creates a **proxy bean** for each one. That's usually what you want — one bean per concrete repository. But the hierarchy also contains **intermediate base interfaces** that must **not** become beans. **`@NoRepositoryBean`** (in `org.springframework.data.repository`) is a marker annotation placed on a repository interface to tell the scanner: *do not create an instance/proxy for this exact interface.* ## The classic use case: a custom base repository Suppose you want a method available on **every** repository in your app — say `Optional<T> findByIdOrThrow(ID id)` or a soft-delete helper. You create a generic base interface and put your shared methods there, then have each concrete repository extend it: ```java @NoRepositoryBean public interface BaseRepository<T, ID> extends JpaRepository<T, ID> { T getByIdOrThrow(ID id); } ``` This interface: - is **generic** (`T`, `ID` are unbound) — it isn't tied to any concrete entity, so Spring Data can't (and shouldn't) build a proxy for it; - is meant to be **extended, not used directly**. Without the annotation, the scanner sees `BaseRepository extends Repository` and tries to instantiate it, causing a startup error (it has no domain type to bind) or a spurious bean. `@NoRepositoryBean` cleanly excludes it. Concrete children (`UserRepository extends BaseRepository<User, Long>`) still inherit `getByIdOrThrow` and *do* get proxied. You'd typically pair this with a custom base **implementation** class registered via `repositoryBaseClass` on `@EnableJpaRepositories`. ## Why the framework's own interfaces use it Every shared interface in the hierarchy is annotated `@NoRepositoryBean`: `Repository`, `CrudRepository`, `ListCrudRepository`, `PagingAndSortingRepository`, `ListPagingAndSortingRepository`, `JpaRepository`, `SimpleJpaRepository`'s interface, etc. Otherwise Spring Data would try to instantiate `CrudRepository` itself. ## Key semantics & gotchas - **Not inherited**: `@NoRepositoryBean` applies **only** to the exact interface it annotates. A sub-interface without the annotation is still eligible for proxying — which is precisely what you want (base excluded, concrete children included). Never put it on your concrete `UserRepository` or you'll get no bean and an autowiring failure. - **Symptom of misuse**: If you forget it on a base interface, you may see `BeanCreationException` / errors about not being able to determine the domain type, or an unexpected extra repository bean. - **Alternative for the opposite direction**: `@RepositoryDefinition` lets an interface that does *not* extend `Repository` still be treated as a repository — the inverse tool. - It changes **nothing at runtime behavior** for the concrete repositories; it's purely a scan-time exclusion flag. ## When to use - On any generic/shared intermediate repository interface you introduce to centralize methods or plug in a custom base implementation. - Never on a concrete, entity-bound repository you actually inject.

  • Is @NoRepositoryBean inherited by sub-interfaces?
    No — it applies only to the exact interface it annotates. That's intentional: the generic base is excluded while concrete children (without the annotation) are still proxied and injectable.
  • What happens if you accidentally put @NoRepositoryBean on your concrete UserRepository?
    Spring Data won't create a bean for it, so any attempt to @Autowire UserRepository fails with a NoSuchBeanDefinitionException / unsatisfied-dependency error at startup.
  • How do you add a shared custom implementation behind such a base interface?
    Provide a base implementation class extending SimpleJpaRepository and register it via repositoryBaseClass on @EnableJpaRepositories, so all repositories inherit the behavior.

saying these in an interview costs you the question

  • Saying it's inherited by all sub-interfaces
  • Putting it on concrete repositories
  • Claiming it changes runtime CRUD behavior rather than scan-time proxy creation
  • Confusing it with @RepositoryDefinition (the inverse)

context