首先我們要明確:@Import 註解是 Spring 提供的。 然後我們看一下該註解的官方註釋: Indicates one or more component classes to import — typically @Configuration classes. Provides functi ...
首先我們要明確:@Import 註解是 Spring 提供的。
然後我們看一下該註解的官方註釋:
Indicates one or more component classes to import — typically @Configuration classes.
Provides functionality equivalent to the <import/> element in Spring XML. Allows for importing @Configuration classes, ImportSelector and ImportBeanDefinitionRegistrar implementations, as well as regular component classes (as of 4.2; analogous to AnnotationConfigApplicationContext.register).
@Bean definitions declared in imported @Configuration classes should be accessed by using @Autowired injection. Either the bean itself can be autowired, or the configuration class instance declaring the bean can be autowired. The latter approach allows for explicit, IDE-friendly navigation between @Configuration class methods.
May be declared at the class level or as a meta-annotation.
If XML or other non-@Configuration bean definition resources need to be imported, use the @ImportResource annotation instead.
我們將其中我們關心的重要部分提取出來翻譯整理如下:
-
該註解可導入一個或多個組件類——通常是Configuration類。
-
允許導入@Configuration類、ImportSelector介面和ImportBeanDefinitionRegistrar介面的實現,以及普通類(從4.2版本開始)。
-
@Import註解可以在類上聲明,也可以作為元註解聲明在其他註解上。
那麼 @Import 註解到底有什麼作用和優勢呢?
記住下麵幾句話:
導入配置。
從各個地方、通過各種方式導入配置。
在你需要的時候,從各個地方、通過各種方式導入配置。
在你需要的時候,從各個地方、通過各種方式導入並改造成你喜歡的配置。
下麵我們通過示例講 4 種官方註釋給出的導入類型,分別是:
-
普通類
-
@Configuration類
-
ImportSelector介面實現
-
ImportBeanDefinitionRegistrar介面實現
普通類
普通類不必啰嗦,有手就行。
public class Circle {
}
@Configuration
@Import({Circle.class})
public class MainConfig {
}
導入@Configuration類
我們建立三個不同的模塊,MainModule, Module1, Module2。
讓 MainModule 引入 Module1、Module2依賴。
在 MainModule 中創建 MainConfig 類如下:
@Configuration
@Import({Config1.class, Config2.class})
public class MainConfig {
}
很明顯,Config1,Config2 分別屬於2個模塊,如下:
@Configuration
public class Config1 {
@Bean
public String config1() {
return "我是config1配置類!";
}
}
@Configuration
public class Config2 {
@Bean
public String config2() {
return "我是config2配置類!";
}
}
在 MainModule 中創建測試類看是否生效:
@Test
void contextLoads() {
AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(MainConfig.class);
for (String name : context.getBeanDefinitionNames()) {
System.out.println(String.format("%s->%s", name, context.getBean(name)));
}
}
測試結果如下:
mainConfig->com.example.annotation.config.MainConfig$$EnhancerBySpringCGLIB$$a98d5b6a@55a609dd
com.example.module1.config1->com.example.module1.config.Config1$$EnhancerBySpringCGLIB$$72aca5d0@4afd21c6
config1->我是config1配置類!
com.example.module2.config2->com.example.module2.config.Config2$$EnhancerBySpringCGLIB$$72aca5d0@4afd21c6
config2->我是config2配置類!
ImportSelector介面實現
首先我們需要認識一下 ImportSelector 介面:
public interface ImportSelector {
String[] selectImports(AnnotationMetadata importingClassMetadata);
@Nullable
default Predicate<String> getExclusionFilter() {
return null;
}
}
只有一個需要實現的方法:
selectImports
參數 AnnotationMetadata 是什麼?
翻譯為註解元數據。
有什麼用?
用來獲取被@Import標註的類上面所有的註解信息。
String[] 是什麼?
返回需要導入的類名的數組,可以是任何普通類,配置類。
上示例:
創建一個配置類
@Configuration
public class Config {
@Bean
public String name() {
return "公眾號:JavaCode";
}
}
定義一個ImportSelectors實現類
public class MyImportSelector implements ImportSelector {
@Override
public String[] selectImports(AnnotationMetadata importingClassMetadata) {
return new String[]{
Config.class.getName()
};
}
}
上述代碼中需要說明一下,Config.class.getName() 得到的是類的全類名,也就是[包名.類名],也就是com.example.annotation.pojo.Config。
導入MyImportSelector
@Import({MyImportSelector.class})
public class MainConfig {
}
這時候有人問了:
這裡怎麼沒有 @Configuration 註解?
沒關係哈,雖然 @Import 註解通常是與 @Configuration 註解一起使用的,但不是絕對必要的。
ImportBeanDefinitionRegistrar介面實現
首先我們還是先認識一下 ImportBeanDefinitionRegistrar 介面:
public interface ImportBeanDefinitionRegistrar {
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
registerBeanDefinitions(importingClassMetadata, registry);
}
default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
}
}
可以看到只有2個方法,第一個方法調用第二個方法,所以我們就只看第二個方法就好了。
AnnotationMetadata 我們前邊說過了:
用來獲取被@Import標註的類上面所有的註解信息。
BeanDefinitionRegistry 呢?
它是一個介面,內部提供了註冊bean的各種方法,用於我們手動註冊bean。
上示例:
創建一個 Java 類
public class Rectangle {
public void sayHi() {
System.out.println("Rectangle sayHi()");
}
}
創建 ImportBeanDefinitionRegistrar 實現類
public class MyImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
RootBeanDefinition rootBeanDefinition = new RootBeanDefinition(Rectangle.class);
// 註冊一個名字叫做 rectangle 的 bean
registry.registerBeanDefinition("rectangle", rootBeanDefinition);
}
}
總結
第一種:
我們有什麼就可以導什麼。
第二種:
我們可以把分散的配置集中起來,更加清晰和有組織。
第三種:
除了我們模塊現有的配置,你只要能拿到類的全類名,就能註冊進來。
第四種:
我們不光能到處拿,我們還能拿來以後隨便改!
以上四種方式就實現了我們當初的豪言:
導入配置。
從各個地方、通過各種方式導入配置。
在你需要的時候,從各個地方、通過各種方式導入配置。
在你需要的時候,從各個地方、通過各種方式導入並改造成你喜歡的配置。
● 學會@ConfigurationProperties月薪過三千