10 kesalahan paling umum saat bekerja dengan platform Spring. Bagian 1

Halo semuanya. Hari ini kami membagikan bagian pertama artikel, yang terjemahannya disiapkan khusus untuk siswa dari kursus "Pengembang Kerangka Kerja Musim Semi" . Ayo mulai!





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 Boot Spring, tetapi ingin bereksperimen dengan contoh-contoh dalam artikel ini, saya membuat repositori GitHub dengan 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 # 1: Pemrograman yang Terlalu Rendah


Kami mudah menyerah pada kesalahan yang tersebar luas ini, karena "sindrom penolakan pengembangan orang lain" cukup khas untuk lingkungan pemrograman. Salah satu gejalanya adalah penulisan ulang konstan potongan kode yang umum digunakan, dan ini adalah gejala yang dilihat oleh banyak programmer.
Mempelajari struktur internal dan fitur implementasi dari perpustakaan tertentu sering kali merupakan proses yang berguna, perlu dan menarik, tetapi jika Anda terus-menerus menulis jenis kode yang sama saat melakukan implementasi fungsi tingkat rendah, ini mungkin berbahaya bagi Anda sebagai pengembang perangkat lunak. Itulah mengapa abstraksi dan platform seperti Spring ada dan digunakan - mereka menyelamatkan Anda dari pekerjaan manual berulang dan memungkinkan Anda untuk berkonsentrasi pada objek area subjek Anda dan logika kode program pada level yang lebih tinggi.

Karena itu, abstraksi tidak boleh dihindari. Lain kali Anda dihadapkan dengan kebutuhan untuk menyelesaikan masalah tertentu, pertama-tama lakukan pencarian cepat dan cari tahu apakah perpustakaan yang memecahkan masalah ini sudah ada di Spring - Anda kemungkinan akan menemukan solusi turnkey yang cocok. Salah satu perpustakaan yang bermanfaat adalah Project Lombok , anotasi yang akan saya gunakan dalam contoh-contoh dalam artikel ini. Lombok digunakan sebagai generator kode templat, sehingga pengembang malas yang tinggal di masing-masing dari kita akan sangat senang berkenalan dengan perpustakaan ini. Lihat, misalnya, bagaimana tampilan komponen JavaBean standar di Lombok:

@Getter @Setter @NoArgsConstructor public class Bean implements Serializable { int firstBeanProperty; String secondBeanProperty; } 

Seperti yang mungkin sudah Anda pahami, kode di atas dikonversi ke bentuk berikut:

 public class Bean implements Serializable { private int firstBeanProperty; private String secondBeanProperty; public int getFirstBeanProperty() { return this.firstBeanProperty; } public String getSecondBeanProperty() { return this.secondBeanProperty; } public void setFirstBeanProperty(int firstBeanProperty) { this.firstBeanProperty = firstBeanProperty; } public void setSecondBeanProperty(String secondBeanProperty) { this.secondBeanProperty = secondBeanProperty; } public Bean() { } } 

Perhatikan bahwa Anda kemungkinan besar harus menginstal plug-in yang sesuai jika Anda ingin menggunakan Lombok di lingkungan pengembangan terintegrasi Anda. Versi plug-in ini untuk IntelliJ IDEA dapat ditemukan di sini .

Kesalahan umum nomor 2. "Kebocoran" dari struktur internal


Memberikan akses ke struktur internal tidak pernah merupakan ide yang baik, karena hal itu merusak fleksibilitas model layanan dan, sebagai hasilnya, berkontribusi pada pembentukan gaya pemrograman yang buruk. "Kebocoran" struktur internal dimanifestasikan dalam kenyataan bahwa struktur database menjadi dapat diakses dari titik akhir API tertentu. Misalnya, anggap "objek Jawa tua yang baik" (POJO) berikut ini mewakili tabel di database Anda:

 @Entity @NoArgsConstructor @Getter public class TopTalentEntity { @Id @GeneratedValue private Integer id; @Column private String name; public TopTalentEntity(String name) { this.name = name; } } 

Asumsikan bahwa ada titik akhir yang perlu mengakses data objek TopTalentEntity . Untuk mengembalikan instance TopTalentEntity sepertinya ide yang menggoda, tetapi solusi yang lebih fleksibel adalah membuat kelas baru yang mewakili data TopTalentEntity untuk titik akhir API:

 @AllArgsConstructor @NoArgsConstructor @Getter public class TopTalentData { private String name; } 

Dengan demikian, membuat perubahan pada komponen internal dari basis data tidak akan memerlukan perubahan tambahan pada tingkat layanan. Mari kita lihat apa yang terjadi jika bidang kata sandi ditambahkan ke kelas TopTalentEntity untuk menyimpan hash kata sandi pengguna dalam database: jika tidak ada konektor, seperti TopTalentData , dan pengembang lupa untuk mengubah bagian antarmuka layanan, ini dapat menyebabkan pengungkapan informasi rahasia yang sangat tidak diinginkan!

