日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當(dāng)前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

idea去掉无用import类_@Import注解的魅力

發(fā)布時間:2024/10/14 编程问答 38 豆豆
生活随笔 收集整理的這篇文章主要介紹了 idea去掉无用import类_@Import注解的魅力 小編覺得挺不錯的,現(xiàn)在分享給大家,幫大家做個參考.

? ? 本篇主要介紹Spring注解@Import的魅力所在:它能讓你高度自由的定義配置類裝載規(guī)則與Bean注冊邏輯。@Import是Spring體系中的一個比較重要的注解,下面讓我們一起看看它都有哪些神奇的魅力吧!

注冊Bean

?? @Import第一個功能就是注冊Bean到Spring容器中,用法非常簡單,將需要注冊到Spring的類通過@Import直接導(dǎo)入即可,這時候注冊的Bean對應(yīng)的名稱為類完全限定名。如下所示:

package?com.swj.mj.autoconfig.service;

@Slf4j
public?class?HelloService?{

????public?HelloService()?{
????????log.info("HelloService?Initialization...");
????}

????public?String?sayHello(String?name)?{
????????return?"Hello?"?+?name;
????}

}

? ??首先任意定義一個類,然后我們直接在某個Bean或者配置類上通過@Import(HelloService.class)方法注冊Bean,如下:

package?com.swj.mj.web.hello;

@RestController
@AllArgsConstructor
@RequestMapping("/hello")
@Import(HelloService.class)
public?class?HelloController?implements?ApplicationContextAware?{

????private?static?ApplicationContext?appCtx;
????private?final?HelloService?helloService;

????@GetMapping
????public?Map?hello(String?name)?{
????????String?qualifiedBeanName?=?HelloService.class.getName();
????????assert?appCtx.containsBean(qualifiedBeanName)?:?"Spring容器中找不到名稱為"?+?qualifiedBeanName?+?"的Bean";
????????String[]?beanNames?=?appCtx.getBeanNamesForType(HelloService.class);
????????assert?beanNames.length?==?1?&&?qualifiedBeanName.equals(beanNames[0]);
????????return?ImmutableMap.of("name",?name,?"helloResult",?helloService.sayHello(name));
????}

????@Override
????public?void?setApplicationContext(ApplicationContext?applicationContext)?throws?BeansException?{
????????appCtx?=?applicationContext;
????}

}

? ??創(chuàng)建一個啟動類:

package?com.swj.mj.web;

@SpringBootApplication
public?class?WebApplication?{

????public?static?void?main(String[]?args)?throws?Exception?{
????????SpringApplication.run(WebApplication.class,?args);
????}

}

? ? 添加JVM選項-ea(激活斷言功能)并啟動容器后(啟動類的所在包路徑為com.swj.mj.web,并且沒有額外配置掃描包,所以默認(rèn)掃描的包是com.swj.mj.web),可以看到如下日志打印,說明@Import可以用來注冊Bean。

@Import注冊SpringBean

? ??同時,請求后可以看到兩個斷言都成立,說明通過@Import注冊的SpringBean的beanName為類對應(yīng)的類完全限定名。

@Import注冊的SpringBean對應(yīng)名稱

? ??如果我們?nèi)サ袅?#64;Import(HelloService.class),那么應(yīng)用啟動后將直接失敗并終止:

去掉@Import后將直接找不到類以致于啟動失敗

導(dǎo)入配置類

? ??第二個功能是直接導(dǎo)入配置類。我們在原先的基礎(chǔ)上新增一個配置類HelloServiceConfiguration:

@Configuration
public?class?HelloServiceConfiguration?{

????@Bean
????@ConditionalOnMissingBean
????public?HelloService?helloService()?{
????????return?new?HelloService();
????}

}

? ??該配置類會在容器中沒有helloService這個Bean時自動注冊HelloService。然后修改HelloController:

