Salute, Khabrovsk. Jadi terjemahan bagian kedua dari artikel yang disiapkan khusus untuk siswa kursus "Pengembang Kerangka Kerja Musim Semi" tiba pada waktunya. Bagian pertama bisa dibaca di sini .
Spring mungkin merupakan salah satu platform pengembangan Java yang paling populer. Ini adalah alat yang kuat, tetapi agak sulit dipelajari. Konsep dasarnya cukup mudah dipahami dan dipelajari, tetapi perlu waktu dan upaya untuk menjadi pengembang Spring yang berpengalaman.
Pada artikel ini, kita akan melihat beberapa kesalahan paling umum yang dibuat ketika bekerja di Spring dan terkait, khususnya, untuk pengembangan aplikasi web dan penggunaan platform Spring Boot. Sebagaimana dicatat di situs web Spring Boot , Spring Boot mengambil pendekatan standar untuk membuat aplikasi yang siap digunakan, dan artikel ini akan mengikuti pendekatan ini. Ini akan memberikan sejumlah rekomendasi yang dapat digunakan secara efektif dalam pengembangan aplikasi web standar berdasarkan Spring Boot.
Jika Anda tidak terlalu mengenal platform Spring Boot, tetapi ingin bereksperimen dengan contoh-contoh dalam artikel ini, saya membuat repositori GitHub dengan bahan-bahan tambahan untuk artikel ini . Jika pada titik tertentu Anda sedikit bingung saat membaca artikel ini, saya akan menyarankan Anda untuk membuat klon dari repositori ini dan bereksperimen dengan kode di komputer Anda.
Kesalahan Umum # 6: Tidak menggunakan validasi data berbasis anotasi
Mari kita bayangkan bahwa layanan TopTalent kami dari contoh sebelumnya membutuhkan titik akhir untuk menambahkan data TopTalent baru. Juga, mari kita asumsikan bahwa untuk beberapa alasan yang sangat penting, setiap nama yang Anda tambahkan harus panjangnya tepat 10 karakter. Ini dapat diterapkan, misalnya, sebagai berikut:
@RequestMapping("/put") public void addTopTalent(@RequestBody TopTalentData topTalentData) { boolean nameNonExistentOrHasInvalidLength = Optional.ofNullable(topTalentData) .map(TopTalentData::getName) .map(name -> name.length() == 10) .orElse(true); if (nameNonExistentOrInvalidLength) {
Namun, kode di atas tidak hanya terstruktur dengan buruk, tetapi juga bukan solusi yang βbersihβ. Kami melakukan beberapa jenis validasi data (yaitu, kami memverifikasi bahwa objek
TopTalentData
bukan nol, bahwa nilai bidang TopTalentData.name bukan nol, dan bahwa panjang bidang
TopTalentData.name
adalah 10 karakter), dan juga memberikan pengecualian jika datanya salah.
Semua ini dapat dilakukan dengan lebih akurat menggunakan
validator Hibernate di Spring. Pertama-tama mari kita menulis ulang metode
addTopTalent
, menambahkan dukungan untuk validasi data:
@RequestMapping("/put") public void addTopTalent(@Valid @NotNull @RequestBody TopTalentData topTalentData) { topTalentService.addTopTalent(topTalentData); } @ExceptionHandler @ResponseStatus(HttpStatus.BAD_REQUEST) public ErrorResponse handleInvalidTopTalentDataException(MethodArgumentNotValidException methodArgumentNotValidException) {
Selain itu, kami perlu menunjukkan validasi properti apa yang ingin kami lakukan di kelas
TopTalentData
:
public class TopTalentData { @Length(min = 10, max = 10) @NotNull private String name; }
Spring sekarang akan memotong permintaan dan memverifikasinya sebelum memanggil metode, jadi tidak ada pemeriksaan manual tambahan yang diperlukan.
Sasaran yang diinginkan juga dapat dicapai dengan membuat anotasi Anda sendiri. Dalam kondisi nyata, biasanya masuk akal untuk menggunakan anotasi Anda sendiri hanya ketika kebutuhan Anda melebihi kemampuan
kumpulan Hibernate @Length
, tetapi untuk contoh ini, mari kita bayangkan bahwa anotasi
@Length
tidak ada. Anda bisa membuat validator data yang memeriksa panjang string dengan membuat dua kelas tambahan: satu untuk validasi dan satu untuk anotasi properti:
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.PARAMETER}) @Retention(RetentionPolicy.RUNTIME) @Documented @Constraint(validatedBy = { MyAnnotationValidator.class }) public @interface MyAnnotation { String message() default "String length does not match expected"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; int value(); } @Component public class MyAnnotationValidator implements ConstraintValidator<MyAnnotation, String> { private int expectedLength; @Override public void initialize(MyAnnotation myAnnotation) { this.expectedLength = myAnnotation.value(); } @Override public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) { return s == null || s.length() == this.expectedLength; } }
Harap perhatikan bahwa dalam kasus ini, penerapan prinsip pemisahan tanggung jawab yang benar mengharuskan penandaan properti sebagai valid jika nilainya nol
(s == null
dalam metode
isValid
), dan kemudian gunakan anotasi
@NotNull
jika ini juga diperlukan untuk properti ini:
public class TopTalentData { @MyAnnotation(value = 10) @NotNull private String name; }
Kesalahan Umum # 7: Menggunakan Konfigurasi Berbasis Legacy XML
Menggunakan XML adalah suatu keharusan ketika bekerja dengan versi Spring sebelumnya, tetapi sekarang sebagian besar tugas konfigurasi dapat diimplementasikan menggunakan kode Java dan anotasi. Konfigurasi XML sekarang bertindak sebagai kode templat tambahan dan opsional.
Dalam artikel ini (dan juga dalam materi repositori GitHub yang menyertai), anotasi digunakan untuk mengonfigurasi Spring, dan Spring tahu komponen JavaBean mana yang harus diikat karena paket root dianotasi menggunakan anotasi komposit @SpringBootApplication - seperti ini:
@SpringBootApplication public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); } }
Anotasi komposit ini (lihat
dokumentasi Spring untuk lebih lanjut tentang ini) hanya memberi tahu platform Spring paket mana yang akan dipindai untuk mengekstrak komponen JavaBean. Dalam kasus khusus kami, ini berarti bahwa sub-paket co.kukurin berikut akan digunakan untuk mengikat:
- Komponen (TopTalentConverter, MyAnnotationValidator)
- @RestController (TopTalentController)
- Repositori (TopTalentRepository)
- Kelas Layanan (TopTalentService)
Jika kita memiliki kelas tambahan yang memiliki anotasi
@Configuration
, mereka juga akan diperiksa untuk konfigurasi Java.
Kesalahan Umum # 8: Tidak Menggunakan Profil Konfigurasi
Ketika mengembangkan sistem server, masalah umum adalah beralih di antara konfigurasi yang berbeda (sebagai aturan, ini adalah konfigurasi untuk lingkungan operasional dan pengembangan). Alih-alih mengubah berbagai parameter secara manual pada setiap transisi antara mode pengujian dan pengoperasian, lebih efisien menggunakan profil konfigurasi.
Bayangkan kasusnya ketika di lingkungan pengembangan lokal Anda menggunakan database dalam RAM, dan di lingkungan operasi aktual aplikasi Anda, database MySQL digunakan. Ini pada dasarnya berarti bahwa Anda akan menggunakan URL yang berbeda dan, mungkin, kredensial berbeda untuk mengakses masing-masing database ini. Mari kita lihat bagaimana ini dapat diimplementasikan menggunakan dua file konfigurasi:
APLIKASI FILE.YAML # set default profile to 'dev' spring.profiles.active: dev # production database details spring.datasource.url: 'jdbc:mysql://localhost:3306/toptal' spring.datasource.username: root spring.datasource.password:
APLIKASI FILE-DEV.YAML spring.datasource.url: 'jdbc:h2:mem:' spring.datasource.platform: h2
Harus diasumsikan bahwa ketika bekerja dengan kode, Anda tidak ingin melakukan beberapa tindakan acak dengan database yang ditujukan untuk lingkungan operasi, jadi masuk akal untuk memilih profil untuk lingkungan pengembangan (DEV) sebagai profil default. Selanjutnya, Anda dapat secara manual mengganti profil konfigurasi di server dengan menentukan
-Dspring.profiles.active=prod
untuk JVM. Selain itu, profil konfigurasi default dapat ditentukan dalam variabel lingkungan sistem operasi.
Nomor kesalahan umum 9. Kegagalan untuk menggunakan mekanisme injeksi ketergantungan
Penggunaan yang tepat dari mekanisme injeksi dependensi di Spring berarti Spring dapat mengikat semua objek Anda dengan memindai semua kelas konfigurasi yang diperlukan. Ini berguna untuk melonggarkan saling ketergantungan dan sangat memudahkan pengujian. Alih-alih menghubungkan kelas dengan ketat, sesuatu seperti ini:
public class TopTalentController { private final TopTalentService topTalentService; public TopTalentController() { this.topTalentService = new TopTalentService(); } }
... kami mengizinkan platform Spring mengikat:
public class TopTalentController { private final TopTalentService topTalentService; public TopTalentController(TopTalentService topTalentService) { this.topTalentService = topTalentService; } }
Kuliah Mishko Hevery di saluran Google Tech Talks menjelaskan secara terperinci mengapa injeksi ketergantungan harus digunakan, tetapi di sini kita akan melihat bagaimana mekanisme ini digunakan dalam praktik. Di divisi tanggung jawab ("Kesalahan Umum # 3"), kami menciptakan kelas layanan dan pengontrol. Misalkan kita ingin menguji controller, dengan asumsi bahwa kelas
TopTalentService
berfungsi dengan benar. Kami dapat menyisipkan objek simulator alih-alih implementasi layanan saat ini dengan membuat kelas konfigurasi terpisah:
@Configuration public class SampleUnitTestConfig { @Bean public TopTalentService topTalentService() { TopTalentService topTalentService = Mockito.mock(TopTalentService.class); Mockito.when(topTalentService.getTopTalent()).thenReturn( Stream.of("Mary", "Joel").map(TopTalentData::new).collect(Collectors.toList())); return topTalentService; } }
Setelah itu, kita bisa mengimplementasikan objek simulator dengan
SampleUnitTestConfig
platform Spring bahwa kita perlu menggunakan
SampleUnitTestConfig
sebagai sumber konfigurasi:
@ContextConfiguration(classes = { SampleUnitTestConfig.class })
Selanjutnya, ini akan memungkinkan kita untuk menggunakan konfigurasi kontekstual untuk menanamkan komponen JavaBean kustom dalam uji unit.
Nomor kesalahan umum 10. Kurang pengujian atau pengujian salah
Terlepas dari kenyataan bahwa ide pengujian unit tidak berarti baru, tampaknya banyak pengembang "lupa" tentang hal itu (terutama jika
tidak wajib ) atau menghabiskannya terlambat. Jelas, ini salah, karena tes tidak hanya memungkinkan Anda untuk memeriksa operasi kode yang benar, tetapi juga berfungsi sebagai dokumentasi yang menunjukkan bagaimana aplikasi harus berperilaku dalam berbagai situasi.
Saat menguji layanan web, Anda jarang menjalankan tes unit "bersih" yang luar biasa, karena untuk koneksi HTTP, Anda biasanya perlu menggunakan servlet Spring
DispatcherServlet
dan melihat apa yang terjadi ketika Anda menerima permintaan
HttpServletRequest
nyata (yaitu, ternyata tes
integrasi yang menggunakan validasi, serialisasi, dll.). Solusi yang elegan dan terbukti adalah dengan menggunakan
REST Assured , perpustakaan Java untuk menguji layanan REST dengan nyaman, dengan MockMVC. Pertimbangkan fragmen kode berikut dengan injeksi dependensi:
@RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = { Application.class, SampleUnitTestConfig.class }) public class RestAssuredTestDemonstration { @Autowired private TopTalentController topTalentController; @Test public void shouldGetMaryAndJoel() throws Exception {
SampleUnitTestConfig
mengikat implementasi pengganti dari kelas
TopTalentService
ke
TopTalentController
, dan semua kelas lainnya terikat menggunakan konfigurasi standar yang diperoleh dengan memindai paket-paket berdasarkan paket kelas Aplikasi.
RestAssuredMockMvc
hanya digunakan untuk mengatur lingkungan yang ringan dan mengirim permintaan
GET
ke
/toptal/get
.
Gunakan Spring secara Profesional
Spring adalah platform yang kuat yang mudah untuk memulai, tetapi butuh waktu dan upaya untuk menguasainya sepenuhnya. Jika Anda meluangkan waktu untuk mengenal platform ini, pada akhirnya itu akan meningkatkan efisiensi pekerjaan Anda, membantu Anda membuat kode yang lebih bersih dan meningkatkan kualifikasi Anda sebagai pengembang.
Saya sarankan Anda memperhatikan
Spring In Action - ini adalah buku yang bagus, berorientasi pada aplikasi yang membahas banyak topik penting yang terkait dengan platform Spring.
Pada titik ini, terjemahan artikel ini berakhir.
Baca bagian pertama .