👉 這是一個或許對你有用的社群
《專案實戰(影片)》:從書中學,往事上“練” 《網際網路高頻面試題》:面朝簡歷學習,春暖花開 《架構 x 系統設計》:摧枯拉朽,掌控面試高頻場景題 《精進 Java 學習指南》:系統學習,網際網路主流技術棧 《必讀 Java 原始碼專欄》:知其然,知其所以然

👉這是一個或許對你有用的開源專案國產 Star 破 10w+ 的開源專案,前端包括管理後臺 + 微信小程式,後端支援單體和微服務架構。功能涵蓋 RBAC 許可權、SaaS 多租戶、資料許可權、商城、支付、工作流、大屏報表、微信公眾號、ERP、CRM、AI 大模型等等功能:
Boot 多模組架構:https://gitee.com/zhijiantianya/ruoyi-vue-pro Cloud 微服務架構:https://gitee.com/zhijiantianya/yudao-cloud 影片教程:https://doc.iocoder.cn 【國內首批】支援 JDK 17/21 + SpringBoot 3.3、JDK 8/11 + Spring Boot 2.7 雙版本
xxxDAO
或者xxxService
時,很多時候一時半會也是緩不過神的:
1,再看@ComponentScan
1) IoC容器的掃描起點
@SpringBootApplication
這個註解並不陌生,我們建立的每個Spring Boot工程主類都長這樣差不多:package
com.gitee.swsk33.mainmodule;
import
org.springframework.boot.SpringApplication;
import
org.springframework.boot.autoconfigure.SpringBootApplication;
@SpringBootApplication
publicclassMainModuleApplication
{
publicstaticvoidmain(String[] args)
{
SpringApplication.run(MainModuleApplication
.class, args)
;
}
}
@Component
)之外,還需要將其放在主類(也就是標註了@SpringBootApplication
的類)所在的軟體包或者其子包層級下,這樣在IoC容器初始化時,我們的類才會被掃描到。@SpringBootApplication
事實上標註了IoC容器建立Bean時掃描的起點,不過@SpringBootApplication
是一個複雜的複合註解,它是下列註解的組合:
@ComponentScan
,當該註解標註在一個類上時,這個類就會被標記為IoC容器的掃描起點,相信大家初學Spring時都寫過這樣類似的入門示例:package
com.gitee.swsk33.springdemo;
import
com.gitee.swsk33.springdemo.service.MessageService;
import
org.springframework.context.annotation.AnnotationConfigApplicationContext;
import
org.springframework.context.annotation.ComponentScan;
import
org.springframework.context.ApplicationContext;
@ComponentScan
publicclassMain
{
publicstaticvoidmain(String[] args)
{
// 建立基於註解的上下文容器例項,並傳入配置類Main以例項化其它標註了Bean註解的類
ApplicationContext context =
new
AnnotationConfigApplicationContext(Main
.class)
;
// 利用Spring框架,取出Bean:MessageService物件
MessageService messageService = context.getBean(MessageService
.class)
;
// 這時,就可以使用了!
messageService.printMessage();
}
}
@ComponentScan
註解,主類位於軟體包com.gitee.swsk33.springdemo
,那麼IoC容器初始化時,就會遞迴掃描位於軟體包com.gitee.swsk33.springdemo
中及其下所有子包中標註了相關注解(例如@Component
、@Service
)的類,並將它們例項化為Bean放入IoC容器託管,上述程式碼中,MessageService
位於com.gitee.swsk33.springdemo
中的子包service下且標註了相關注解,因此能夠被例項化為Bean並放入IoC容器,後續我們可以取出。@ComponentScan
還是@SpringBootApplication
註解,都是可以指定掃描位置的,比如說:@SpringBootApplication
(scanBasePackages =
"com.gitee.swsk33.mainmodule"
)
publicclassMainModuleApplication
{
// ...
}
com.gitee.swsk33.mainmodule
中及其所有子包下對應的類,只不過平時大多數時候我們都預設這個引數,這樣預設情況下,@ComponentScan
或者@SpringBootApplication
就是以自身為起點向下掃描當前包以及所有的子包中的類了。2) 匯入其它模組作為依賴?