@RestController
@AllArgsConstructor
@RequestMapping("/hello")
@Import(HelloServiceConfiguration.class)
public?class?HelloController?implements?ApplicationContextAware?{

????private?static?ApplicationContext?appCtx;
????private?final?HelloService?helloService;

????@GetMapping
????public?Map?hello(String?name)?{
????????String?beanName?=?"helloService";
????????assert?appCtx.containsBean(beanName)?:?"Spring容器中找不到名稱為"?+?beanName?+?"的Bean";
????????String[]?beanNames?=?appCtx.getBeanNamesForType(HelloService.class);
????????assert?beanNames.length?==?1?&&?beanName.equals(beanNames[0]);
????????return?ImmutableMap.of("name",?name,?"helloResult",?helloService.sayHello(name));
????}

????@Override
????public?void?setApplicationContext(ApplicationContext?applicationContext)?throws?BeansException?{
????????appCtx?=?applicationContext;
????}

}

? ??這里通過@Import(HelloServiceConfiguration.class)直接導(dǎo)入配置類,由配置類注冊HelloService,這時候的beanName就不是類完全限定名了,而是方法名helloService。

? ??實際上HelloServiceConfiguration同樣會被注冊到Spring容器中,通過appCtx.getBean(HelloServiceConfiguration.class)可以得到配置類。所以可以說第一個功能與第二個功能是重合的,這點在源碼上也可以得到解釋,具體可以參考o(jì)rg.springframework.context.annotation.ConfigurationClassParser#processImports。本篇不講源碼哈,只介紹如何使用。

選擇性裝載配置類

? ??第三個功能是通過實現(xiàn)org.springframework.context.annotation.ImportSelector接口完成動態(tài)開啟配置類。

? ??首先看一下ImportSelector接口的定義:

public?interface?ImportSelector?{

?/**
??*?Select?and?return?the?names?of?which?class(es)?should?be?imported?based?on
??*?the?{@link?AnnotationMetadata}?of?the?importing?@{@link?Configuration}?class.
??*?@return?the?class?names,?or?an?empty?array?if?none
??*/
?String[]?selectImports(AnnotationMetadata?importingClassMetadata);

?/**
??*?Return?a?predicate?for?excluding?classes?from?the?import?candidates,?to?be
??*?transitively?applied?to?all?classes?found?through?this?selector's?imports.
??*?

If?this?predicate?returns?{@code?true}?for?a?given?fully-qualified
??*?class?name,?said?class?will?not?be?considered?as?an?imported?configuration
??*?class,?bypassing?class?file?loading?as?well?as?metadata?introspection.
??*?@return?the?filter?predicate?for?fully-qualified?candidate?class?names
??*?of?transitively?imported?configuration?classes,?or?{@code?null}?if?none
??*?@since?5.2.4
??*/


?@Nullable
?default?Predicate?getExclusionFilter()?{
??return?null;
?}

}

? ??主要關(guān)注String[] selectImports(AnnotationMetadata importingClassMetadata)方法,參數(shù)importingClassMetadata表示使用了@Import(? extends ImportSelector)注解的類的元數(shù)據(jù)。通過該參數(shù)可以實現(xiàn)獲取一些組合注解的自定義屬性等內(nèi)容,從而實現(xiàn)選擇性裝載配置類。

「注意這個方法的返回值不能是null,極端情況請返回空數(shù)組,否則運行將拋出NPE。」

? ??讓我們先創(chuàng)建一個SpringUtil:

package?com.swj.mj.autoconfig.util;

public?class?SpringUtil?implements?BeanFactoryAware,?ApplicationContextAware,?Ordered?{

????private?static?ApplicationContext?appCtx;
????private?static?BeanFactory?beanFactory;

????public?static?ApplicationContext?getAppCtx()?{
????????return?appCtx;
????}

????public?static?BeanFactory?getBeanFactory()?{
????????return?beanFactory;
????}

????@Override
????public?void?setBeanFactory(BeanFactory?beanFactory)?throws?BeansException?{
????????SpringUtil.beanFactory?=?beanFactory;
????}

????@Override
????public?void?setApplicationContext(ApplicationContext?applicationContext)?throws?BeansException?{
????????SpringUtil.appCtx?=?applicationContext;
????}

????@Override
????public?int?getOrder()?{
????????return?HIGHEST_PRECEDENCE?+?1;
????}

}

? ??這個類主要用于通過靜態(tài)持有Spring的ApplicationContext和BeanFactory,后續(xù)可以直接通過該類訪問Spring容器。然后創(chuàng)建我們的ImportSelector配置類裝載選擇器:

