kecualikan @Component dari @ComponentScan

90

Saya memiliki komponen yang ingin saya kecualikan dari a @ComponentScansecara khusus @Configuration:

@Component("foo") class Foo {
...
}

Jika tidak, sepertinya akan bentrok dengan kelas lain dalam proyek saya. Saya tidak sepenuhnya memahami tabrakan, tetapi jika saya mengomentari @Componentanotasi, semuanya berfungsi seperti yang saya inginkan. Tetapi proyek lain yang mengandalkan pustaka ini mengharapkan kelas ini dikelola oleh Spring, jadi saya ingin melewatkannya hanya di proyek saya.

Saya mencoba menggunakan @ComponentScan.Filter:

@Configuration 
@EnableSpringConfigured
@ComponentScan(basePackages = {"com.example"}, excludeFilters={
  @ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value=Foo.class)})
public class MySpringConfiguration {}

tetapi tampaknya tidak berhasil. Jika saya mencoba menggunakan FilterType.ASSIGNABLE_TYPE, saya mendapatkan kesalahan aneh tentang tidak dapat memuat beberapa kelas yang tampaknya acak:

Disebabkan oleh: java.io.FileNotFoundException: sumber daya jalur kelas [junit / framework / TestCase.class] tidak dapat dibuka karena tidak ada

Saya juga mencoba menggunakan type=FilterType.CUSTOMsebagai berikut:

class ExcludeFooFilter implements TypeFilter {
    @Override
    public boolean match(MetadataReader metadataReader,
            MetadataReaderFactory metadataReaderFactory) throws IOException {
        return metadataReader.getClass() == Foo.class;
    }
}

@Configuration @EnableSpringConfigured
@ComponentScan(basePackages = {"com.example"}, excludeFilters={
  @ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value=Foo.class)})
public class MySpringConfiguration {}

Tapi sepertinya itu tidak mengecualikan komponen dari pemindaian seperti yang saya inginkan.

Bagaimana cara mengecualikannya?

ykaganovich
sumber

Jawaban:

107

Konfigurasi tampak baik-baik saja, kecuali yang harus Anda gunakan excludeFiltersdaripada excludes:

@Configuration @EnableSpringConfigured
@ComponentScan(basePackages = {"com.example"}, excludeFilters={
  @ComponentScan.Filter(type=FilterType.ASSIGNABLE_TYPE, value=Foo.class)})
public class MySpringConfiguration {}
Sergi Almar
sumber
maaf, itu adalah kesalahan potong dan tempel. Saya menggunakan excludeFilters. Saya akan melihat lagi, kesalahan yang diberikan ini kepada saya benar-benar aneh ...
ykaganovich
52

Menggunakan tipe eksplisit dalam filter pindai jelek bagi saya. Saya percaya pendekatan yang lebih elegan adalah membuat anotasi marker sendiri:

@Retention(RetentionPolicy.RUNTIME)
public @interface IgnoreDuringScan {
}

Tandai komponen yang harus dikecualikan dengannya:

@Component("foo") 
@IgnoreDuringScan
class Foo {
    ...
}

Dan kecualikan anotasi ini dari pemindaian komponen Anda:

@ComponentScan(excludeFilters = @Filter(IgnoreDuringScan.class))
public class MySpringConfiguration {}
luboskrnac.dll
sumber
13
Itu ide cerdas untuk pengecualian universal meskipun tidak akan membantu jika Anda ingin mengecualikan komponen hanya dari subset konteks aplikasi dalam sebuah proyek. Sungguh, untuk mengecualikannya secara universal, seseorang bisa saja menghapusnya @Component, tapi saya rasa bukan itu pertanyaannya
Kirby
2
ini tidak akan berfungsi jika Anda memiliki anotasi pemindaian komponen lain di suatu tempat yang tidak memiliki filter yang sama
Bashar Ali Labadi
@Bashar Ali Labadi, bukankah hal seperti itu dalam konstruksi ini? Jika Anda ingin mengecualikannya dari semua pemindaian komponen, mungkin itu bukan komponen Spring sama sekali.
luboskrnac
30

