Memahami kelas semi @Configuration

108

Mengikuti pertanyaan Memahami Penggunaan Spring @Autowired Saya ingin membuat basis pengetahuan lengkap untuk opsi lain kabel pegas, @Configurationkelas.

Anggaplah saya memiliki file XML pegas yang terlihat seperti ini:

<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

  <import resource="another-application-context.xml"/>

  <bean id="someBean" class="stack.overflow.spring.configuration.SomeClassImpl">
    <constructor-arg value="${some.interesting.property}" />
  </bean>

  <bean id="anotherBean" class="stack.overflow.spring.configuration.AnotherClassImpl">
    <constructor-arg ref="someBean"/>
    <constructor-arg ref="beanFromSomewhereElse"/>
  </bean>
</beans>

Bagaimana saya bisa menggunakan @Configuration? Apakah itu berpengaruh pada kode itu sendiri?

Avi
sumber

Jawaban:

151

Memigrasi XML ke @Configuration

Anda dapat memigrasi xml ke a @Configurationdalam beberapa langkah:

  1. Buat @Configurationkelas beranotasi:

    @Configuration
    public class MyApplicationContext {
    
    }
  2. Untuk setiap <bean>tag buat metode yang dianotasi dengan @Bean:

    @Configuration
    public class MyApplicationContext {
    
      @Bean(name = "someBean")
      public SomeClass getSomeClass() {
        return new SomeClassImpl(someInterestingProperty); // We still need to inject someInterestingProperty
      }
    
      @Bean(name = "anotherBean")
      public AnotherClass getAnotherClass() {
        return new AnotherClassImpl(getSomeClass(), beanFromSomewhereElse); // We still need to inject beanFromSomewhereElse
      }
    }
  3. Untuk mengimpor beanFromSomewhereElsekita perlu mengimpor definisinya. Itu bisa didefinisikan dalam XML dan kita akan menggunakan @ImportResource:

    @ImportResource("another-application-context.xml")
    @Configuration
    public class MyApplicationContext {
      ...  
    }

    Jika kacang didefinisikan di @Configurationkelas lain, kita dapat menggunakan @Importanotasi:

    @Import(OtherConfiguration.class)
    @Configuration
    public class MyApplicationContext {
      ...
    }
  4. Setelah kita mengimpor XML atau @Configurationkelas lain, kita dapat menggunakan kacang yang dideklarasikan dalam konteks kita dengan mendeklarasikan anggota pribadi ke @Configurationkelas sebagai berikut:

    @Autowired
    @Qualifier(value = "beanFromSomewhereElse")
    private final StrangeBean beanFromSomewhereElse;

    Atau gunakan secara langsung sebagai parameter dalam metode yang mendefinisikan kacang yang bergantung padanya dengan beanFromSomewhereElsemenggunakan @Qualifiersebagai berikut:

    @Bean(name = "anotherBean")
    public AnotherClass getAnotherClass(@Qualifier (value = "beanFromSomewhereElse") final StrangeBean beanFromSomewhereElse) {
      return new AnotherClassImpl(getSomeClass(), beanFromSomewhereElse);
    }
  5. Mengimpor properti sangat mirip dengan mengimpor kacang dari xml atau @Configurationkelas lain. Alih-alih menggunakan, @Qualifierkami akan menggunakan @Valuedengan properti sebagai berikut:

    @Autowired
    @Value("${some.interesting.property}")
    private final String someInterestingProperty;

    Ini dapat digunakan dengan ekspresi SpEL juga.

  6. Untuk mengizinkan spring memperlakukan kelas seperti wadah kacang, kita perlu menandai ini di xml utama kita dengan meletakkan tag ini dalam konteks:

    <context:annotation-config/>

    Anda sekarang dapat mengimpor @Configurationkelas persis sama seperti Anda membuat kacang sederhana:

    <bean class="some.package.MyApplicationContext"/>

    Ada cara untuk menghindari XML pegas sama sekali tetapi mereka tidak termasuk dalam cakupan jawaban ini. Anda dapat menemukan salah satu opsi ini di posting blog saya yang menjadi dasar jawaban saya.


