Spring Boot 多模塊注入訪問不到jar包中的Bean問題(巧用@ComponentScan)

原因:導致此類問題的主要原因是,此類不在Spring Boot的組件掃描範圍之內。

目錄

1. 關於 Spring Boot 自動注入及組件掃描

2. Spring Boot 中 @ComponentScan

3. Spring Boot 中 @SpringBootApplication

4 @ComponentScans

5 具體使用例子

6 補充說明:@AliasFor


1. 關於 Spring Boot 自動注入及組件掃描

在平時使用 Spring Boot 時,常常會使用到@Configuration,@Contoller,@Service,@Component等註解,被添加這些註解的類,在 Spring Boot 啓動時,會自動被 Spring 容器管理起來。

上面提到了,添加了一些註解的類會在Spring Boot 容器啓動時,被加載到Spring 容器中。那麼,組件掃描的作用就是:當 Spring Boot 啓動時,根據定義的掃描路徑,把符合掃描規則的類裝配到spring容器中。

2. Spring Boot 中 @ComponentScan

@ComponentScan有如下屬性:
 

value:指定要掃描的package;
includeFilters=Filter[]:指定只包含的組件
excludeFilters=Filter[]:指定需要排除的組件;
useDefaultFilters=true/false:指定是否需要使用Spring默認的掃描規則:被@Component, @Repository, @Service, @Controller或者已經聲明過@Component自定義註解標記的組件;

在過濾規則Filter中:
FilterType:指定過濾規則,支持的過濾規則有
    ANNOTATION:按照註解規則,過濾被指定註解標記的類;
    ASSIGNABLE_TYPE:按照給定的類型;
    ASPECTJ:按照ASPECTJ表達式;
    REGEX:按照正則表達式
    CUSTOM:自定義規則;
value:指定在該規則下過濾的表達式;

@ComponentScan過濾規則說明

規則表達式說明

1. 掃描指定類文件
   @ComponentScan(basePackageClasses = Person.class)
2. 掃描指定包,使用默認掃描規則,即被@Component, @Repository, @Service, @Controller或者已經聲明過@Component自定義註解標記的組件;
   @ComponentScan(value = "com.yibai")
3. 掃描指定包,加載被@Component註解標記的組件和默認規則的掃描(因爲useDefaultFilters默認爲true)
   @ComponentScan(value = "com.yibai", includeFilters = { @Filter(type = FilterType.ANNOTATION, value = Component.class) })
4. 掃描指定包,只加載Person類型的組件
   @ComponentScan(value = "com.yibai", includeFilters = { @Filter(type = FilterType.ASSIGNABLE_TYPE, value = Person.class) }, useDefaultFilters = false)
5. 掃描指定包,過濾掉被@Component標記的組件
   @ComponentScan(value = "com.yibai", excludeFilters = { @Filter(type = FilterType.ANNOTATION, value = Component.class) })
6. 掃描指定包,自定義過濾規則
   @ComponentScan(value = "com.yibai", includeFilters = { @Filter(type = FilterType.CUSTOM, value = ColorBeanLoadFilter.class) }, useDefaultFilters = true)

簡單的介紹了@ComponentScan的基礎作用,這個註解爲我們使用提供了一些可自定義配置屬性,先來看看@ComponentScan註解源碼:

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
  	// 指定掃描包的位置(同:basePackages 屬性),可以是單個路徑,也可以是掃描的路徑數組
    @AliasFor("basePackages")
    String[] value() default {};
	// 指定掃描包的位置(同:value 屬性)
    @AliasFor("value")
    String[] basePackages() default {};
	// 指定具體的掃描的類
    Class<?>[] basePackageClasses() default {};
	// bean的名稱的生成器	
    Class<? extends BeanNameGenerator> nameGenerator() default BeanNameGenerator.class;

    Class<? extends ScopeMetadataResolver> scopeResolver() default AnnotationScopeMetadataResolver.class;

    ScopedProxyMode scopedProxy() default ScopedProxyMode.DEFAULT;
	// 控制符合組件檢測條件的類文件   默認是包掃描下的  **/*.class
    String resourcePattern() default "**/*.class";
    // 是否開啓對@Component,@Repository,@Service,@Controller的類進行檢測
    boolean useDefaultFilters() default true;
    // 包含的過濾條件
    // 1. FilterType.ANNOTATION:			按照註解過濾
    // 2. FilterType.ASSIGNABLE_TYPE:	    按照給定的類型
    // 3. FilterType.ASPECTJ:				使用ASPECTJ表達式
    // 4. FilterType.REGEX:					正則
    // 5. FilterType.CUSTOM:				自定義規則
    ComponentScan.Filter[] includeFilters() default {};
    // 排除的過濾條件,用法和includeFilters一樣
    ComponentScan.Filter[] excludeFilters() default {};

    boolean lazyInit() default false;

    @Retention(RetentionPolicy.RUNTIME)
    @Target({})
    public @interface Filter {
        FilterType type() default FilterType.ANNOTATION;
        @AliasFor("classes")
        Class<?>[] value() default {};
        @AliasFor("value")
        Class<?>[] classes() default {};
        String[] pattern() default {};
    }
}