main-module
工程是主功能,而另外兩個是兩個子功能模組,主功能模組需要以Maven依賴的形式匯入子功能模組,它們才能組成一個完整的系統。FunctionOneService
類也會被掃描到並例項化為Bean嗎?com.gitee.swsk33.mainmodule
中,那麼啟動時就會掃描該軟體包及其子包下的類,不可能說掃描到功能1中的軟體包com.gitee.swsk33.functionone
了。@SpringBootApplication
註解中指定scanBasePackages
欄位將兩個子模組的包路徑加進去就行了,這樣確實沒有問題,但是好像總覺得不是很優雅:如果我需要按需停用或者啟用功能,那就需要修改這個主類的註解中傳入的引數。@Import
註解也可以實現這個功能。基於 Spring Boot + MyBatis Plus + Vue & Element 實現的後臺管理系統 + 使用者小程式,支援 RBAC 動態許可權、多租戶、資料許可權、工作流、三方登入、支付、簡訊、商城等功能
專案地址:https://github.com/YunaiV/ruoyi-vue-pro 影片教程:https://doc.iocoder.cn/video/
2,@Import註解的基本使用
@Import
註解通常標註在配置類上,它可以在IoC容器初始化當前配置類的同時,將其它的指定類也引入進來並初始化為Bean,例如:@Configuration
@Import
(DemoFunction
.
class
)
publicclassFunctionImportConfig
{
}
FunctionImportConfig
是一個配置類,該類會在IoC容器初始化時被掃描並初始化為Bean,那麼在IoC容器掃描這個FunctionImportConfig
的同時,也會讀取到它上面的@Import註解,而@Import
註解中指定了類DemoFunction
,這就可以使得DemoFunction
類也被加入掃描的候選類,最終也被例項化為Bean並交給IoC容器。@Import
的類放在哪裡,主要這個類能被掃描到,且標註了@Configuration
等註解、能被例項化為Bean,那麼其上的@Import
註解中指定的類也會被連帶著加入掃描以及初始化為Bean的候選。DemoFunction
類也是有要求的,它必須是一個配置類,分下面兩種情況討論:-
被匯入的 DemoFunction
是@Configuration
標註的類: Spring會將這個DemoFuntion
配置類初始化為Bean並載入到IoC容器中,這意味著只有該配置類本身、以及其中顯示宣告的Bean才會被載入到容器中,其他未宣告的bean則不會被載入 -
被匯入的 DemoFunction
是@ComponentScan
標註的類: Spring則會在匯入該配置類同時,還會根據@ComponentScan
指定的掃描包路徑,掃描其指定的全部包下對應的類(標註了@Component
等等註解的)並初始化為Bean,預設則是將該類及其所在包的所有子包下的相關類初始化為Bean
@Import
註解不就可以在主模組中,把功能1模組中的類全部匯入並初始化為Bean嗎?1) 匯入其它模組的@ComponentScan類
spring-boot-starter
作為一些註解的基本支援即可:
@SpringBootApplication
改成@ComponentScan
,僅作為掃描起點類即可,該類位於功能模組1最頂層軟體包中,其中內容如下:package
com.gitee.swsk33.functionone;
import
org.springframework.context.annotation.ComponentScan;
@ComponentScan
publicclassFunctionOneApplication
{
}
package
com.gitee.swsk33.functionone.service;
import
jakarta.annotation.PostConstruct;
import
lombok.extern.slf4j.Slf4j;
import
org.springframework.stereotype.Service;
@Slf
4j
@Service
publicclassFunctionOneService
{
@PostConstruct
privatevoidinit()
{
log.info(
"功能1,啟動!"
);
}
}

