How are Spring Data repositories actually implemented?

developer picture developer · Jul 21, 2016 · Viewed 27.4k times · Source

I have been working with Spring Data JPA repository in my project for some time and I know the below points:

  • In the repository interfaces, we can add the methods like findByCustomerNameAndPhone() (assuming customerName and phone are fields in the domain object).
  • Then, Spring provides the implementation by implementing the above repository interface methods at runtime (during the application run).

I am interested on how this has been coded and I have looked at the Spring JPA source code & APIs, but I could not find answers to the questions below:

  1. How is the repository implementation class generated at runtime & methods being implemented and injected?
  2. Does Spring Data JPA use CGlib or any bytecode manipulation libraries to implement the methods and inject dynamically?

Could you please help with the above queries and also provide any supported documentation ?

Answer

Oliver Drotbohm picture Oliver Drotbohm · Jul 21, 2016

First of all, there's no code generation going on, which means: no CGLib, no byte-code generation at all. The fundamental approach is that a JDK proxy instance is created programmatically using Spring's ProxyFactory API to back the interface and a MethodInterceptor intercepts all calls to the instance and routes the method into the appropriate places:

  1. If the repository has been initialized with a custom implementation part (see that part of the reference documentation for details), and the method invoked is implemented in that class, the call is routed there.
  2. If the method is a query method (see DefaultRepositoryInformation for how that is determined), the store specific query execution mechanism kicks in and executes the query determined to be executed for that method at startup. For that a resolution mechanism is in place that tries to identify explicitly declared queries in various places (using @Query on the method, JPA named queries) eventually falling back to query derivation from the method name. For the query mechanism detection, see JpaQueryLookupStrategy. The parsing logic for the query derivation can be found in PartTree. The store specific translation into an actual query can be seen e.g. in JpaQueryCreator.
  3. If none of the above apply the method executed has to be one implemented by a store-specific repository base class (SimpleJpaRepository in case of JPA) and the call is routed into an instance of that.

The method interceptor implementing that routing logic is QueryExecutorMethodInterceptor, the high level routing logic can be found here.

The creation of those proxies is encapsulated into a standard Java based Factory pattern implementation. The high-level proxy creation can be found in RepositoryFactorySupport. The store-specific implementations then add the necessary infrastructure components so that for JPA you can go ahead and just write code like this:

EntityManager em = … // obtain an EntityManager
JpaRepositoryFactory factory = new JpaRepositoryFactory(em);
UserRepository repository = factory.getRepository(UserRepository.class);

The reason I mention that explicitly is that it should become clear that, in its core, nothing of that code requires a Spring container to run in the first place. It needs Spring as a library on the classpath (because we prefer to not reinvent the wheel), but is container agnostic in general.

To ease the integration with DI containers we've of course then built integration with Spring Java configuration, an XML namespace, but also a CDI extension, so that Spring Data can be used in plain CDI scenarios.