package?com.swj.mj.autoconfig.configuration;

public?class?CustomImportSelector?implements?ImportSelector?{

????@Override
????public?String[]?selectImports(AnnotationMetadata?importingClassMetadata)?{
????????//?importingClassMetadata?指使用?@Import(HelloServiceImportSelector)?時?@Import?注解所在類的元數(shù)據(jù)
????????String?enableCustomConfig?=?EnableCustomConfig.class.getName();
????????if?(importingClassMetadata.hasAnnotation(enableCustomConfig))?{
????????????Map?attrs?=?importingClassMetadata.getAnnotationAttributes(enableCustomConfig);if?(MapUtils.isNotEmpty(attrs))?{
????????????????String?registerUtil?=?Optional.ofNullable(attrs.get("registerUtil")).map(Object::toString).orElse("false");if?(Boolean.parseBoolean(registerUtil))?{return?new?String[]{
????????????????????????????HelloServiceConfiguration.class.getName(),
????????????????????????????SpringUtil.class.getName()
????????????????????};
????????????????}
????????????}
????????}return?new?String[]{
????????????????HelloServiceConfiguration.class.getName()
????????};
????}
}

? ??然后創(chuàng)建我們的EnableCustomConfig注解:

package?com.swj.mj.autoconfig.annotation;

@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(CustomImportSelector.class)
public?@interface?EnableCustomConfig?{

????/**
?????*?是否注冊?{@link?com.swj.mj.autoconfig.util.SpringUtil}?工具類,默認(rèn)為?{@literal?true}
?????*/
????boolean?registerUtil()?default?true;

}

? ??這里簡單解釋一下:首先@EnableCustomConfig注解組合了@Import(CustomImportSelector.class),這樣在CustomImportSelector#selectImports方法中可以通過importingClassMetadata獲取得到@EnableCustomConfig注解的屬性配置。然后通過該配置實現(xiàn)添加裝載配置。這里我們判斷registerUtil是否為true(默認(rèn)為true),為true時將裝載SpringUtil以便后續(xù)可以通過SpringUtil訪問Spring容器。

? ??接著調(diào)整HelloController:

@RestController
@AllArgsConstructor
@RequestMapping("/hello")
@EnableCustomConfig
public?class?HelloController?{

????private?final?HelloService?helloService;

????@GetMapping
????public?Map?hello(String?name)?{
????????ApplicationContext?appCtx?=?SpringUtil.getAppCtx();
????????assert?Objects.nonNull(appCtx)?:?"appCtx?為?null";
????????assert?appCtx.containsBean(SpringUtil.class.getName());
????????assert?appCtx.containsBean(HelloServiceConfiguration.class.getName());
????????assert?appCtx.getBean(HelloService.class)?==?helloService;
????????return?ImmutableMap.of("name",?name,?"helloResult",?helloService.sayHello(name));
????}

}

? ??這里通過添加@EnableCustomConfig激活聯(lián)動激活@Import(CustomImportSelector.class)以注冊SpringUtil和HelloServiceConfiguration,由于默認(rèn)registerUtil為true,所以會注冊SpringUtil。啟動應(yīng)用后能訪問http://localhost:8080/hello?name=Reka表示斷言均成立,容器中確實注冊了對應(yīng)的Bean。

? ??接著我們調(diào)整@EnableCustomConfig為@EnableCustomConfig(registerUtil = false),再次啟動容器訪問http://localhost:8080/hello?name=Reka,這時候會發(fā)現(xiàn)斷言失敗:

通過注解屬性配置選擇性裝載配置類

動態(tài)注冊Bean

? ??這是@Import的第四個功能,通過org.springframework.context.annotation.ImportBeanDefinitionRegistrar自由注冊SpringBean。該接口的定義如下:

public?interface?ImportBeanDefinitionRegistrar?{

?default?void?registerBeanDefinitions(AnnotationMetadata?importingClassMetadata,?BeanDefinitionRegistry?registry,
???BeanNameGenerator?importBeanNameGenerator)?{

??registerBeanDefinitions(importingClassMetadata,?registry);
?}

?default?void?registerBeanDefinitions(AnnotationMetadata?importingClassMetadata,?BeanDefinitionRegistry?registry)?{
?}

}