Keuntungan dan kerugian menggunakan metode ini

Pada dasarnya saya menemukan metode mendeklarasikan kacang jauh lebih nyaman daripada menggunakan XML karena beberapa keuntungan yang saya lihat:

  1. Typos - @Configurationkelas dikompilasi dan kesalahan ketik tidak mengizinkan kompilasi
  2. Gagal cepat (waktu kompilasi) - Jika Anda lupa menyuntikkan kacang, Anda akan gagal pada waktu kompilasi dan tidak saat run-time seperti pada XML
  3. Lebih mudah dinavigasi dalam IDE - antara konstruktor kacang untuk memahami pohon ketergantungan.
  4. Memungkinkan untuk dengan mudah men-debug startup konfigurasi

Kerugiannya tidak banyak seperti yang saya lihat tetapi ada beberapa yang dapat saya pikirkan:

  1. Penyalahgunaan - Kode lebih mudah disalahgunakan daripada XML
  2. Dengan XML Anda bisa mendefinisikan dependensi berdasarkan kelas yang tidak tersedia selama waktu kompilasi tetapi disediakan selama run-time. Dengan @Configurationkelas Anda harus memiliki kelas yang tersedia pada waktu kompilasi. Biasanya itu bukan masalah, tetapi ada beberapa kasus yang mungkin terjadi.

Intinya: Tidak masalah untuk menggabungkan XML @Configurationdan anotasi dalam konteks aplikasi Anda. Spring tidak peduli dengan metode deklarasi kacang.

Avi
sumber
2
Salah satu kelemahan yang mungkin adalah hilangnya konfigurasi. Katakanlah Anda memiliki kelas yang meniru beberapa fungsionalitas dalam pengembangan, lalu Anda ingin menukarnya dengan kelas lain di lingkungan UAT. Menggunakan XML, maka itu hanya masalah mengubah konfigurasi dan mengizinkan aplikasi untuk dijalankan / dimulai ulang. Dengan konfigurasi kelas baru ini, kelas harus dikompilasi ulang.
Jose
5
@JoseChavez - Itu argumen bagus yang sudah saya dengar beberapa kali. Dan saya mencoba melakukan penelitian statistik di mana saya tidak dapat menemukan aplikasi atau sistem apa pun yang menggunakan XML di luar toples / perangnya. Arti praktisnya adalah Anda perlu mengekstrak toples dan mengubah XML (yang saya tidak dapat menemukan siapa pun yang melakukannya) atau membangun kembali toples Anda (yang menurut semua orang yang saya ajak bicara telah mereka lakukan sejauh ini) . Jadi, intinya - karena ini mungkin argumen yang cukup besar, biasanya tidak penting dalam kehidupan nyata.
Avi
6
Untuk itulah anotasi @Profile, dan sintaks "$ {env.value}". Dengan @Profile ("someName") Anda dapat menandai seluruh konfigurasi untuk digunakan hanya saat profil tersebut aktif. Dalam file application.properties (atau .yml), Anda dapat menyetel spring.profiles.active = someName, default ... Untuk menyetelnya secara dinamis berdasarkan variabel lingkungan, gunakan sintaks $ {SOME_ENV_VAR} sebagai nilai untuk spring. active.profiles dan mengatur variabel lingkungan. Spring sekarang merekomendasikan penggunaan konfigurasi java - docs.spring.io/spring-boot/docs/current/reference/htmlsingle/…
Jack Viers
Apa alternatif selain mendefinisikan setiap kacang sebagai metode dalam file konfigurasi?
Asif Mushtaq
@AsifMushtaq - Anda dapat menggunakan fitur autoscan dan setiap kelas yang memiliki @Component @Serviceatau penjelasan serupa lainnya akan secara otomatis dibuat menjadi kacang (tapi itu bukan fokus dari pertanyaan ini)
Avi