@Import
匯入功能模組1中的掃描起點(標註了@ComponentScan
的類):package
com.gitee.swsk33.mainmodule.config;
import
com.gitee.swsk33.functionone.FunctionOneApplication;
import
com.gitee.swsk33.functiontwo.FunctionTwoApplication;
import
org.springframework.context.annotation.Configuration;
import
org.springframework.context.annotation.Import;
/**
* 用於匯入其它模組的配置,使得其它模組中的Bean也能夠交給IoC託管
*/
@Configuration
@Import
(FunctionOneApplication
.
class
)
publicclassFunctionImportConfig
{
}
@Import
可以匯入多個類,傳入陣列形式即可,這裡我們只匯入模組1的起點類。
-
首先初始化主模組中的配置類 FunctionImportConfig
,同時讀取到該配置類上的@Import
註解中指定的模組1中的類FunctionOneApplication
-
模組1中的類 FunctionOneApplication
被@ComponentScan
標註,因此新增掃描起點,將FunctionOneApplication
所在的包及其所有子包也加入掃描路徑 -
這樣不僅僅主模組自身,還有模組1下所有標註了對應註解的類都被掃描並初始化為了Bean,並加入了IoC容器中 -
這樣,我們就可以在主模組中,自動裝配模組1中的類了
@Import
註解可以很方便地將一個其它模組,甚至其它外部庫中的對應配置類匯入,並加入掃描初始化為Bean,加入到我們當前的IoC容器中去,並且在我們使用@Import
匯入@ComponentScan
標註的類時,可以實現新增一個掃描起點的效果,而不僅僅是隻掃描我們當前專案中的包路徑,這樣就將其它模組中的包路徑也加入掃描。2) 封裝@Import註解
@EnableAsync
以及@EnableDiscoveryClient
這些註解,都是基於@Import
實現的,當我們給自己專案的主類或者某個配置類打上該註解時,就能夠啟用某些功能,反之對應功能不會載入。@EnableFunctionOne
註解,在主模組中編寫該註解程式碼如下:package
com.gitee.swsk33.mainmodule.annotation;
import
com.gitee.swsk33.functionone.FunctionOneApplication;
import
org.springframework.context.annotation.Import;
import
java.lang.annotation.ElementType;
import
java.lang.annotation.Retention;
import
java.lang.annotation.RetentionPolicy;
import
java.lang.annotation.Target;
/**
* 結合
@Import
註解,實現註解控制功能模組1是否啟用
*/
@Target
(ElementType.TYPE)
@Retention
(RetentionPolicy.RUNTIME)
@Import
(FunctionOneApplication
.
class
)
public
@
interfaceEnableFunctionOne
{
}
@Import
註解,並在其中指定需要匯入的類,比如功能1的掃描起點。FunctionImportConfig
,而是在主模組啟動類上標註我們這個自定義註解:

@EnableFunctionOne
時,也會讀取到@EnableFunctionOne
中的@Import
註解,並獲取要匯入的類的資訊,完成匯入。@EnableFunctionOne
放在別的地方,比如某個配置類上,也可以起到一樣的效果。基於 Spring Cloud Alibaba + Gateway + Nacos + RocketMQ + Vue & Element 實現的後臺管理系統 + 使用者小程式,支援 RBAC 動態許可權、多租戶、資料許可權、工作流、三方登入、支付、簡訊、商城等功能
專案地址:https://github.com/YunaiV/yudao-cloud 影片教程:https://doc.iocoder.cn/video/
3,動態匯入
@Import
中宣告的類就會被匯入,那麼能不能更加靈活一點控制類的匯入呢?事實上也是可以的。@Import
中指定的類,可以有三種:-
@Configuration
或者@ComponentScan
標註的配置類 -
實現了 ImportSelector
介面的類 -
實現了 ImportBeanDefinitionRegistrar
介面的類
@PostConstruct
在啟動時列印一個訊息。
1) 指定實現了ImportSelector介面的類
ImportSelector
介面是Spring中的一個擴充套件介面,用於動態地控制哪些配置類應該被匯入。透過實現ImportSelector
介面,我們就可以根據特定的條件或邏輯在執行時決定要匯入的配置類。selectImports
,該方法返回一個字串陣列,陣列中就包含了需要匯入的配置類的全限定類名。Spring在載入配置類時會呼叫selectImports
方法,並根據方法返回的類名動態地匯入對應的類並初始化為Bean。ImportSelector
介面的類如下:package
com.gitee.swsk33.mainmodule.selector;
import
org.springframework.context.annotation.ImportSelector;
的全限定名放在字串資料返回,則會匯入返回的陣列中指定的類
*/
@Override
public
String[] selectImports(AnnotationMetadata importingClassMetadata) {
// 列印被標註@Import的類的元資料
System.out.println(
"被標註@Import的類名:"
+ importingClassMetadata.getClassName());
// 直接引入第一個和第二個功能的主類
returnnew
String[]{
"com.gitee.swsk33.functionone.FunctionOneApplication"
,
"com.gitee.swsk33.functiontwo.FunctionTwoApplication"
};
}
}
ImportSelector
介面的類即可:package
com.gitee.swsk33.mainmodule.config;
import
com.gitee.swsk33.mainmodule.selector.DemoImportSelector;
import
org.springframework.context.annotation.Configuration;
import
org.springframework.context.annotation.Import;
/**
* 指定匯入實現了ImportSelector的類,然後就會根據其中selectImports方法返回值,實現自定義匯入指定類
*/
@Configuration
@Import
(DemoImportSelector
.
class
)
publicclassFunctionImportConfig
{
}

FunctionImportConfig
配置類時,讀取@Import
註解,而其中指定的類是一個實現了ImportSelector
的類,那麼這時Spring框架就會執行實現了ImportSelector
的類中的介面方法selectImports
,並獲取其返回值,根據返回值指定的全限定類名引入相關的類,並初始化為Bean。-
實現 ImportSelector
介面的類無需標註@Component
等註解 -
介面方法 selectImports
返回的需要匯入的類,也無需一定要是配置類,而可以是任何標註了@Component
等等相關Bean註解的類
@Import
註解進行封裝,實現一個自己的@EnableXXX
註解。2) 指定實現了ImportBeanDefinitionRegistrar介面的類
ImportBeanDefinitionRegistrar
介面是Spring中的另一個擴充套件介面,它允許我們在執行時動態地註冊BeanDefinition
,從而實現更高階的配置管理。與ImportSelector
不同的是,ImportBeanDefinitionRegistrar
不僅可以匯入配置類,還可以動態地註冊Bean定義。ImportBeanDefinitionRegistrar
介面定義了一個方法registerBeanDefinitions
,該方法接受兩個引數:-
AnnotationMetadata
用於獲取當前類的註解資訊 -
BeanDefinitionRegistry
用於註冊Bean定義
ImportBeanDefinitionRegistrar
介面,我們就可以根據特定的條件或邏輯在執行時註冊Bean定義,從而實現更加靈活和動態的配置管理。ImportBeanDefinitionRegistrar
介面的類如下:package
com.gitee.swsk33.mainmodule.selector;
import
com.gitee.swsk33.functionone.FunctionOneApplication;
import
com.gitee.swsk33.functiontwo.FunctionTwoApplication;
import
org.springframework.beans.factory.support.BeanDefinitionRegistry;
import
org.springframework.beans.factory.support.GenericBeanDefinition;
import
org.springframework.context.annotation.ImportBeanDefinitionRegistrar;
import
org.springframework.core.type.AnnotationMetadata;
/**
* 實現ImportBeanDefinitionRegistrar介面後,可在其中使用自定義的邏輯,實現動態地將對應類註冊為Bean
*/
publicclassDemoImportSelectorimplementsImportBeanDefinitionRegistrar
{
/**
* 定義一個自定義邏輯,在其中可以動態地將對應的類註冊為Bean
*
*
@param
importingClassMetadata 標註了
@Import
註解的類的元資料
*
@param
registry 用於將指定類註冊到IoC容器
*/
@Override
publicvoidregisterBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry)
{
// 註冊兩個功能模組中標註了@ComponentScan的類為Bean
// 定義一個Bean定義物件,傳入第一個模組的@ComponentScan配置類
GenericBeanDefinition functionOneScanBean =
new
GenericBeanDefinition();
functionOneScanBean.setBeanClass(FunctionOneApplication
.class)
;
// 表示第二個模組的Bean定義物件
GenericBeanDefinition functionTwoScanBean =
new
GenericBeanDefinition();
functionTwoScanBean.setBeanClass(FunctionTwoApplication
.class)
;
// 將兩個定義物件進行註冊,這樣上述兩個類就會被註冊為Bean
registry.registerBeanDefinition(
"functionOneComponentScan"
, functionOneScanBean);
registry.registerBeanDefinition(
"functionTwoComponentScan"
, functionTwoScanBean);
}
}
ImportSelector
介面的類作用一樣,都是自定義匯入其它類的邏輯,不過方式不一樣,首先我們建立GenericBeanDefinition
例項,並指定需要匯入的類,然後藉助BeanDefinitionRegistry
引數傳入我們的GenericBeanDefinition
例項,實現將對應的類匯入並註冊為Bean。@Import
指定這個實現了ImportBeanDefinitionRegistrar
介面的類即可:package
com.gitee.swsk33.mainmodule.config;
import
com.gitee.swsk33.mainmodule.selector.DemoImportSelector;
import
org.springframework.context.annotation.Configuration;
import
org.springframework.context.annotation.Import;
/**
* 指定匯入實現了ImportSelector的類,然後就會根據其中selectImports方法返回值,實現自定義匯入指定類
*/
@Configuration
@Import
(DemoImportSelector
.
class
)
publicclassFunctionImportConfig
{
}

