Bagaimana sebenarnya repositori Spring Data diimplementasikan?

111

Saya telah bekerja dengan repositori JPA Spring Data dalam proyek saya selama beberapa waktu dan saya tahu poin-poin di bawah ini:

  • Di antarmuka repositori, kita dapat menambahkan metode seperti findByCustomerNameAndPhone()(asumsi customerNamedanphone bidang di objek domain).
  • Kemudian, Spring menyediakan implementasi dengan mengimplementasikan metode antarmuka repositori di atas pada waktu proses (selama aplikasi dijalankan).

Saya tertarik tentang bagaimana ini telah dikodekan dan saya telah melihat kode sumber & API Spring JPA, tetapi saya tidak dapat menemukan jawaban untuk pertanyaan di bawah ini:

  1. Bagaimana kelas implementasi repositori yang dihasilkan saat runtime & metode diimplementasikan dan dimasukkan?
  2. Apakah Spring Data JPA menggunakan CGlib atau library manipulasi bytecode apa pun untuk mengimplementasikan metode dan menyuntikkan secara dinamis?

Bisakah Anda membantu dengan pertanyaan di atas dan juga memberikan dokumentasi yang didukung?

pengembang
sumber

Jawaban:

144

Pertama-tama, tidak ada pembuatan kode yang terjadi, yang berarti: tidak ada CGLib, tidak ada pembuatan kode byte sama sekali. Pendekatan dasarnya adalah bahwa instance proxy JDK dibuat secara terprogram menggunakan ProxyFactoryAPI Spring untuk mendukung antarmuka dan MethodInterceptormemotong semua panggilan ke instance tersebut dan mengarahkan metode ke tempat yang sesuai:

  1. Jika repositori telah diinisialisasi dengan bagian implementasi kustom (lihat bagian dokumentasi referensi itu untuk detailnya), dan metode yang dipanggil diimplementasikan di kelas itu, panggilan akan diarahkan ke sana.
  2. Jika metode tersebut adalah metode kueri (lihat DefaultRepositoryInformationbagaimana itu ditentukan), mekanisme eksekusi kueri khusus penyimpanan akan menjalankan dan mengeksekusi kueri yang ditentukan untuk dieksekusi untuk metode itu saat memulai. Untuk itu mekanisme resolusi ada di tempat yang mencoba untuk mengidentifikasi kueri yang dideklarasikan secara eksplisit di berbagai tempat (menggunakan @Querymetode, JPA bernama kueri) yang akhirnya jatuh kembali ke derivasi kueri dari nama metode. Untuk deteksi mekanisme kueri, lihat JpaQueryLookupStrategy. Logika parsing untuk derivasi kueri dapat ditemukan di PartTree. Terjemahan khusus toko ke dalam kueri aktual dapat dilihat, misalnya di JpaQueryCreator.
  3. Jika tidak ada di atas yang menerapkan metode yang dijalankan harus diterapkan oleh kelas basis repositori khusus toko ( SimpleJpaRepositorydalam kasus JPA) dan panggilan dialihkan ke instance dari itu.

Metode interceptor yang mengimplementasikan logika routing QueryExecutorMethodInterceptor, logika routing tingkat tinggi dapat ditemukan di sini .

Pembuatan proxy tersebut dienkapsulasi menjadi implementasi pola Pabrik berbasis Java standar. Pembuatan proxy tingkat tinggi dapat ditemukan di RepositoryFactorySupport. Implementasi khusus toko kemudian menambahkan komponen infrastruktur yang diperlukan sehingga untuk JPA Anda dapat melanjutkan dan cukup menulis kode seperti ini:

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

Alasan saya menyebutkannya secara eksplisit adalah bahwa itu harus menjadi jelas bahwa, pada intinya, tidak ada dari kode itu yang membutuhkan wadah Spring untuk berjalan di tempat pertama. Perlu Spring sebagai pustaka di classpath (karena kami lebih suka untuk tidak menemukan kembali roda), tetapi container agnostik secara umum.

Untuk memudahkan integrasi dengan kontainer DI, kami tentunya telah membangun integrasi dengan konfigurasi Spring Java, namespace XML, tetapi juga ekstensi CDI , sehingga Spring Data dapat digunakan dalam skenario CDI biasa.

Oliver Drotbohm
sumber
3
Hai Oliver, bisakah Anda menjelaskan tentang bagaimana spring menemukan @Repositoryantarmuka beranotasi di tempat pertama? Melihat RepositoryFactorySupport#getRepository()pertunjukan yang mengambil kelas antarmuka sebagai parameter, jadi itu harus ditemukan di tempat lain. Saya secara khusus mencoba mencari cara untuk menemukan antarmuka beranotasi dan secara otomatis menghasilkan kacang proxy JDK yang mengimplementasikan antarmuka, sangat mirip dengan data musim semi, tetapi untuk tujuan khusus aplikasi yang tidak terkait dengan Repositori.
Chris Rice
1
Anda mungkin ingin melihatnya RepositoryComponentProvider. Tidak ada hal otomatis yang terjadi kecuali komponen memindai jenis tertentu (baik yang diberi anotasi atau membawa anotasi) dan FactoryBeandikonfigurasi untuk masing-masing jenis tersebut.
Oliver Drotbohm
2
Maaf mengomentari utas lama, tapi penasaran ... Apakah repositori proxy objek tunggal? Kami melihat masalah saat kode kami mencoba memanggil metode repo, tetapi tampaknya tidak pernah dapat melakukan panggilan di proxy. Itu hanya hang. Aku ingin tahu apakah menunggu seorang lajang yang sibuk.
iu.david