Spring Boot通過ImportBeanDefinitionRegistrar動態注入Bean
在閱讀Spring Boot原始碼時,看到Spring Boot中大量使用ImportBeanDefinitionRegistrar來實現Bean的動態注入。它是Spring中一個強大的擴充套件介面。本篇文章來講講它相關使用。
Spring Boot中的使用
在Spring Boot 內建容器的相關自動配置中有一個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; // 實現BeanFactoryAware的方法,設定BeanFactory @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { if (beanFactory instanceof ConfigurableListableBeanFactory) { this.beanFactory = (ConfigurableListableBeanFactory) beanFactory; } } // 註冊一個WebServerFactoryCustomizerBeanPostProcessor @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { if (this.beanFactory == null) { return; } registerSyntheticBeanIfMissing(registry, "webServerFactoryCustomizerBeanPostProcessor", WebServerFactoryCustomizerBeanPostProcessor.class); registerSyntheticBeanIfMissing(registry, "errorPageRegistrarBeanPostProcessor", ErrorPageRegistrarBeanPostProcessor.class); } // 檢查並註冊Bean private void registerSyntheticBeanIfMissing(BeanDefinitionRegistry registry, String name, Class<?> beanClass) { // 檢查指定型別的Bean name陣列是否存在,如果不存在則建立Bean並注入到容器中 if (ObjectUtils.isEmpty(this.beanFactory.getBeanNamesForType(beanClass, true, false))) { RootBeanDefinition beanDefinition = new RootBeanDefinition(beanClass); beanDefinition.setSynthetic(true); registry.registerBeanDefinition(name, beanDefinition); } } } }
在這個自動配置類中,基本上展示了ImportBeanDefinitionRegistrar最核心的用法。這裡該介面主要用來註冊BeanDefinition。
BeanPostProcessorsRegistrar實現了ImportBeanDefinitionRegistrar介面和BeanFactoryAware介面。其中BeanFactoryAware介面的實現是用來暴露Spring的ConfigurableListableBeanFactory物件。
而實現registerBeanDefinitions方法則是用來對Bean的動態注入,這裡注入了WebServerFactoryCustomizerBeanPostProcessor和ErrorPageRegistrarBeanPostProcessor。
簡單瞭解了Spring Boot中的一個使用例項,下面我們總結一下使用方法,並自己實現一個類似的功能。
ImportBeanDefinitionRegistrar使用
Spring官方通過ImportBeanDefinitionRegistrar實現了@Component、@Service等註解的動態注入機制。
很多三方框架整合Spring的時候,都會通過該介面,實現掃描指定的類,然後註冊到spring容器中。 比如Mybatis中的Mapper介面,springCloud中的FeignClient介面,都是通過該介面實現的自定義註冊邏輯。
所有實現了該介面的類的都會被ConfigurationClassPostProcessor處理,ConfigurationClassPostProcessor實現了BeanFactoryPostProcessor介面,所以ImportBeanDefinitionRegistrar中動態註冊的bean是優先於依賴其的bean初始化,也能被aop、validator等機制處理。
基本步驟:
- 實現ImportBeanDefinitionRegistrar介面;
- 通過registerBeanDefinitions實現具體的類初始化;
- 在@Configuration註解的配置類上使用@Import匯入實現類;
簡單示例
這裡實現一個非常簡單的操作,自定義一個@Mapper註解(並非Mybatis中的Mapper實現),實現類似@Component的功能,添加了@Mapper註解的類會被自動載入到spring容器中。
首先建立@Mapper註解。
@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
public @interface Mapper {
}
建立UserMapper類,用於使用@Mapper注。
@Mapper
public class UserMapper {
}
定義ImportBeanDefinitionRegistrar的實現類MapperAutoConfigureRegistrar。如果需要獲取Spring中的一些資料,可實現一些Aware介面,這實現了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;
}
}
在上面程式碼中,通過ResourceLoaderAware介面的setResourceLoader方法獲得到了ResourceLoader物件。
在registerBeanDefinitions方法中,藉助ClassPathBeanDefinitionScanner類的實現類來掃描獲取需要註冊的Bean。
MapperBeanDefinitionScanner的實現如下:
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方法的引數為包含Mapper的AnnotationTypeFilter。
當然也可以通過excludeFilters指定不載入的型別。這兩個方法由它們的父類ClassPathScanningCandidateComponentProvider提供的。
完成了上面的定義,則進行最後一步引入操作了。建立一個自動配置類MapperAutoConfig,並通過@Import引入自定義的Registrar。
@Configuration
@Import(MapperAutoConfigureRegistrar.class)
public class MapperAutoConfig {
}
至此,整個程式碼的功能已經編寫完成,下面寫一個單元測試。
@RunWith(SpringRunner.class)
@SpringBootTest
public class MapperAutoConfigureRegistrarTest {
@Autowired
UserMapper userMapper;
@Test
public void contextLoads() {
System.out.println(userMapper.getClass());
}
}
執行單元測試程式碼,會發現列印如下日誌:
class com.secbro2.learn.mapper.UserMapper
說明UserMapper已經被例項化成功,並注入Spring容器當中。
小結
當然,這裡的UserMapper並不介面,這裡的實現也並不是Mybatis中的實現形式。只是為了演示該功能的簡單示例。需要注意的是文中提到了兩種實現的例項,第一種是Spring Boot中的實現,第二種是我們的Mapper例項。展現了兩種不同方法的註冊的操作,但整個使用流程是一致的,讀者注意仔細品味,並在此基礎上進行拓展更復雜的功能。
原文連結:《Spring Boot通過ImportBeanDefinitionRegistrar動態注入Bean》
相關推薦
Spring Boot通過ImportBeanDefinitionRegistrar動態注入Bean
在閱讀Spring Boot原始碼時,看到Spring Boot中大量使用ImportBeanDefinitionRegistrar來實現Bean的動態注入。它是Spring中一個強大的擴充套件介面。本篇文章來講講它相關使用。 Spring Boot中的使用 在Spring Boot 內建容器的相關自動配置中
已經解決 spring boot 攔截器中注入bean
已經解決 spring boot 攔截器中如何注入bean的問題 @Configuration public class MyWebAppConfigurer extends WebMvcConfigurerAdapter @Bean RequestInterce
spring boot 學習(三) — 依賴注入 @Bean
spring 4推薦的@Configuration 和@bean 的用法,這樣我們可以省去繁瑣的配置檔案 第一步 建一個Maven工程 第二步新增依賴 pom.xml <?xml version="1.0" encoding="UTF-8"?> <pro
記錄Spring Boot大坑一個,在bean中如果有@Test單元測試,不會注入成功
記錄Spring Boot大坑一個,在bean中如果有@Test單元測試,不會注入成功 記錄Spring Boot大坑一個,在bean中如果有@Test單元測試,不會注入成功 記錄Spring Boot大坑一個,在bean中如果有@Test單元測試,不會注入成功 org.springframework.
Spring的《XML顯式裝配bean》之通過構造器注入Bean
本文主要講解兩點: 1.怎麼樣宣告一個bean 2.通過構造器注入bean 1. 怎麼樣宣告一個bean? 1) 建立一個類: package spring.ch1.topic5; public class Song {
Spring中通過註解來配置bean以及自動注入
今天看到一篇好文章,寫的很是詳細。再加上自己的理解和補充,成了這一篇文章。文後會獻上原文連結。 使用Spring經常性的需要: 通過註解配置bean 基於註解配置bean 基於註解來配置bean的屬性 ----------------------
Spring boot將配置屬性注入到bean類中
一、@ConfigurationProperties註解的使用 看配置檔案,我的是yaml格式的配置: // file application.yml my: servers: - dev.bar.com - foo.bar.co
spring boot 通過session 取數據
get ets attribute ini () fig spring boot word //先把數據存入session 中 LoginInfo loginInfo = userService.login(userName, password); reque
Spring運行時動態註冊bean
factory context 創建 contex ner 註入 ref 刪除 定義 在spring運行時,動態的添加bean,dapeng框架在解析xml的字段時,使用到了動態註冊,註冊了一個實現了FactoryBean類! 定義一個沒有被Spring管理的Cont
記錄Spring Boot大坑一個,在bean中如果有@Test單元測試,不會註入成功
記錄 one except frame oot beans org init def 記錄Spring Boot大坑一個,在bean中如果有@Test單元測試,不會註入成功 記錄Spring Boot大坑一個,在bean中如果有@Test單元測試,不會註入成功 記錄Sp
spring boot去除掃描自動注入依賴方法——Spring常用註解使用方法
問題: 最近做專案的時候,需要引入其他的jar。然後還需要掃描這些jar裡的某些bean。於 是使用註解:@ComponentScan 這個註解直接指定包名就可以,它會去掃描這個包下所有的class,然後判斷是否解析: 原始碼: public @interface SpringBoo
spring-boot 屬性定義和配置bean
自定義bean屬性 1.定義bean屬性 // 通過@ConfigurationProperties載入properties檔案內的配置, // 通過prefix屬性指定properties的配置的字首,通過locations指定properties檔案的位置 @ConfigurationProperti
Spring Boot + Mybatis + Druid 動態切換多資料來源
在大型應用程式中,配置主從資料庫並使用讀寫分離是常見的設計模式。 在Spring應用程式中,要實現讀寫分離,最好不要對現有程式碼進行改動,而是在底層透明地支援。 這樣,就需要我們再一個專案中,配置兩個,乃至多個數據源。 今天,小編先來介紹一下自己配置動態多資料來源的步驟 專案簡介: 編譯器:ID
Spring Boot 系列之五:Spring Boot 通過devtools進行熱部署
前面已經分享過四篇學習文章: 1、Spring Boot 系統之一:Spring Boot 入門 2、Spring Boot 系統之二:Spring Boot 修改預設埠號和context path 3、Spring Boot 系統之三:Spring Boot 整合JdbcTemplat
spring boot 通過過濾器修改request中的body引數
1。先使用Wrapper,重寫HttpServletRequestWrapper。 package com.cepht.platform.icl.config; import java.util.Enumeration; import java.util.Map; imp
Spring 多執行緒下注入bean問題詳解
本文介紹了Spring 多執行緒下注入bean問題詳解,分享給大家,具體如下: 問題 Spring中多執行緒注入userThreadService注不進去,顯示userThreadService為null異常 程式碼如下: public class UserThreadTask implements
spring boot通過自定義註解和AOP攔截指定的請求
本文主要通過切面類和自定註解的方式,攔截指定的介面(程式碼中已經作了詳細的說明) 目錄 一 準備工作 三 切面類 五 測試結果 一 準備工作 1.1 新增依賴 通過spr
Spring Boot通過Profiles實現多環境下配置切換
1、在yml中使用pom定義的maven屬性變數 格式:@[email protected] spring: application: name: dream-web-gateway profiles: active: '@[
Spring Boot + Mybatis 實現動態資料來源
<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd"
Spring Boot啟動時動態切換每個環境的配置檔案
開發專案一般是開發環境,測試環境,和生產環境,例如:Spring Boot的application.properties配置如下 application-dev1.priperties相當於開發環境,以此類推,當你啟動Spring Boot時,切換每個環