ImportSelector
大同小異,過程當然也是差不多的:初始化配置類FunctionImportConfig
時,讀取到@Import
註解中指定的類,並執行該類介面方法registerBeanDefinitions
,完成對Bean的註冊。4,結合Spring Bean條件註解
@Import
是否觸發生效。@Import
的配置類上使用@ConditionalOnProperty
註解:package
com.gitee.swsk33.mainmodule.config;
import
com.gitee.swsk33.functionone.FunctionOneApplication;
import
org.springframework.boot.autoconfigure.condition.ConditionalOnProperty;
import
org.springframework.context.annotation.Configuration;
import
org.springframework.context.annotation.Import;
/**
* 用於匯入其它模組的配置,使得其它模組中的Bean也能夠交給IoC託管
* 還可以藉助Spring的條件註解例如
@ConditionalOnProperty
,實現透過配置或者其它條件動態控制這個配置類是否載入,進而實現控制
@Import
是否生效
*/
@Configuration
@Import
(FunctionOneApplication
.
class
)
@
ConditionalOnProperty
(
prefix
=
"com.gitee.swsk33.function-one"
, name =
"enabled"
)
publicclassFunctionImportConfig
{
}
@ConditionalOnProperty
註解可以用來根據配置檔案條件,控制某個類是否被初始化為Bean,例如上述註解配置表示:配置檔案中必須存在配置項com.gitee.swsk33.function-one.enabled
且其值必須為true時,這個配置類FunctionImportConfig
才會被載入並例項化為Bean,只有這樣@Import
才會被讀取到,進而觸發匯入。application.properties
控制是否匯入功能模組1了:
# 開啟功能
1
com.gitee.swsk33.function-one.enabled=
true
5,總結
@Import
註解,可以很方便地實現自定義匯入對應的配置類,甚至是新增掃描起點,這對於我們Spring Boot模組化開發是一個有利的工具。也可見該註解功能非常強大,可以透過實現對應介面方法,完成靈活地自定義匯入。@Import
註解具體作用是什麼,以及該註解可以傳入哪些類作為引數,以及其大致的工作流程。




