... keempatnya sedang debugging.
Terinspirasi oleh laporan oleh Vladimir Plizgi ( Spring Boot 2: apa yang tidak mereka tulis dalam catatan rilis ), saya memutuskan untuk berbicara tentang pengalaman saya dengan Spring Booth, fitur dan perangkap yang saya temui dalam perjalanan.
Data pegas jpa
Laporan Vladimir dikhususkan untuk migrasi ke Spring Booth 2 dan kesulitan yang muncul. Pada bagian pertama, ia menjelaskan kesalahan kompilasi, jadi saya juga akan memulainya.
Saya pikir banyak yang akrab dengan kerangka kerja Data Spring dan turunannya untuk berbagai gudang data. Saya bekerja dengan hanya satu dari mereka - Spring Data JPA. Antarmuka utama yang digunakan dalam kerangka kerja ini - JpaRepository
, telah mengalami perubahan signifikan dalam versi 2. *.
Pertimbangkan metode yang paling sering digunakan:
Bahkan, ada lebih banyak perubahan, dan setelah pindah ke versi 2. * semuanya akan berubah menjadi kesalahan kompilasi.
Ketika salah satu proyek kami mengangkat masalah migrasi ke Spring Booth 2 dan langkah-langkah pertama diambil (mengganti versi di pom.xml), seluruh proyek benar-benar berubah menjadi merah: puluhan repositori dan ratusan panggilan ke metode mereka menyebabkan migrasi "(menggunakan metode baru) akan mengaitkan setiap file kedua. Bayangkan kode paling sederhana:
SomeEntity something = someRepository.findOne(someId); if (something == null) { something = someRepository.save(new SomeEntity()); } useSomething(something);
Agar proyek hanya berkumpul, Anda perlu:
- panggil metode lain
- ubah jenis variabel
something
ke Optional<SomeEntity>
- ganti memeriksa referensi kosong dengan
Optional::isPresent
atau Optional::orElse
/ Optional::orElseGet
- memperbaiki tes
Untungnya, ada cara yang lebih sederhana, karena Spring Date memberi pengguna kesempatan yang belum pernah terjadi sebelumnya untuk menyesuaikan repositori dengan kebutuhan mereka. Yang kita butuhkan adalah mendefinisikan (jika belum didefinisikan) antarmuka dan kelas yang akan membentuk dasar dari semua repositori proyek kami. Ini dilakukan seperti ini:
Semua repositori kami akan mewarisi dari antarmuka ini. Sekarang implementasi:
public class BaseJpaRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements BaseJpaRepository<T, ID> { private final JpaEntityInformation<T, ?> entityInfo; private final EntityManager entityManager; public BaseJpaRepositoryImpl(JpaEntityInformation<T, ?> entityInfo, EntityManager entityManager) { super(entityInfo, entityManager); this.entityInfo = entityInfo; this.entityManager = entityManager; } @Override public T findOne(ID id) { return findById(id).orElse(null);
Sisanya ada dalam gambar dan rupa. Semua kesalahan kompilasi hilang, kode berfungsi seperti sebelumnya, dan hanya 2 kelas yang diubah, dan bukan 200. Sekarang Anda dapat secara perlahan mengganti API yang sudah usang dengan yang baru saat pengembangan berlangsung, karena Ide akan dengan hati-hati mewarnai semua panggilan ke metode @Deprecated yellow.
Stroke terakhir - kami memberi tahu Spring bahwa mulai sekarang dari repositori harus dibangun di atas kelas kami:
@Configuration @EnableJpaRepositories(repositoryBaseClass = BaseJpaRepositoryImpl.class) public class SomeConfig{ }
Di kolam yang tenang, nampan ditemukan
Spring Booth didasarkan pada dua konsep - starter dan konfigurasi otomatis. Dalam kehidupan, konsekuensi dari ini adalah properti penting - perpustakaan yang masuk ke jalan setapak aplikasi dipindai oleh SB dan jika kelas ditemukan di dalamnya yang mencakup pengaturan tertentu, maka itu akan menyala tanpa sepengetahuan Anda dan instruksi eksplisit. Kami menunjukkan ini dengan contoh sederhana.
Banyak yang menggunakan pustaka gson untuk mengubah objek menjadi string JSON dan sebaliknya. Anda dapat sering melihat kode ini:
@Component public class Serializer { public <T> String serialize(T obj) { return new Gson().toJson(obj); } }
Dengan akses yang sering, kode ini juga akan memuat pemulung, yang tidak kita inginkan. Khususnya orang pintar membuat satu objek dan menggunakannya:
@Configuration public class SomeConfig { @Bean public Gson gson() { return new Gson(); } } @Component @RequiredArgsConstructor public class Serializer { private final Gson gson; public String serialize(T obj) { return gson.toJson(obj); } }
... bahkan tanpa menyadari bahwa Spring Booth dapat melakukan semuanya sendiri. Secara default, Sat hadir dengan org.springframework.boot:spring-boot-autoconfigure
, yang mencakup banyak kelas bernama *AutoConfiguration
, misalnya, ini:
@Configuration @ConditionalOnClass(Gson.class) @EnableConfigurationProperties(GsonProperties.class) public class GsonAutoConfiguration { @Bean @ConditionalOnMissingBean public GsonBuilder gsonBuilder(List<GsonBuilderCustomizer> customizers) { GsonBuilder builder = new GsonBuilder(); customizers.forEach((c) -> c.customize(builder)); return builder; } @Bean @ConditionalOnMissingBean public Gson gson(GsonBuilder gsonBuilder) { return gsonBuilder.create(); } }
Pengaturannya sederhana sebagai rail: jika ada kelas Gson
dalam aplikasi dan tidak ada kacang yang ditentukan secara manual dari tipe ini, implementasi standar akan dibuat. Ini adalah kekuatan yang luar biasa, memungkinkan satu ketergantungan dan beberapa anotasi untuk meningkatkan konfigurasi penuh, deskripsi yang digunakan untuk mengambil lembar XML dan banyak nampan tulisan tangan.
Tapi ini adalah bahaya besar Dewan Keamanan. Insidiousness terletak pada kerahasiaan, karena banyak pekerjaan terjadi di bawah tenda menurut skrip pra-ditulis. Ini bagus untuk aplikasi tipikal, tetapi meninggalkan jalur yang dipukuli dapat menyebabkan masalah - konvoi menembak tanpa peringatan. Banyak kacang dibuat tanpa sepengetahuan kita, contoh dengan Gson menunjukkan ini dengan baik. Hati-hati dengan kelas dan dependensi Anda, karena ...
Apa pun yang masuk ke jalur kelas Anda dapat digunakan untuk melawan Anda.
Sebuah kasus dari kehidupan. Ada dua aplikasi: satu menggunakan LdapTemplate
, sementara yang lain telah menggunakannya sekali. Kedua aplikasi bergantung pada proyek core
, di mana beberapa kelas umum diambil, dan perpustakaan umum untuk kedua aplikasi dilipat dengan hati-hati di pom.xml.
Setelah beberapa waktu, dari proyek kedua, semua penggunaan LdapTemplate
sebagai tidak perlu. Tapi org.springramework:spring-ldap
perpustakaan org.springramework:spring-ldap
tetap di core
. Kemudian SB menyerang dan org.springramework.ldap:ldap-core
berubah menjadi org.springramework.boot:spring-boot-starter-data-ldap
.
Karena itu, pesan menarik muncul di log:
Multiple Spring Data modules found, entering strict repository configuration mode! Spring Data LDAP - Could not safely identify store assignment for repository candidate interface ....
Ketergantungan pada core
dipimpin org.springramework.boot:spring-boot-starter-data-ldap
sesuai dengan proyek di mana LDAP tidak digunakan sama sekali. Apa artinya ragu? Tekan classpath :). Lebih lanjut dengan semua halte:
- Pemberitahuan Spring Boot
org.springramework.boot:spring-boot-starter-data-ldap
di classpath - setiap repositori sekarang diperiksa untuk melihat apakah cocok untuk digunakan dengan Spring Data JPA atau Spring Data LDAP
- reorganisasi dependensi dan
org.springramework.boot:spring-boot-starter-data-ldap
tidak perlu org.springramework.boot:spring-boot-starter-data-ldap
mengurangi waktu startup aplikasi dengan rata-rata 20 (!) detik dari total 40-50
Pembaca yang penuh perhatian dan kritis mungkin akan bertanya: mengapa Anda perlu mengubah org.springramework.ldap:ldap-core
ke org.springramework.boot:spring-boot-starter-data-ldap
jika Spring Data LDAP tidak digunakan, tetapi hanya satu yang digunakan kelas org.springframework.ldap.LdapTempate
?
Jawab: itu kesalahan. Faktanya adalah bahwa sebelum versi 2.1.0.M1, penyetelan otomatis untuk LDAP terlihat seperti ini
@Configuration @ConditionalOnClass({ContextSource.class}) @EnableConfigurationProperties({LdapProperties.class}) public class LdapAutoConfiguration { private final LdapProperties properties; private final Environment environment; public LdapAutoConfiguration(LdapProperties properties, Environment environment) { this.properties = properties; this.environment = environment; } @Bean @ConditionalOnMissingBean public ContextSource ldapContextSource() { LdapContextSource source = new LdapContextSource(); source.setUserDn(this.properties.getUsername()); source.setPassword(this.properties.getPassword()); source.setBase(this.properties.getBase()); source.setUrls(this.properties.determineUrls(this.environment)); source.setBaseEnvironmentProperties(Collections.unmodifiableMap(this.properties.getBaseEnvironment())); return source; } }
Di mana LdapTemplate
? Tapi dia tidak :). Lebih tepatnya, tetapi terletak di tempat lain:
@Configuration @ConditionalOnClass({LdapContext.class, LdapRepository.class}) @AutoConfigureAfter({LdapAutoConfiguration.class}) public class LdapDataAutoConfiguration { @Bean @ConditionalOnMissingBean({LdapOperations.class}) public LdapTemplate ldapTemplate(ContextSource contextSource) { return new LdapTemplate(contextSource); } }
Dengan demikian, asumsi dibuat bahwa Anda LdapTemplate
mendapatkan LdapTemplate
dalam aplikasi Anda dengan memenuhi kondisi @ConditionalOnClass({LdapContext.class, LdapRepository.class})
, yang dimungkinkan ketika dependensi spring-boot-starter-data-ldap
ditambahkan ke classpath.
Kemungkinan lain: untuk menentukan kacang ini dengan tangan Anda, yang tidak Anda inginkan (karena itu mengapa kita membutuhkan SB). org.springramework.boot:spring-boot-starter-data-ldap
datang dengan ini setelah mengganti org.springramework.boot:spring-boot-starter-data-ldap
dengan org.springramework.ldap:ldap-core
.
Masalahnya diselesaikan di sini: https://github.com/spring-projects/spring-boot/pull/13136 . Perubahan besar: LdapTemplate
bean dipindahkan ke LdapAutoConfiguration
. Sekarang Anda dapat menggunakan LDAP tanpa mengikat ke spring-boot-starter-data-ldap
dan mendefinisikan secara manual nampan LdapTemplate
.
Kericuhan hood
Jika Anda menggunakan Hibernate, Anda mungkin terbiasa dengan antipattern open-in-view . Kami tidak akan membahas deskripsinya, dengan referensi dijelaskan dengan cukup detail, dan di sumber lain efek berbahaya dijelaskan dengan sangat rinci.
Bendera khusus bertanggung jawab untuk mengaktifkan / menonaktifkan mode ini:
spring: jpa: open-in-view: true
Dalam SB versi 1. * diaktifkan secara default, sementara pengguna tidak diberitahu tentang ini. Dalam versi 2. * masih diaktifkan, tetapi sekarang peringatan ditulis ke log:
WARN spring.jpa.open-in-view is enabled by default. Therefore, database queries may be performed during view rendering. Explicitly configure spring.jpa.open-in-view to disable this warning.
Awalnya, ada permintaan untuk menonaktifkan mode ini, epic srach pada topik berisi beberapa lusin (!) Dari komentar rinci, termasuk dari Oliver GΓΆrke (pengembang Spring Date), Vlad Michalce (pengembang Hibernate), Phil Web dan Vedran Pavic (pengembang Spring) dengan pro dan kontra.
Mereka sepakat bahwa perilaku tidak akan berubah, tetapi peringatan akan ditampilkan (yang diamati). Ada juga tip yang cukup umum untuk menonaktifkan mode ini:
spring: jpa: open-in-view: false
Itu saja, tulis tentang fitur rake dan menarik Anda dari Dewan Keamanan - ini benar-benar topik yang tidak ada habisnya.