ImportBeanDefinitionRegistrar手動(dòng)控制BeanDefinition創(chuàng)建注冊(cè)詳解
一、什么是ImportBeanDefinitionRegistrar
ImportBeanDefinitionRegistrar接口是也是spring的擴(kuò)展點(diǎn)之一,ImportBeanDefinitionRegistrar類只能通過(guò)其他類 @Import的方式來(lái)加載,通常是啟動(dòng)類或配置類。它可以支持我們自己寫的代碼封裝成BeanDefinition對(duì)象;實(shí)現(xiàn)此接口的類會(huì)回調(diào)postProcessBeanDefinitionRegistry方法,注冊(cè)到spring容器中。
把bean注入到spring容器不止有 @Service @Component等注解方式;還可以實(shí)現(xiàn)此接口,這種方式是最靈活的,能在registerBeanDefinitions
方法中獲取到BeanDefinitionRegistry
容器注冊(cè)對(duì)象,可以手動(dòng)控制BeanDefinition的創(chuàng)建和注冊(cè)。
public interface ImportBeanDefinitionRegistrar { default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) { this.registerBeanDefinitions(importingClassMetadata, registry); } default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { } }
二、ImportBeanDefinitionRegistrar使用很簡(jiǎn)單
加載指定類
定義一個(gè)類TestImportBeanDefinitionRegistrar實(shí)現(xiàn)ImportBeanDefinitionRegistrar接口,在配置類TestConfiguration加上注解@Import一個(gè)TestImportBeanDefinitionRegistrar實(shí)現(xiàn)類,重寫registerBeanDefinitions方法,手動(dòng)注冊(cè)bean,實(shí)現(xiàn)注冊(cè)BeanDefinition到容器中,也可以實(shí)現(xiàn)一些Aware接口,以便獲取Spring的一些數(shù)據(jù)。加載指定DataSource類如下:
public class TestImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar,ResourceLoaderAware,BeanFactoryAware { private static ResourceLoader resourceLoader; private static BeanFactory beanFactory; @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { //創(chuàng)建DataSourceBean GenericBeanDefinition beanDefinition = new GenericBeanDefinition(); beanDefinition.setBeanClass(DataSource.class); beanDefinition.setSynthetic(true); MutablePropertyValues mpv = beanDefinition.getPropertyValues(); //spring名稱約定為defaultTargetDataSource和targetDataSources mpv.addPropertyValue("defaultTargetDataSource", defaultTargetDataSource); mpv.addPropertyValue("targetDataSources", DataSourceSet.getTargetDataSourcesMap()); beanDefinitionRegistry.registerBeanDefinition("dataSource", beanDefinition); } @Override public void setBeanFactory(BeanFactory beanFactory) throws BeansException { this.beanFactory=beanFactory; } @Override public void setResourceLoader(ResourceLoader resourceLoader) { this.resourceLoader=resourceLoader; } } @Import(TestImportBeanDefinitionRegistrar.class) @Configuration public class TestConfiguration { }
這樣就注冊(cè)了一個(gè)bean名字是dataSource,有兩個(gè)屬性分別是defaultTargetDataSource和targetDataSources。那么使用這個(gè)類直接用 @Autowired和@Resource注入即可。
加載掃描器類
如果我們并不知道需要register哪些bean。這里我們還需要借助一個(gè)掃描器類ClassPathBeanDefinitionScanner
,通過(guò)掃描器獲取我們需要注冊(cè)的bean。首先創(chuàng)建一個(gè)@Mapper
注解,再新建一個(gè)CountryMapper
類,使用該Mapper注解。
@Documented @Inherited @Retention(RetentionPolicy.RUNTIME) @Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER}) public @interface Mapper { } @Mapper public class CountryMapper { }
創(chuàng)建一個(gè)MyClassPathBeanDefinitionScanner
類繼承ClassPathBeanDefinitionScanner
,掃描使用@Mapper
的注解的類,ClassPathBeanDefinitionScanner
又繼承ClassPathScanningCandidateComponentProvider
類,ClassPathScanningCandidateComponentProvider
中有兩個(gè)TypeFilter集合,includeFilters、excludeFilters。滿足任意includeFilters會(huì)被加載,同樣的滿足任意excludeFilters不會(huì)被加載。
@Slf4j public class MyClassPathBeanDefinitionScanner extends ClassPathBeanDefinitionScanner{ public MyClassPathBeanDefinitionScanner(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); } }
registerFilters()方法
核心代碼就是registerFilters()方法,然后在我們的ImportBeanDefinitionRegistrar
實(shí)現(xiàn)類中調(diào)用:
public class MapperAutoConfiguredMyBatisRegistrar implements ImportBeanDefinitionRegistrar{ @Override public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) { MyClassPathBeanDefinitionScanner scanner = new MyClassPathBeanDefinitionScanner(registry, false); scanner.setResourceLoader(resourceLoader); scanner.registerFilters(); scanner.doScan("com.faderw.school.domain"); } }
這里我們自定義帶有@Mapper注解的類CountryMapper就被注入到IOC容器了,可以直接使用噢。
三、ImportBeanDefinitionRegistrar原理
TestImportBeanDefinitionRegistrar是被TestConfiguration類import導(dǎo)入的,所以要加載到TestImportBeanDefinitionRegistrar必然要先加載TestConfiguration才行;所以先看Configuration的代碼,找到ConfigurationClassPostProcessor類,它實(shí)現(xiàn)了BeanDefinitionRegistryPostProcessor接口。
Map<String, BeanDefinitionRegistryPostProcessor> beanMap = beanFactory.getBeansOfType(BeanDefinitionRegistryPostProcessor.class, true, false); List<BeanDefinitionRegistryPostProcessor> registryPostProcessorBeans = new ArrayList<BeanDefinitionRegistryPostProcessor>(beanMap.values()); OrderComparator.sort(registryPostProcessorBeans); for (BeanDefinitionRegistryPostProcessor postProcessor : registryPostProcessorBeans) { postProcessor.postProcessBeanDefinitionRegistry(registry); }
從spring容器中拿到實(shí)現(xiàn)了BeanDefinitionRegistryPostProcessor接口的類。拿到后執(zhí)行對(duì)應(yīng)的postProcessBeanDefinitionRegistry方法:
//從invokeBeanFactoryPostProcessors方法調(diào)過(guò)來(lái) public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) { RootBeanDefinition iabpp = new RootBeanDefinition(ImportAwareBeanPostProcessor.class); iabpp.setRole(BeanDefinition.ROLE_INFRASTRUCTURE); registry.registerBeanDefinition(IMPORT_AWARE_PROCESSOR_BEAN_NAME, iabpp); //取得registry的id并做判重處理或記錄 int registryId = System.identityHashCode(registry); if (this.registriesPostProcessed.contains(registryId)) { throw new IllegalStateException( "postProcessBeanDefinitionRegistry already called for this post-processor against " + registry); } if (this.factoriesPostProcessed.contains(registryId)) { throw new IllegalStateException( "postProcessBeanFactory already called for this post-processor against " + registry); } //保存處理過(guò)的registry,避免重復(fù)處理 this.registriesPostProcessed.add(registryId); //處理java配置形式的bean定義 processConfigBeanDefinitions(registry); }
最后調(diào)用了processConfigBeanDefinitions方法處理java配置形式的bean定義:
public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) { Set<BeanDefinitionHolder> configCandidates = new LinkedHashSet<BeanDefinitionHolder>(); //加載當(dāng)前已知所有bean定義 for (String beanName : registry.getBeanDefinitionNames()) { BeanDefinition beanDef = registry.getBeanDefinition(beanName); // 判斷對(duì)應(yīng)bean是否為配置類,如果是,則加入到configCandidates // @Configuration, @Component, @ComponentScan, @Import, @Bean等注解 if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) { configCandidates.add(new BeanDefinitionHolder(beanDef, beanName)); } } //如果找不到@configuration類,則立即返回 if (configCandidates.isEmpty()) { return; } ........... // 實(shí)例化ConfigurationClassParser 為了解析 各個(gè)配置類 ConfigurationClassParser parser = new ConfigurationClassParser( this.metadataReaderFactory, this.problemReporter, this.environment, this.resourceLoader, this.componentScanBeanNameGenerator, registry); for (BeanDefinitionHolder holder : configCandidates) { BeanDefinition bd = holder.getBeanDefinition(); System.out.println("ConfigurationClassPostProcessor bd " + bd.getBeanClassName()); try { if (bd instanceof AbstractBeanDefinition && ((AbstractBeanDefinition) bd).hasBeanClass()) { parser.parse(((AbstractBeanDefinition) bd).getBeanClass(), holder.getBeanName()); } else { parser.parse(bd.getBeanClassName(), holder.getBeanName()); } } catch (IOException ex) { throw new BeanDefinitionStoreException("Failed to load bean class: " + bd.getBeanClassName(), ex); } } parser.validate(); ......
ConfigurationClassParser#parse方法:
public void parse(String className, String beanName) throws IOException { MetadataReader reader = this.metadataReaderFactory.getMetadataReader(className); processConfigurationClass(new ConfigurationClass(reader, beanName)); }
processConfigurationClass方法:
protected void processConfigurationClass(ConfigurationClass configClass) throws IOException { ................... do { //重點(diǎn) metadata = doProcessConfigurationClass(configClass, metadata); } while (metadata != null); ................. }
doProcessConfigurationClass方法:
protected AnnotationMetadata doProcessConfigurationClass(ConfigurationClass configClass, AnnotationMetadata metadata) throws IOException { // Recursively process any member (nested) classes first processMemberClasses(metadata); // Process any @PropertySource annotations //處理@PropertySource 加載外面資源文件 AnnotationAttributes propertySource = MetadataUtils.attributesFor(metadata, org.springframework.context.annotation.PropertySource.class); if (propertySource != null) { processPropertySource(propertySource); } // Process any @ComponentScan annotations //處理 @ComponentScan 掃描包 AnnotationAttributes componentScan = MetadataUtils.attributesFor(metadata, ComponentScan.class); if (componentScan != null) { // The config class is annotated with @ComponentScan -> perform the scan immediately Set<BeanDefinitionHolder> scannedBeanDefinitions = this.componentScanParser.parse(componentScan, metadata.getClassName()); // Check the set of scanned definitions for any further config classes and parse recursively if necessary for (BeanDefinitionHolder holder : scannedBeanDefinitions) { if (ConfigurationClassUtils.checkConfigurationClassCandidate(holder.getBeanDefinition(), this.metadataReaderFactory)) { this.parse(holder.getBeanDefinition().getBeanClassName(), holder.getBeanName()); } } } //處理@Import注解 // Process any @Import annotations Set<Object> imports = new LinkedHashSet<Object>(); Set<String> visited = new LinkedHashSet<String>(); collectImports(metadata, imports, visited); if (!imports.isEmpty()) { processImport(configClass, metadata, imports, true); } // Process any @ImportResource annotations if (metadata.isAnnotated(ImportResource.class.getName())) { AnnotationAttributes importResource = MetadataUtils.attributesFor(metadata, ImportResource.class); String[] resources = importResource.getStringArray("value"); Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader"); for (String resource : resources) { String resolvedResource = this.environment.resolveRequiredPlaceholders(resource); configClass.addImportedResource(resolvedResource, readerClass); } } // Process individual @Bean methods //處理@Bean注解 Set<MethodMetadata> beanMethods = metadata.getAnnotatedMethods(Bean.class.getName()); for (MethodMetadata methodMetadata : beanMethods) { configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass)); } // Process superclass, if any if (metadata.hasSuperClass()) { String superclass = metadata.getSuperClassName(); if (!superclass.startsWith("java") && !this.knownSuperclasses.containsKey(superclass)) { this.knownSuperclasses.put(superclass, configClass); // superclass found, return its annotation metadata and recurse if (metadata instanceof StandardAnnotationMetadata) { Class<?> clazz = ((StandardAnnotationMetadata) metadata).getIntrospectedClass(); return new StandardAnnotationMetadata(clazz.getSuperclass(), true); } else { MetadataReader reader = this.metadataReaderFactory.getMetadataReader(superclass); return reader.getAnnotationMetadata(); } } } // No superclass -> processing is complete return null; }
processImport方法:
private void processImport(ConfigurationClass configClass, AnnotationMetadata metadata, Collection<?> classesToImport, boolean checkForCircularImports) throws IOException { if (checkForCircularImports && this.importStack.contains(configClass)) { this.problemReporter.error(new CircularImportProblem(configClass, this.importStack, configClass.getMetadata())); }else { this.importStack.push(configClass); try { for (Object candidate : classesToImport) { Object candidateToCheck = (candidate instanceof Class ? (Class) candidate : this.metadataReaderFactory.getMetadataReader((String) candidate)); //實(shí)現(xiàn)ImportSelector接口的處理 if (checkAssignability(ImportSelector.class, candidateToCheck)) { // Candidate class is an ImportSelector -> delegate to it to determine imports Class<?> candidateClass = (candidate instanceof Class ? (Class) candidate : this.resourceLoader.getClassLoader().loadClass((String) candidate)); ImportSelector selector = BeanUtils.instantiateClass(candidateClass, ImportSelector.class); processImport(configClass, metadata, Arrays.asList(selector.selectImports(metadata)), false); } //實(shí)現(xiàn)ImportBeanDefinitionRegistrar接口的處理 else if (checkAssignability(ImportBeanDefinitionRegistrar.class, candidateToCheck)) { // Candidate class is an ImportBeanDefinitionRegistrar -> // delegate to it to register additional bean definitions Class<?> candidateClass = (candidate instanceof Class ? (Class) candidate : this.resourceLoader.getClassLoader().loadClass((String) candidate)); ImportBeanDefinitionRegistrar registrar = BeanUtils.instantiateClass(candidateClass, ImportBeanDefinitionRegistrar.class); invokeAwareMethods(registrar); //調(diào)用該類的registerBeanDefinitions方法 registrar.registerBeanDefinitions(metadata, this.registry); } else { //候選類不是importSelector或importBeanDefinitionRegistrar //@Configuration類的處理 // Candidate class not an ImportSelector or ImportBeanDefinitionRegistrar -> // process it as a @Configuration class this.importStack.registerImport(metadata, (candidate instanceof Class ? ((Class) candidate).getName() : (String) candidate)); processConfigurationClass(candidateToCheck instanceof Class ? new ConfigurationClass((Class) candidateToCheck, true) : new ConfigurationClass((MetadataReader) candidateToCheck, true)); } } } catch (ClassNotFoundException ex) { throw new NestedIOException("Failed to load import candidate class", ex); } finally { this.importStack.pop(); } } }
最終調(diào)用了DefaultListableBeanFactory的registerBeanDefinition方法,這里就是處理ImportBeanDefinitionRegistrar接口實(shí)現(xiàn)類的地方:
public void registerBeanDefinition(String beanName, BeanDefinition beanDefinition) throws BeanDefinitionStoreException { Assert.hasText(beanName, "Bean name must not be empty"); Assert.notNull(beanDefinition, "BeanDefinition must not be null"); if("configurationTest".equals(beanName)){ System.out.println(" registerBeanDefinition(String beanName, BeanDefinition beanDefinition) " + beanName); } if (beanDefinition instanceof AbstractBeanDefinition) { try { ((AbstractBeanDefinition) beanDefinition).validate(); } catch (BeanDefinitionValidationException ex) { throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Validation of bean definition failed", ex); } } // old? 還記得 “允許 bean 覆蓋” 這個(gè)配置嗎?allowBeanDefinitionOverriding BeanDefinition oldBeanDefinition; synchronized (this.beanDefinitionMap) { // 之后會(huì)看到,所有的 Bean 注冊(cè)后會(huì)放入這個(gè) beanDefinitionMap 中 oldBeanDefinition = this.beanDefinitionMap.get(beanName); // 處理重復(fù)名稱的 Bean 定義的情況 if (oldBeanDefinition != null) { if (!this.allowBeanDefinitionOverriding) { // 如果不允許覆蓋的話,拋異常 throw new BeanDefinitionStoreException(beanDefinition.getResourceDescription(), beanName, "Cannot register bean definition [" + beanDefinition + "] for bean '" + beanName + "': There is already [" + oldBeanDefinition + "] bound."); } else { if (this.logger.isInfoEnabled()) { this.logger.info("Overriding bean definition for bean '" + beanName + "': replacing [" + oldBeanDefinition + "] with [" + beanDefinition + "]"); } } } else { //添加beanDefinitionNames this.beanDefinitionNames.add(beanName); this.frozenBeanDefinitionNames = null; } // 覆蓋 this.beanDefinitionMap.put(beanName, beanDefinition); } if (oldBeanDefinition != null || containsSingleton(beanName)) { resetBeanDefinition(beanName); } } public void removeBeanDefinition(String beanName) throws NoSuchBeanDefinitionException { Assert.hasText(beanName, "'beanName' must not be empty"); synchronized (this.beanDefinitionMap) { BeanDefinition bd = this.beanDefinitionMap.remove(beanName); if (bd == null) { if (this.logger.isTraceEnabled()) { this.logger.trace("No bean named '" + beanName + "' found in " + this); } throw new NoSuchBeanDefinitionException(beanName); } this.beanDefinitionNames.remove(beanName); this.frozenBeanDefinitionNames = null; } resetBeanDefinition(beanName); }
這段代碼主要就是把定義的bean放到beanDefinitionMap里去。beanDefinitionMap維護(hù)的就是bean的定義,當(dāng)需要獲取的時(shí)候就從里面拿到對(duì)應(yīng)的BeanDefinition,根據(jù)BeanDefinition生成一個(gè)對(duì)象。
以上就是ImportBeanDefinitionRegistrar手動(dòng)控制BeanDefinition創(chuàng)建注冊(cè)詳解的詳細(xì)內(nèi)容,更多關(guān)于BeanDefinition創(chuàng)建注冊(cè)的資料請(qǐng)關(guān)注腳本之家其它相關(guān)文章!
相關(guān)文章
arthas排查jvm中CPU占用過(guò)高問(wèn)題解決
這篇文章主要介紹了arthas排查jvm中CPU占用過(guò)高問(wèn)題解決,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步,早日升職加薪2022-09-09從Myeclipse 導(dǎo)入到eclipse中無(wú)法識(shí)別為 web項(xiàng)目 問(wèn)題的解決步驟
這篇文章主要介紹了從Myeclipse 導(dǎo)入到eclipse中無(wú)法識(shí)別為 web項(xiàng)目 問(wèn)題的解決步驟,需要的朋友可以參考下2018-05-05SpringBoot使用AOP+注解實(shí)現(xiàn)簡(jiǎn)單的權(quán)限驗(yàn)證的方法
這篇文章主要介紹了SpringBoot使用AOP+注解實(shí)現(xiàn)簡(jiǎn)單的權(quán)限驗(yàn)證的方法,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-05-05Java數(shù)據(jù)結(jié)構(gòu)之圖的原理與實(shí)現(xiàn)
圖(Graph)是由頂點(diǎn)的有窮非空集合和頂點(diǎn)之間邊的集合組成,通常表示為:G(V,E),其中,G表示一個(gè)圖,V是圖G中頂點(diǎn)的集合,E是圖G中邊的集合。本文將詳細(xì)介紹圖的原理及其代碼實(shí)現(xiàn),需要的可以參考一下2022-01-01XFire構(gòu)建web service客戶端的五種方式
本篇文章主要介紹了XFire構(gòu)建web service客戶端的五種方式。具有很好的參考價(jià)值,下面跟著小編一起來(lái)看下吧2017-01-01Java實(shí)現(xiàn)拖拽文件上傳dropzone.js的簡(jiǎn)單使用示例代碼
本篇文章主要介紹了Java實(shí)現(xiàn)拖拽文件上傳dropzone.js的簡(jiǎn)單使用示例代碼,具有一定的參考價(jià)值,有興趣的可以了解一下2017-07-07