總結一下@ComponentScan的常用方式如下:

  • 通過使用value,basePackages屬性來指定掃描範圍;

  • 自動掃描路徑下邊帶有@Controller,@Service,@Repository,@Component註解加入Spring容器

  • 通過includeFilters加入掃描路徑下沒有以上註解的類加入spring容器

  • 通過excludeFilters過濾出不用加入spring容器的類

  • 自定義增加了@Component註解的註解方式

3. Spring Boot 中 @SpringBootApplication

在創建Spring Boot 項目之後,在默認的啓動類上會被添加@SpringBootApplication註解,這個註解默認幫我們開啓一些自動配置的功能,比如:基於Java的Spring配置,組件掃描,特別是用於啓用Spring Boot的自動配置功能。

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration                    // 允許自動配置
@ComponentScan(
    excludeFilters = {@Filter(              // 定義排除規則
    type = FilterType.CUSTOM,               // 採用自定義的方式
    classes = {TypeExcludeFilter.class}     // 自定義實現邏輯
), @Filter(                                 // 同上
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
    
    // 爲 @EnableAutoConfiguration 添加 exclude 規則
    @AliasFor(
        annotation = EnableAutoConfiguration.class,
        attribute = "exclude"
    )
    Class<?>[] exclude() default {};
    
    // 爲 @EnableAutoConfiguration 添加 excludeName 規則
    @AliasFor(
        annotation = EnableAutoConfiguration.class,
        attribute = "excludeName"
    )
    String[] excludeName() default {};
    
    // 爲 @ComponentScan 添加 basePackages 規則
    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackages"
    )
    String[] scanBasePackages() default {};

    // 爲 @ComponentScan 添加 basePackageClasses 規則
    @AliasFor(
        annotation = ComponentScan.class,
        attribute = "basePackageClasses"
    )
    Class<?>[] scanBasePackageClasses() default {};
}

從上面的源碼部分可以看到,@SpringBootApplication是一個組合註解,也就相當於使用一個@SpringBootApplication可以替代@SpringBootConfiguration,@EnableAutoConfiguration,@ComponentScan幾個註解聯合使用。

那麼,可能會有這樣的問題,我只是使用了一個@SpringBootApplication註解,但是我如何對@ComponentScan的屬性做自定義配置呢?

當然,Spring 團隊已經很好的解決了這個問題,在@SpringBootApplication註解類中的屬性上添加@AliasFor註解,從而實現通過對@SpringBootApplication中的屬性進行自定義,達到對對應的註解的屬性的自定義。
比如:

@AliasFor(
    annotation = ComponentScan.class,
    attribute = "basePackages"
)
String[] scanBasePackages() default {};

這段代碼就是實現,通過對@SpringBootApplication的屬性scanBasePackages,實現對@ComponentScan中的屬性basePackages進行自定義。

4 @ComponentScans

可以一次聲明多個@ComponentScan

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Repeatable(ComponentScans.class)  //指定ComponentScan可以被ComponentScans作爲數組使用
public @interface ComponentScan {
}
 
 
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
public @interface ComponentScans {
    
	ComponentScan[] value();
 
}

例如:

@ComponentScans(value = { @ComponentScan(value = "com.yibai.spring.annotation"),
		@ComponentScan(value = "com.yibai.spring.annotation", includeFilters = {
				@Filter(type = FilterType.CUSTOM, value = ColorBeanLoadFilter.class) }) })
public class MainConfig {
 
	@Bean(name = "pers", initMethod = "init", destroyMethod = "destory")
	public Person person() {
		return new Person();
	}
 
}

 

5 具體使用例子


先看看項目結構,項目入口文件在子項目A-project中,並且入口類所在包位置爲:package com.aa.bb.cc。

也就是說,在不做任何配置的情況下,此項目只會掃描當前包路徑及其子路徑下的文件,並將符合條件的對象注入到容器中管理。

再看看配置文件所在的包路徑位置:package aa.bb.dd.ee.ff,可見此包路徑並不在項目掃描的路徑範圍之內。

這也就導致了,我們定義的配置類,雖然加了@Configuration也不會對我們的項目起到作用。

可以對項目註解進行稍微修改,制定掃描包的範圍,就可以簡單的解決這個問題。如下:

@SpringBootApplication(scanBasePackages="aa.bb")
public class MyApplication {

    public static void main(String[] args) {
        SpringApplication.run(MyApplication.class,args);
    }
}

6 補充說明:@AliasFor

在Spring註解中,經常會發現很多註解的不同屬性起着相同的作用,比如@ComponentScan的value屬性和basePackages屬性。所以在使用的時候就需要做一些基本的限制,比如value和basePackages的值不能衝突,比如任意設置value或者設置basePackages屬性的值,都能夠通過另一個屬性來獲取值等等。爲了統一處理這些情況,Spring創建了@AliasFor標籤。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章