Pendekatan lain adalah dengan menggunakan anotasi bersyarat baru. Sejak polos Spring bintang 4 Anda dapat menggunakan penjelasan @Conditional:

@Component("foo")
@Conditional(FooCondition.class)
class Foo {
    ...
}

dan tentukan logika kondisional untuk mendaftarkan komponen Foo:

public class FooCondition implements Condition{
    @Override
    public boolean matches(ConditionContext context, AnnotatedTypeMetadata metadata) {
        // return [your conditional logic]
    }     
}

Logika bersyarat dapat didasarkan pada konteks, karena Anda memiliki akses ke pabrik kacang. Sebagai Contoh ketika komponen "Bar" tidak terdaftar sebagai kacang:

    return !context.getBeanFactory().containsBean(Bar.class.getSimpleName());

Dengan Spring Boot (sebaiknya digunakan untuk SETIAP project Spring baru), Anda dapat menggunakan anotasi bersyarat berikut:

  • @ConditionalOnBean
  • @ConditionalOnClass
  • @ConditionalOnExpression
  • @ConditionalOnJava
  • @ConditionalOnMissingBean
  • @ConditionalOnMissingClass
  • @ConditionalOnNotWebApplication
  • @ConditionalOnProperty
  • @ConditionalOnResource
  • @ConditionalOnWebApplication

Anda dapat menghindari pembuatan kelas Kondisi dengan cara ini. Lihat dokumen Spring Boot untuk detail selengkapnya.

luboskrnac.dll
sumber
1
+1 untuk kondisional, ini adalah cara yang jauh lebih bersih bagi saya daripada menggunakan filter. Saya belum pernah mendapatkan filter untuk bekerja secara konsisten seperti pemuatan kacang bersyarat
bersyarat wonderergoat77
13

Jika Anda perlu menentukan dua atau lebih kriteria excludeFilters , Anda harus menggunakan array.

Untuk contoh di bagian kode ini saya ingin mengecualikan semua kelas di paket org.xxx.yyy dan kelas khusus lainnya, MyClassToExclude

 @ComponentScan(            
        excludeFilters = {
                @ComponentScan.Filter(type = FilterType.REGEX, pattern = "org.xxx.yyy.*"),
                @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = MyClassToExclude.class) })
Enrico Giurin
sumber
saya akan menyarankan ASPECTJ sebagai jenis filter, karena regex ini juga akan cocok dengan "org.xxx.yyyy.z"
wutzebaer
9

Saya mengalami masalah saat menggunakan @Configuration , @EnableAutoConfiguration , dan @ComponentScan ketika mencoba untuk mengecualikan kelas konfigurasi tertentu, hal itu tidak bekerja!

Akhirnya saya memecahkan masalah dengan menggunakan @SpringBootApplication , yang menurut dokumentasi Spring melakukan fungsi yang sama seperti tiga di atas dalam satu anotasi.

Tip lainnya adalah mencoba terlebih dahulu tanpa menyempurnakan pemindaian paket Anda (tanpa filter basePackages).

@SpringBootApplication(exclude= {Foo.class})
public class MySpringConfiguration {}
Dorony
sumber
Saya telah mencoba ini tetapi menampilkan kesalahan sebagai "Kelas-kelas berikut tidak dapat dikecualikan karena mereka bukan kelas konfigurasi otomatis". excludeFilters dengan @ComponentScan berfungsi dengan baik.
AzarEJ
1

Dalam hal mengecualikan komponen pengujian atau konfigurasi pengujian, Spring Boot 1.4 memperkenalkan anotasi pengujian baru @TestComponentdan@TestConfiguration .

luboskrnac.dll
sumber
0

Saya perlu mengecualikan @Aspect @Component audit dari konteks aplikasi tetapi hanya untuk beberapa kelas pengujian. Saya akhirnya menggunakan @Profile ("audit") di kelas aspek; menyertakan profil untuk operasi normal tetapi mengecualikannya (jangan letakkan di @ActiveProfiles) pada kelas pengujian tertentu.

Miguel Pereira
sumber