SpringBoot實(shí)現(xiàn)ImportBeanDefinitionRegistrar動(dòng)態(tài)注入
在閱讀Spring Boot源碼時(shí),看到Spring Boot中大量使用ImportBeanDefinitionRegistrar來(lái)實(shí)現(xiàn)Bean的動(dòng)態(tài)注入。它是Spring中一個(gè)強(qiáng)大的擴(kuò)展接口。本篇文章來(lái)講講它相關(guān)使用。
Spring Boot中的使用
在Spring Boot 內(nèi)置容器的相關(guān)自動(dòng)配置中有一個(gè)ServletWebServerFactoryAutoConfiguration類。該類的部分代碼如下:
@Configuration(proxyBeanMethods = false)
@AutoConfigureOrder(Ordered.HIGHEST_PRECEDENCE)
@ConditionalOnClass(ServletRequest.class)
@ConditionalOnWebApplication(type = Type.SERVLET)
@EnableConfigurationProperties(ServerProperties.class)
@Import({ ServletWebServerFactoryAutoConfiguration.BeanPostProcessorsRegistrar.class,
ServletWebServerFactoryConfiguration.EmbeddedTomcat.class,
ServletWebServerFactoryConfiguration.EmbeddedJetty.class,
ServletWebServerFactoryConfiguration.EmbeddedUndertow.class })
public class ServletWebServerFactoryAutoConfiguration {
// ...
/**
* Registers a {@link WebServerFactoryCustomizerBeanPostProcessor}. Registered via
* {@link ImportBeanDefinitionRegistrar} for early registration.
*/
public static class BeanPostProcessorsRegistrar implements ImportBeanDefinitionRegistrar, BeanFactoryAware {
private ConfigurableListableBeanFactory beanFactory;
// 實(shí)現(xiàn)BeanFactoryAware的方法,設(shè)置BeanFactory
@Override
public void setBeanFactory(BeanFactory beanFactory) throws BeansException {
if (beanFactory instanceof ConfigurableListableBeanFactory) {
this.beanFactory = (ConfigurableListableBeanFactory) beanFactory;
}
}
// 注冊(cè)一個(gè)WebServerFactoryCustomizerBeanPostProcessor
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata,
BeanDefinitionRegistry registry) {
if (this.beanFactory == null) {
return;
}
registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor",
WebServerFactoryCustomizerBeanPostProcessor.class);
registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor",
ErrorPageRegistrarBeanPostProcessor.class);
}
// 檢查并注冊(cè)Bean
private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name, Class<?> beanClass) {
// 檢查指定類型的Bean name數(shù)組是否存在,如果不存在則創(chuàng)建Bean并注入到容器中
if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) {
RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass);
beanDefinition.setSynthetic(true);
registry.registerBeanDefinition(name, beanDefinition);
}
}
}在這個(gè)自動(dòng)配置類中,基本上展示了ImportBeanDefinitionRegistrar最核心的用法。這里該接口主要用來(lái)注冊(cè)BeanDefinition。
BeanPostProcessorsRegistrar實(shí)現(xiàn)了ImportBeanDefinitionRegistrar接口和BeanFactoryAware接口。其中BeanFactoryAware接口的實(shí)現(xiàn)是用來(lái)暴露Spring的ConfigurableListableBeanFactory對(duì)象。
而實(shí)現(xiàn)registerBeanDefinitions方法則是用來(lái)對(duì)Bean的動(dòng)態(tài)注入,這里注入了WebServerFactoryCustomizerBeanPostProcessor和ErrorPageRegistrarBeanPostProcessor。
簡(jiǎn)單了解了Spring Boot中的一個(gè)使用實(shí)例,下面我們總結(jié)一下使用方法,并自己實(shí)現(xiàn)一個(gè)類似的功能。
ImportBeanDefinitionRegistrar使用
Spring官方通過(guò)ImportBeanDefinitionRegistrar實(shí)現(xiàn)了@Component、@Service等注解的動(dòng)態(tài)注入機(jī)制。
很多三方框架集成Spring的時(shí)候,都會(huì)通過(guò)該接口,實(shí)現(xiàn)掃描指定的類,然后注冊(cè)到spring容器中。 比如Mybatis中的Mapper接口,springCloud中的FeignClient接口,都是通過(guò)該接口實(shí)現(xiàn)的自定義注冊(cè)邏輯。
所有實(shí)現(xiàn)了該接口的類的都會(huì)被ConfigurationClassPostProcessor處理,ConfigurationClassPostProcessor實(shí)現(xiàn)了BeanFactoryPostProcessor接口,所以ImportBeanDefinitionRegistrar中動(dòng)態(tài)注冊(cè)的bean是優(yōu)先于依賴其的bean初始化,也能被aop、validator等機(jī)制處理。
基本步驟:
- 實(shí)現(xiàn)ImportBeanDefinitionRegistrar接口;
- 通過(guò)registerBeanDefinitions實(shí)現(xiàn)具體的類初始化;
- 在@Configuration注解的配置類上使用@Import導(dǎo)入實(shí)現(xiàn)類;
簡(jiǎn)單示例
這里實(shí)現(xiàn)一個(gè)非常簡(jiǎn)單的操作,自定義一個(gè)@Mapper注解(并非Mybatis中的Mapper實(shí)現(xiàn)),實(shí)現(xiàn)類似@Component的功能,添加了@Mapper注解的類會(huì)被自動(dòng)加載到spring容器中。
首先創(chuàng)建@Mapper注解。
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
public @interface Mapper {
}創(chuàng)建UserMapper類,用于使用@Mapper注。
@Mapper
public class UserMapper {
}定義ImportBeanDefinitionRegistrar的實(shí)現(xiàn)類MapperAutoConfigureRegistrar。如果需要獲取Spring中的一些數(shù)據(jù),可實(shí)現(xiàn)一些Aware接口,這實(shí)現(xiàn)了ResourceLoaderAware。
public class MapperAutoConfigureRegistrar implements ImportBeanDefinitionRegistrar, ResourceLoaderAware {
private ResourceLoader resourceLoader;
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
MapperBeanDefinitionScanner scanner = new MapperBeanDefinitionScanner(registry, false);
scanner.setResourceLoader(resourceLoader);
scanner.registerFilters();
scanner.addIncludeFilter(new AnnotationTypeFilter(Mapper.class));
scanner.doScan("com.secbro2.learn.mapper");
}
@Override
public void setResourceLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
}在上面代碼中,通過(guò)ResourceLoaderAware接口的setResourceLoader方法獲得到了ResourceLoader對(duì)象。
在registerBeanDefinitions方法中,借助ClassPathBeanDefinitionScanner類的實(shí)現(xiàn)類來(lái)掃描獲取需要注冊(cè)的Bean。
MapperBeanDefinitionScanner的實(shí)現(xiàn)如下:
public class MapperBeanDefinitionScanner extends ClassPathBeanDefinitionScanner {
public MapperBeanDefinitionScanner(BeanDefinitionRegistry registry, boolean useDefaultFilters) {
super(registry, useDefaultFilters);
}
protected void registerFilters() {
addIncludeFilter(new AnnotationTypeFilter(Mapper.class));
}
@Override
protected Set<BeanDefinitionHolder> doScan(String... basePackages) {
return super.doScan(basePackages);
}
}MapperBeanDefinitionScanner繼承子ClassPathBeanDefinitionScanner,掃描被@Mapper的注解的類。
在MapperBeanDefinitionScanner中指定了addIncludeFilter方法的參數(shù)為包含Mapper的AnnotationTypeFilter。
當(dāng)然也可以通過(guò)excludeFilters指定不加載的類型。這兩個(gè)方法由它們的父類ClassPathScanningCandidateComponentProvider提供的。
完成了上面的定義,則進(jìn)行最后一步引入操作了。創(chuàng)建一個(gè)自動(dòng)配置類MapperAutoConfig,并通過(guò)@Import引入自定義的Registrar。
@Configuration
@Import(MapperAutoConfigureRegistrar.class)
public class MapperAutoConfig {
}
至此,整個(gè)代碼的功能已經(jīng)編寫完成,下面寫一個(gè)單元測(cè)試。
@RunWith(SpringRunner.class)
@SpringBootTest
public class MapperAutoConfigureRegistrarTest {
@Autowired
UserMapper userMapper;
@Test
public void contextLoads() {
System.out.println(userMapper.getClass());
}
}執(zhí)行單元測(cè)試代碼,會(huì)發(fā)現(xiàn)打印如下日志:
class com.edu.mapper.UserMapper
當(dāng)然,這里的UserMapper并不是接口,這里的實(shí)現(xiàn)也并不是Mybatis中的實(shí)現(xiàn)形式。只是為了演示該功能的簡(jiǎn)單示例。需要注意的是文中提到了兩種實(shí)現(xiàn)的實(shí)例,第一種是Spring Boot中的實(shí)現(xiàn),第二種是我們的Mapper實(shí)例。展現(xiàn)了兩種不同方法的注冊(cè)的操作,但整個(gè)使用流程是一致的,讀者注意仔細(xì)品味,并在此基礎(chǔ)上進(jìn)行拓展更復(fù)雜的功能。
到此這篇關(guān)于SpringBoot實(shí)現(xiàn)ImportBeanDefinitionRegistrar動(dòng)態(tài)注入的文章就介紹到這了,更多相關(guān)SpringBoot 動(dòng)態(tài)注入內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
IntelliJ IDEA JRebel 安裝使用圖文教程(熱部署插件)
IDEA 全稱 IntelliJ IDEA,是java語(yǔ)言開發(fā)的集成環(huán)境,IntelliJ在業(yè)界被公認(rèn)為最好的java開發(fā)工具之一。這篇文章主要介紹了IntelliJ IDEA 熱部署插件JRebel 安裝使用圖文教程,需要的朋友可以參考下2018-03-03
10個(gè)Java解決內(nèi)存溢出OOM的方法詳解
在Java開發(fā)過(guò)程中,有效的內(nèi)存管理是保證應(yīng)用程序穩(wěn)定性和性能的關(guān)鍵,不正確的內(nèi)存使用可能導(dǎo)致內(nèi)存泄露甚至是致命的OutOfMemoryError(OOM),下面我們就來(lái)學(xué)習(xí)一下有哪些解決辦法吧2024-01-01
基于java配置nginx獲取真實(shí)IP代碼實(shí)例
這篇文章主要介紹了基于java配置nginx獲取真實(shí)IP代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-09-09
spring源碼學(xué)習(xí)之bean的初始化以及循環(huán)引用
這篇文章主要給大家介紹了關(guān)于spring源碼學(xué)習(xí)之bean的初始化以及循環(huán)引用的相關(guān)資料,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10
java.lang.UnsupportedOperationException分析及解決辦法
日常開發(fā)中我遇到j(luò)ava.lang.UnsupportedOperationException:異常兩次了,下面這篇文章主要給對(duì)大家介紹了關(guān)于java.lang.UnsupportedOperationException分析及解決辦法,需要的朋友可以參考下2024-03-03
Spring Boot中單例類實(shí)現(xiàn)對(duì)象的注入方式
這篇文章主要介紹了Spring Boot中單例類實(shí)現(xiàn)對(duì)象的注入方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-08-08
java加密MD5實(shí)現(xiàn)及密碼驗(yàn)證代碼實(shí)例
這篇文章主要介紹了java加密MD5實(shí)現(xiàn)及密碼驗(yàn)證代碼實(shí)例,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12
POI通過(guò)模板導(dǎo)出EXCEL文件的實(shí)例
下面小編就為大家?guī)?lái)一篇POI通過(guò)模板導(dǎo)出EXCEL文件的實(shí)例。小編覺(jué)得挺不錯(cuò)的,現(xiàn)在就分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2017-08-08