? ??主要看void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry),其中參數(shù)importingClassMetadata與ImportSelector里的參數(shù)效用一致,registry參數(shù)簡單理解是Bean注冊器,通過它可以往Spring容器中注冊Bean。現(xiàn)在讓我們通過動態(tài)注冊Bean的方式實現(xiàn)CustomImportSelector的功能。

package?com.swj.mj.autoconfig.configuration;

public?class?CustomImportBeanDefinitionRegistrar?implements?ImportBeanDefinitionRegistrar?{

????@Override
????public?void?registerBeanDefinitions(AnnotationMetadata?importingClassMetadata,?BeanDefinitionRegistry?registry,?BeanNameGenerator?importBeanNameGenerator)?{
????????AnnotationAttributes?attrs?=?AnnotatedElementUtils.getMergedAnnotationAttributes(
????????????????ClassUtils.resolveClassName(importingClassMetadata.getClassName(),?null),
????????????????EnableCustomBean.class);
????????if?(MapUtils.isNotEmpty(attrs)?&&?BooleanUtils.isTrue(attrs.getBoolean("registerUtil")))?{
????????????registry.registerBeanDefinition("springUtil",?new?RootBeanDefinition(SpringUtil.class));
????????}
????????registry.registerBeanDefinition("helloService",?new?RootBeanDefinition(HelloService.class));
????}

}
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(CustomImportBeanDefinitionRegistrar.class)
public?@interface?EnableCustomBean?{

????/**
?????*?是否注冊?{@link?com.swj.mj.autoconfig.util.SpringUtil}?工具類,默認(rèn)為?{@literal?true}
?????*/
????boolean?registerUtil()?default?true;

}

? ? CustomImportBeanDefinitionRegistrar的邏輯與CustomImportSelector基本一致,只不過最后不是返回配置類,而是直接通過org.springframework.beans.factory.support.BeanDefinitionRegistry#registerBeanDefinition注冊Bean。最后調(diào)整一下HelloController:

@RestController
@AllArgsConstructor
@RequestMapping("/hello")
@EnableCustomBean
public?class?HelloController?{

????private?final?HelloService?helloService;

????@GetMapping
????public?Map?hello(String?name)?{
????????ApplicationContext?appCtx?=?SpringUtil.getAppCtx();
????????assert?Objects.nonNull(appCtx)?:?"appCtx?為?null";
????????assert?appCtx.containsBean("springUtil");
????????assert?appCtx.getBean(HelloService.class)?==?helloService;
????????return?ImmutableMap.of("name",?name,?"helloResult",?helloService.sayHello(name));
????}

}

? ??實際運行效果與CustomImportSelect和@EnableCustomConfig一致,請讀者自行驗證。

? ??如果僅僅是這樣,你是不是認(rèn)為就沒必要有ImportBeanDefinitionRegistrar了。實際上通過該接口我們可以定義自己的容器Bean注解。實現(xiàn)很多特殊的功能:比如在三維家美家技術(shù)中使用到了SpringCloud Stream,但SCS的@org.springframework.cloud.stream.annotation.EnableBinding注解很麻煩,每次引入新的@Input和@Output都要將對應(yīng)接口添加到@EnableBinding中,三維家美家通過ImportBeanDefinitionRegistrar和自定義注解實現(xiàn)新的Input/Ouput Bean掃描注冊流程。后續(xù)會計劃將該功能提PR到SpringCloud項目中。

? ??通過自定義注冊和ImportBeanDefinitionRegistrar可以更靈活地自定義Bean注冊邏輯,限于篇幅原因,我們往后有機會再講。各位,今天關(guān)于@Import的基本用法是否掌握了呢,建議可以自己實踐一次以加深理解,如果對源碼有興趣,可以閱讀org.springframework.context.annotation.ConfigurationClassParser類。

總結(jié)

以上是生活随笔為你收集整理的idea去掉无用import类_@Import注解的魅力的全部內(nèi)容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網(wǎng)站內(nèi)容還不錯,歡迎將生活随笔推薦給好友。