loại trừ @Component khỏi @ComponentScan


90

Tôi có một thành phần mà tôi muốn loại trừ khỏi một @ComponentScanthành phần cụ thể @Configuration:

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

Nếu không, nó dường như xung đột với một số lớp khác trong dự án của tôi. Tôi không hiểu đầy đủ về vụ va chạm, nhưng nếu tôi nhận xét ra @Componentchú thích, mọi thứ sẽ hoạt động như tôi muốn. Nhưng các dự án khác dựa trên thư viện này mong đợi lớp này được quản lý bởi Spring, vì vậy tôi chỉ muốn bỏ qua nó trong dự án của mình.

Tôi đã thử sử dụng @ComponentScan.Filter:

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

nhưng nó dường như không hoạt động. Nếu tôi thử sử dụng FilterType.ASSIGNABLE_TYPE, tôi gặp lỗi lạ về việc không thể tải một số lớp dường như ngẫu nhiên:

Gây ra bởi: java.io.FileNotFoundException: tài nguyên đường dẫn lớp [junit / framework / TestCase.class] không thể mở được vì nó không tồn tại

Tôi cũng đã thử sử dụng type=FilterType.CUSTOMnhư sau:

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 {}

Nhưng điều đó dường như không loại trừ thành phần khỏi quá trình quét như tôi muốn.

Làm cách nào để loại trừ nó?

Câu trả lời:


107

Cấu hình có vẻ ổn, ngoại trừ việc bạn nên sử dụng excludeFiltersthay vì excludes:

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

xin lỗi, đó là lỗi cắt và dán. Tôi đang sử dụngludeFilters. Tôi sẽ đưa một cái nhìn, những lỗi này mang lại cho tôi thực sự là kỳ lạ ...
ykaganovich

52

Đối với tôi, việc sử dụng các loại rõ ràng trong bộ lọc quét là điều xấu. Tôi tin rằng cách tiếp cận thanh lịch hơn là tạo chú thích điểm đánh dấu của riêng mình:

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

Đánh dấu thành phần cần được loại trừ với nó:

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

Và loại trừ chú thích này khỏi quá trình quét thành phần của bạn:

@ComponentScan(excludeFilters = @Filter(IgnoreDuringScan.class))
public class MySpringConfiguration {}

13
Đó là một ý tưởng thông minh để loại trừ phổ quát mặc dù sẽ không hữu ích nếu bạn muốn loại trừ một thành phần chỉ khỏi một tập hợp con các ngữ cảnh ứng dụng trong một dự án. Thực sự, để loại trừ nó trên toàn cầu, người ta chỉ có thể xóa @Component, nhưng tôi không nghĩ đó là những gì câu hỏi đang đặt ra
Kirby

2
điều này sẽ không hoạt động nếu bạn có một quét ở đâu đó chú thích thành phần mà không có cùng một bộ lọc
Bashar Ali Labadi

@Bashar Ali Labadi, đó không phải là điểm của cấu trúc này sao? Nếu bạn muốn loại trừ nó khỏi tất cả các lần quét thành phần, có lẽ nó không phải là thành phần Spring.
luboskrnac

30

Một cách tiếp cận khác là sử dụng các chú thích có điều kiện mới. Vì Spring 4 đơn giản, bạn có thể sử dụng chú thích @Conditional:

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

và xác định logic có điều kiện để đăng ký thành phần Foo:

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

Logic có điều kiện có thể dựa trên ngữ cảnh, vì bạn có quyền truy cập vào bean factory. Ví dụ khi thành phần "Bar" không được đăng ký là bean:

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

Với Spring Boot (nên được sử dụng cho MỌI dự án Spring mới), bạn có thể sử dụng các chú thích có điều kiện sau:

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

Bạn có thể tránh tạo lớp Điều kiện theo cách này. Tham khảo tài liệu Spring Boot để biết thêm chi tiết.


1
+1 cho các điều kiện, với tôi đây là một cách rõ ràng hơn nhiều so với việc sử dụng bộ lọc. Tôi đã không bao giờ nhận được bộ lọc để làm việc và kiên trì như tải có điều kiện của đậu
wondergoat77

13

Trong trường hợp bạn cần xác định hai hoặc nhiều tiêu chíludeFilters , bạn phải sử dụng mảng.

Đối với các trường hợp trong phần mã này, tôi muốn loại trừ tất cả các lớp trong gói org.xxx.yyy và một lớp cụ thể khác, MyClassToExclude

 @ComponentScan(            
        excludeFilters = {
                @ComponentScan.Filter(type = FilterType.REGEX, pattern = "org.xxx.yyy.*"),
                @ComponentScan.Filter(type = FilterType.ASSIGNABLE_TYPE, value = MyClassToExclude.class) })

tôi sẽ đề xuất ASPECTJ làm loại bộ lọc, vì regex này cũng sẽ khớp với "org.xxx.yyyy.z"
wutzebaer

9

Tôi đã gặp sự cố khi sử dụng @Configuration , @EnableAutoConfiguration@ComponentScan trong khi cố gắng loại trừ các lớp cấu hình cụ thể, vấn đề là nó không hoạt động!

Cuối cùng, tôi đã giải quyết được vấn đề bằng cách sử dụng @SpringBootApplication , theo tài liệu Spring thực hiện chức năng tương tự như ba chức năng ở trên trong một chú thích.

Một Mẹo khác là thử trước mà không cần tinh chỉnh quá trình quét gói của bạn (không có bộ lọc basePackages).

@SpringBootApplication(exclude= {Foo.class})
public class MySpringConfiguration {}

Tôi đã thử điều này nhưng hiển thị lỗi là "Không thể loại trừ các lớp sau vì chúng không phải là các lớp cấu hình tự động". Loại trừ với @ComponentScan đang hoạt động tốt.
AzarEJ


0

Tôi cần loại trừ @Aspect @Component đang kiểm tra khỏi ngữ cảnh ứng dụng nhưng chỉ đối với một số lớp thử nghiệm. Tôi đã kết thúc bằng cách sử dụng @Profile ("kiểm toán") trên lớp khía cạnh; bao gồm cấu hình cho các hoạt động bình thường nhưng loại trừ nó (không đặt nó trong @ActiveProfiles) trên các lớp thử nghiệm cụ thể.

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.