Nomor kesalahan umum 3. Menggabungkan fungsi yang akan lebih baik untuk didistribusikan dalam kode


Mengelola kode aplikasi seiring pertumbuhannya menjadi tugas yang semakin penting. Anehnya, sebagian besar prinsip pemrograman efektif berhenti bekerja ketika skala pengembangan tertentu tercapai, terutama jika arsitektur aplikasi belum dipikirkan dengan baik. Dan salah satu kesalahan yang paling sering dibuat adalah kombinasi fungsi dalam kode yang lebih masuk akal untuk diterapkan secara terpisah.

Alasan pelanggaran prinsip pemisahan tanggung jawab biasanya adalah penambahan fungsi baru ke kelas yang ada. Mungkin ini adalah solusi sesaat yang baik (khususnya, membutuhkan penulisan kode dalam jumlah yang lebih kecil), tetapi di masa depan itu pasti akan menjadi masalah, termasuk pada tahap pengujian dan pemeliharaan kode dan di antara mereka. Pertimbangkan pengontrol berikut yang mengembalikan TopTalentData dari repositori:

 @RestController public class TopTalentController { private final TopTalentRepository topTalentRepository; @RequestMapping("/toptal/get") public List<TopTalentData> getTopTalent() { return topTalentRepository.findAll() .stream() .map(this::entityToData) .collect(Collectors.toList()); } private TopTalentData entityToData(TopTalentEntity topTalentEntity) { return new TopTalentData(topTalentEntity.getName()); } } 

Pada pandangan pertama, tampaknya tidak ada kesalahan yang jelas dalam fragmen kode ini. Ini memberikan daftar objek TopTalentData , yang diambil dari instance kelas TopTalentEntity . Tetapi jika Anda melihat kode lebih dekat, kita akan melihat bahwa dalam kenyataannya TopTalentController melakukan beberapa tindakan di sini, yaitu, itu menghubungkan permintaan dengan titik akhir tertentu, mengambil data dari repositori dan mengubah objek yang diterima dari TopTalentRepository ke dalam format yang berbeda. Solusi yang lebih bersih harus memisahkan fungsi-fungsi ini ke dalam kelas yang terpisah. Misalnya, mungkin terlihat seperti ini:

 @RestController @RequestMapping("/toptal") @AllArgsConstructor public class TopTalentController { private final TopTalentService topTalentService; @RequestMapping("/get") public List<TopTalentData> getTopTalent() { return topTalentService.getTopTalent(); } } @AllArgsConstructor @Service public class TopTalentService { private final TopTalentRepository topTalentRepository; private final TopTalentEntityConverter topTalentEntityConverter; public List<TopTalentData> getTopTalent() { return topTalentRepository.findAll() .stream() .map(topTalentEntityConverter::toResponse) .collect(Collectors.toList()); } } @Component public class TopTalentEntityConverter { public TopTalentData toResponse(TopTalentEntity topTalentEntity) { return new TopTalentData(topTalentEntity.getName()); } } 

Keuntungan tambahan dari hierarki ini adalah ia memungkinkan kita untuk memahami di mana fungsi berada hanya dengan melihat nama kelas. Selanjutnya, selama pengujian, kita dapat dengan mudah mengganti kode dari kelas-kelas ini dengan kode pengganti, jika perlu.

Nomor kesalahan umum 4. Kode seragam dan penanganan kesalahan buruk


Topik keseragaman kode tidak unik untuk Spring (atau untuk Java pada umumnya), tetapi, bagaimanapun, merupakan aspek penting yang harus dipertimbangkan ketika bekerja dengan proyek-proyek di Spring. Memilih gaya pemrograman tertentu dapat menjadi topik diskusi (dan biasanya konsisten di dalam tim pengembangan atau di seluruh perusahaan), tetapi dalam hal apa pun, keberadaan standar umum untuk penulisan kode berkontribusi pada peningkatan efisiensi kerja. Ini benar terutama jika beberapa orang mengerjakan kode. Kode seragam dapat diteruskan dari pengembang ke pengembang tanpa menghabiskan banyak usaha untuk mempertahankannya atau pada penjelasan yang panjang tentang mengapa ini atau kelas-kelas itu diperlukan.

Bayangkan ada proyek Musim Semi di mana terdapat berbagai file konfigurasi, layanan, dan pengontrol. Mengikuti keseragaman semantik dalam penamaan mereka, kami membuat struktur tempat Anda dapat dengan mudah mencari dan di mana pengembang mana pun dapat dengan mudah memahami kode tersebut. Misalnya, Anda bisa menambahkan akhiran Config ke nama kelas konfigurasi, akhiran Layanan ke nama layanan, dan akhiran Controller ke nama controller.

