Pada artikel ini saya ingin menggambarkan antarmuka kondisi dan kondisi yang sangat berguna, dan sering digunakan.
Konteks musim semi adalah wadah besar dari berbagai kacang, baik musim semi itu sendiri maupun yang khusus. Anda selalu ingin memiliki alat manajemen yang fleksibel untuk kebun binatang bin ini. Anotasi @Conditional baru saja dibuat untuk ini.
Cara paling umum untuk mengelola konteks pegas adalah melalui profil. Mereka memungkinkan Anda untuk dengan cepat dan mudah mengontrol pembuatan kacang. Tetapi kadang-kadang, lebih banyak penyempurnaan mungkin diperlukan.
Sebagai contoh, selama pengujian, muncul masalah: unit test pada mesin pengembang memerlukan bin tipe-X untuk pekerjaannya, ketika menjalankan tes yang sama pada build server, Y-bin diperlukan, dan Z-bin diperlukan pada produksi. @Conditional menawarkan yang sederhana dan mudah dalam hal ini keputusan. Seperti yang sering terjadi ketika bekerja di tim yang tidak disinkronkan, seseorang tidak punya waktu untuk menyelesaikan revisi pada batas waktu, dan fungsionalitas Anda sudah siap. Penting untuk beradaptasi dengan kondisi ini dan mengubah perilaku. Artinya, tambahkan kemampuan untuk mengubah konteks aplikasi tanpa mengkompilasi ulang, misalnya, mengubah hanya satu parameter dalam konfigurasi.
Pertimbangkan anotasi ini secara lebih rinci. Di atas setiap nampan di kode sumber, kita dapat menambahkan @Kondisi dan pegas akan secara otomatis memeriksa kondisi yang ditentukan dalam anotasi ini saat dibuat.
Dalam dokumentasi resmi, dinyatakan seperti ini:
@Target(value={TYPE,METHOD}) @Retention(value=RUNTIME) @Documented public @interface Conditional
Pada saat yang sama, Anda perlu mentransfer serangkaian kondisi ke dalamnya:
Class<? extends Condition>[]
Di mana Conditional adalah antarmuka fungsional yang berisi metode ini
boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata)
Mari kita periksa cara kerjanya dalam praktik, menggunakan contoh hidup. Aplikasi kami memiliki antarmuka baik dalam bentuk sabun / istirahat - layanan, dan dalam bentuk JMS. Tetapi administrator tidak punya waktu untuk menyiapkan infrastruktur yang tepat pada waktunya - kami tidak dapat menggunakan JMS.
Ada beberapa konfigurasi java untuk JMS di proyek kami:
@Configuration public class JmsConfig { ... }
Spring menemukan konfigurasi ini dan mulai menginisialisasi itu. Selanjutnya, semua kacang tergantung lainnya ditarik, misalnya, membaca dari antrian. Untuk menonaktifkan pembuatan konfigurasi ini, kami akan menggunakan anotasi turunan bersyarat - ConditioanalOnProperty
@ConditionalOnProperty( value="project.mq.enabled", matchIfMissing = false) @Configuration public class JmsConfig { ... }
Di sini kami menginformasikan konteks pembangun bahwa kami akan membuat konfigurasi ini hanya jika ada nilai positif dari project.mq.enable konstan dalam file pengaturan.
Sekarang mari kita beralih ke kacang dependen dan menandainya dengan anotasi ConditioanalOnBean , yang akan mencegah pegas membuat kacang yang tergantung pada konfigurasi kita.
@ConditionalOnBean(JmsConfig.class) @Component public class JmsConsumer { ... }
Dengan demikian, dengan satu parameter, kita dapat menonaktifkan komponen aplikasi yang tidak kita butuhkan, dan kemudian menambahkannya ke konteks dengan mengubah konfigurasi.
Bersama-sama dengan kerangka kerja, ada sejumlah besar anotasi siap pakai yang mencakup 99% dari kebutuhan pengembang (yang akan dijelaskan nanti dalam artikel). Tetapi bagaimana jika Anda perlu menangani beberapa situasi tertentu. Untuk melakukan ini, Anda bisa menambahkan logika kustom Anda sendiri ke pegas.
Misalkan kita memiliki kacang - SuperDBLogger , yang ingin kita buat hanya jika ada penjelasan @Loggable atas sembarang tempat sampah kami. Bagaimana akan terlihat dalam kode:
@Component @ConditionalOnLoggableAnnotation public class SuperDBLogger âĻ
Pertimbangkan cara kerja anotasi @ConditionalOnLoggableAnnotation :
@Target({ ElementType.TYPE, ElementType.METHOD }) @Retention(RetentionPolicy.RUNTIME) @Conditional(OnLoggableAnnotation.class) public @interface ConditionalOnLoggableAnnotation { }
Kita tidak memerlukan parameter lagi, sekarang mari kita beralih ke logikanya sendiri - konten kelas OnLoggableAnnotation . Di dalamnya, kami mendefinisikan kembali metode kecocokan , di mana kami mengimplementasikan pencarian kacang yang ditandai dalam paket kami.
public class OnLoggableAnnotation implements Condition { @Override public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) { ClassPathScanner scanner = new ClassPathScanner(); scanner.addIncludeFilter(new AnnotationTypeFilter(Loggable.class)); Set<BeanDefinition> bd = scanner.findInPackage("ru.habr.mybeans"); if (!bd.isEmpty()) return true; return false; } }
Jadi, kami membuat aturan yang menurutnya Spring sekarang membuat SuperDBLogger . Untuk penggemar SpringBoot, pembuat kerangka menciptakan SpringBootCondition , yang merupakan penerus Kondisi . Ini berbeda dalam tanda tangan dari metode yang didefinisikan ulang:
public abstract ConditionOutcome getMatchOutcome(ConditionContext context, AnnotatedTypeMetadata metadata);
Artinya, selain jawabannya, kita membutuhkan nampan atau tidak, Anda dapat menambahkan pesan, yang kemudian dapat dilihat di log pegas, menyadari untuk alasan apa nampan itu dibuat atau tidak.
Untuk membuat kondisi yang lebih kompleks, berbagai kondisi dapat digabungkan, mekanisme ini menyediakan kelas AnyNestedCondition, AllNestedConditions, dan NoneNestedConditions . Misalkan kita ingin membuat dua kondisi sehingga ketika salah satunya dieksekusi, kacang kita dibuat. Untuk melakukan ini, buat kelas Anda sendiri dan mewarisinya dari AnyNestedCondition .
public class AnnotationAndPropertyCondition extends AnyNestedCondition { public AnnotationAndPropertyCondition() { super(REGISTER_BEAN); } @ConditionalOnProperty(value = "db.superLogger") static class Condition1 {} @ConditionalOnLoggableAnnotation static class Condition2 {} }
Kelas tidak perlu ditandai tambahan dengan anotasi, pegas itu sendiri akan menemukannya dan memprosesnya dengan benar. Pengguna hanya perlu menunjukkan pada tahap konfigurasi apa syarat-syarat dipenuhi: ConfigurationPhase .REGISTER_BEAN - saat membuat kacang biasa, ConfigurationPhase .PARSE_CONFIGURATION - saat bekerja dengan konfigurasi (yaitu, untuk nampan yang ditandai dengan penjelasan konfigurasi @ ).
Demikian pula, untuk kelas AllNestedConditions dan NoneNestedConditions - yang pertama memonitor pemenuhan semua kondisi, yang kedua - yang tidak memenuhi syarat.
Juga, untuk memeriksa beberapa kondisi, Anda dapat melewati beberapa kelas dengan ketentuan ke @Conditional . Sebagai contoh, @Conditional ({OnLoggableAnnotation.class, AnnotationAndPropertyCondition.class}) . Keduanya harus mengembalikan true sehingga kondisinya terpenuhi dan kacang dibuat.
Seperti yang saya sebutkan di atas, sudah ada banyak solusi siap pakai dengan pegas, yang disajikan pada tabel di bawah ini.
Semuanya dapat diaplikasikan bersama dalam satu definisi kacang.
Dengan demikian, @Conditional adalah alat konfigurasi konteks yang cukup kuat, membuat aplikasi lebih fleksibel. Tetapi perlu mempertimbangkan fakta bahwa Anda perlu menggunakan anotasi ini dengan hati-hati, karena perilaku konteksnya tidak sejelas ketika menggunakan profil - dengan sejumlah besar tempat sampah yang dikonfigurasi, Anda dapat dengan cepat menjadi bingung. Dianjurkan untuk secara hati-hati mendokumentasikan dan mencatat aplikasinya di proyek Anda, jika tidak maka dukungan kode akan menyebabkan kesulitan.