ไม่รวม @Component จาก @ComponentScan


90

ฉันมีองค์ประกอบที่ฉันต้องการแยกออกจาก@ComponentScanรายการเฉพาะ@Configuration:

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

มิฉะนั้นดูเหมือนว่าจะปะทะกับคลาสอื่น ๆ ในโครงการของฉัน ฉันไม่เข้าใจการชนกันอย่างถ่องแท้ แต่ถ้าฉันแสดงความคิดเห็นใน@Componentคำอธิบายประกอบสิ่งต่างๆจะเป็นไปอย่างที่ฉันต้องการ แต่โปรเจ็กต์อื่น ๆ ที่ต้องอาศัยไลบรารีนี้คาดว่าคลาสนี้จะจัดการโดย Spring ดังนั้นฉันจึงขอข้ามไปเฉพาะในโปรเจ็กต์ของฉัน

ฉันลองใช้@ComponentScan.Filter:

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

แต่ดูเหมือนจะไม่ได้ผล ถ้าฉันลองใช้FilterType.ASSIGNABLE_TYPEฉันได้รับข้อผิดพลาดแปลก ๆ เกี่ยวกับการไม่สามารถโหลดคลาสที่ดูเหมือนสุ่มได้:

เกิดจาก: java.io.FileNotFoundException: class path resource [junit / framework / TestCase.class] ไม่สามารถเปิดได้เนื่องจากไม่มีอยู่

ฉันยังลองใช้type=FilterType.CUSTOMดังต่อไปนี้:

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

แต่ดูเหมือนจะไม่รวมส่วนประกอบออกจากการสแกนอย่างที่ฉันต้องการ

ฉันจะยกเว้นได้อย่างไร

คำตอบ:


108

การกำหนดค่าดูเหมือนจะใช้ได้ยกเว้นว่าคุณควรใช้excludeFiltersแทนexcludes:

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

ขออภัยนั่นเป็นข้อผิดพลาดในการตัดและวาง ฉันใช้ excludeFilters ฉันจะดูอีกครั้งข้อผิดพลาดนี้ทำให้ฉันแปลกจริงๆ ...
ykaganovich

52

การใช้ประเภทที่ชัดเจนในตัวกรองการสแกนเป็นสิ่งที่น่าเกลียดสำหรับฉัน ฉันเชื่อว่าแนวทางที่สวยงามกว่าคือการสร้างคำอธิบายประกอบเครื่องหมายของตัวเอง:

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

ทำเครื่องหมายส่วนประกอบที่ควรยกเว้น:

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

และไม่รวมคำอธิบายประกอบนี้จากการสแกนส่วนประกอบของคุณ:

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

13
นั่นเป็นแนวคิดที่ชาญฉลาดสำหรับการยกเว้นแบบสากลแม้ว่าจะไม่ช่วยอะไรหากคุณต้องการแยกส่วนประกอบออกจากบริบทแอปพลิเคชันเพียงบางส่วนในโครงการ จริงๆแล้วหากต้องการยกเว้นในระดับสากลเราสามารถลบออกได้@Componentแต่ฉันไม่คิดว่านั่นคือสิ่งที่ถาม
Kirby

2
สิ่งนี้จะไม่ทำงานหากคุณมีคำอธิบายประกอบการสแกนส่วนประกอบอื่นที่ใดที่ไม่มีตัวกรองเดียวกัน
Bashar Ali Labadi

@ บาชาร์อาลีลาบาดีไม่ใช่ประเด็นของโครงสร้างนี้หรือ? หากคุณต้องการแยกออกจากการสแกนส่วนประกอบทั้งหมดก็ไม่ควรเป็นองค์ประกอบ Spring เลย
luboskrnac

30

อีกวิธีหนึ่งคือการใช้คำอธิบายประกอบแบบมีเงื่อนไขใหม่ ตั้งแต่Spring 4 ธรรมดาคุณสามารถใช้คำอธิบายประกอบ @Conditional:

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

และกำหนดตรรกะเงื่อนไขสำหรับการลงทะเบียนองค์ประกอบ Foo:

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

ตรรกะเงื่อนไขสามารถขึ้นอยู่กับบริบทเนื่องจากคุณมีสิทธิ์เข้าถึงโรงงานถั่ว ตัวอย่างเช่นเมื่อส่วนประกอบ "Bar" ไม่ได้ลงทะเบียนเป็น bean:

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

ด้วย Spring Boot (ควรใช้สำหรับโครงการ Spring ใหม่ทุกโครงการ) คุณสามารถใช้คำอธิบายประกอบแบบมีเงื่อนไขเหล่านี้:

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

คุณสามารถหลีกเลี่ยงการสร้างคลาสเงื่อนไขด้วยวิธีนี้ โปรดดูเอกสาร Spring Boot สำหรับรายละเอียดเพิ่มเติม


1
+1 สำหรับเงื่อนไขนี่เป็นวิธีที่สะอาดกว่าการใช้ตัวกรองมาก ฉันไม่เคยได้รับตัวกรองเพื่อให้ทำงานได้อย่างสม่ำเสมอเหมือนการโหลดถั่วแบบมีเงื่อนไข
wondergoat77

13

ในกรณีที่คุณต้องการกำหนดexcludeFilters ตั้งแต่สองตัวขึ้นไปเกณฑ์เกณฑ์ไปคุณต้องใช้อาร์เรย์

สำหรับอินสแตนซ์ในโค้ดส่วนนี้ฉันต้องการยกเว้นคลาสทั้งหมดในแพ็กเกจorg.xxx.yyyและคลาสเฉพาะอื่นMyClassToExclude

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

ฉันขอแนะนำ ASPECTJ เป็นประเภทตัวกรองเนื่องจาก regex นี้จะจับคู่ "org.xxx.yyyy.z" ด้วย
wutzebaer

9

ฉันมีปัญหาเมื่อใช้@Configuration , @EnableAutoConfigurationและ@ComponentScanในขณะที่พยายามยกเว้นคลาสการกำหนดค่าเฉพาะ แต่สิ่งนี้ไม่ได้ผล!

ในที่สุดฉันก็แก้ปัญหาโดยใช้@SpringBootApplicationซึ่งตามเอกสาร Spring ทำหน้าที่เหมือนกันกับสามข้อด้านบนในคำอธิบายประกอบเดียว

เคล็ดลับอีกประการหนึ่งคือลองก่อนโดยไม่ต้องปรับแต่งการสแกนแพ็กเกจของคุณ (โดยไม่มีตัวกรอง basePackages)

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

1
ฉันได้ลองแล้ว แต่แสดงข้อผิดพลาดว่า "ไม่สามารถยกเว้นคลาสต่อไปนี้ได้เนื่องจากไม่ใช่คลาสที่กำหนดค่าอัตโนมัติ" excludeFilters กับ @ComponentScan ใช้งานได้ดี
AzarEJ


0

ฉันจำเป็นต้องแยกการตรวจสอบ @Aspect @Component ออกจากบริบทของแอป แต่มีเพียงคลาสทดสอบไม่กี่คลาสเท่านั้น ฉันลงเอยด้วยการใช้ @Profile ("การตรวจสอบ") ในชั้นเรียน รวมถึงโปรไฟล์สำหรับการทำงานปกติ แต่ไม่รวม (อย่าใส่ไว้ใน @ActiveProfiles) ในคลาสทดสอบเฉพาะ

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.