Penanganan kesalahan sisi server terkait erat dengan keseragaman kode dan perlu mendapat perhatian khusus. Jika Anda pernah menangani pengecualian yang berasal dari API yang ditulis dengan buruk, Anda mungkin tahu betapa sulitnya memahami dengan benar makna pengecualian ini, dan bahkan lebih sulit untuk menentukan mengapa mereka benar-benar terjadi.
Sebagai pengembang API, idealnya Anda ingin membahas semua titik akhir yang digunakan pengguna dan mengarahkan mereka untuk menggunakan format pesan kesalahan tunggal. Biasanya ini berarti bahwa Anda perlu menggunakan kode kesalahan standar dan deskripsinya dan mengabaikan keputusan yang meragukan seperti memberikan pengguna pesan "500 Internal Server Error" atau hasil dari jejak tumpukan (opsi terakhir, omong-omong, harus dihindari dengan segala cara, karena Anda mengungkapkan data internal, dan selain itu, hasil seperti itu sulit untuk diproses di sisi klien).
Di sini, misalnya, seperti apa bentuk umum dari pesan kesalahan:

 @Value public class ErrorResponse { private Integer errorCode; private String errorMessage; } 

Format yang mirip dengan ini sering ditemukan di API paling populer dan, biasanya, berfungsi dengan baik karena dapat dengan mudah dan sistematis didokumentasikan. Anda bisa mengonversi pengecualian ke format ini dengan menambahkan anotasi @ExceptionHandler ke metode (untuk contoh anotasi, lihat bagian "Kesalahan Umum # 6").

Nomor kesalahan umum 5. Pekerjaan salah dengan multithreading


Mengimplementasikan multithreading bisa menjadi tugas yang sulit, terlepas dari apakah itu digunakan dalam aplikasi desktop atau dalam aplikasi web, dalam proyek Musim Semi atau proyek untuk platform lain. Masalah yang disebabkan oleh eksekusi paralel program sulit dilacak, dan mengatasinya dengan debugger seringkali sangat sulit. Jika Anda memahami bahwa Anda berurusan dengan kesalahan eksekusi paralel, maka kemungkinan besar Anda harus meninggalkan debugger dan memeriksa kode secara manual sampai akar penyebab kesalahan ditemukan. Sayangnya, tidak ada cara standar untuk menyelesaikan masalah tersebut. Dalam setiap kasus, perlu untuk menilai situasi dan "menyerang" masalah dengan metode yang tampaknya terbaik dalam kondisi yang diberikan.

Idealnya, tentu saja, Anda ingin sepenuhnya menghindari kesalahan yang terkait dengan multithreading. Dan meskipun tidak ada resep universal di sini, saya masih dapat menawarkan beberapa rekomendasi praktis.

Hindari menggunakan negara global


Pertama, selalu ingat masalah "negara global". Jika Anda mengembangkan aplikasi multi-utas, Anda perlu memonitor dengan cermat semua variabel yang dapat dimodifikasi secara global, dan jika mungkin, singkirkan semuanya. Jika Anda masih memiliki alasan mengapa variabel global harus dapat dimodifikasi, terapkan sinkronisasi dengan benar dan pantau kinerja aplikasi Anda - Anda harus memastikan bahwa itu tidak melambat karena penambahan masa tunggu.

Hindari benda yang bisa berubah


Gagasan ini datang langsung dari prinsip-prinsip pemrograman fungsional dan, yang disesuaikan dengan prinsip-prinsip OOP, menyatakan bahwa kelas yang bisa berubah dan keadaan yang bisa berubah harus dihindari. Singkatnya, ini berarti Anda harus menahan diri dari pengaturan metode (setter) dan memiliki bidang pribadi dengan pengubah akhir di semua kelas model. Satu-satunya waktu nilainya berubah adalah ketika objek dibuat. Dalam hal ini, Anda dapat yakin bahwa tidak ada masalah yang terkait dengan persaingan untuk sumber daya, dan ketika mengakses properti objek, nilai yang benar akan selalu diperoleh.

Catat data penting


Mengevaluasi di mana masalah dapat terjadi dalam aplikasi, dan mengatur pencatatan semua data penting sebelumnya. Jika terjadi kesalahan, Anda akan senang memiliki informasi tentang permintaan apa yang telah diterima, dan Anda akan dapat lebih memahami alasan kegagalan fungsi aplikasi Anda. Namun, perlu diingat bahwa logging melibatkan file I / O tambahan dan tidak boleh disalahgunakan, karena ini dapat mempengaruhi kinerja aplikasi.

Gunakan implementasi utas yang sudah jadi


Saat Anda perlu menelurkan utas Anda (misalnya, untuk membuat permintaan asinkron ke berbagai layanan), gunakan implementasi utas aman yang sudah jadi alih-alih membuat sendiri. Dalam kebanyakan kasus, Anda dapat menggunakan abstraksi ExecutorService dan abstraksi fungsional spektakuler CompletableFuture untuk Java 8. Untuk membuat utas, platform Spring juga memungkinkan Anda untuk menangani permintaan asinkron menggunakan kelas DeferredResult .

Akhir dari bagian pertama.
Baca bagian kedua.

Source: https://habr.com/ru/post/id464429/


All Articles