ImportBeanDefinitionRegistrar手动控制BeanDefinition创建注册详解

 更新时间:2022年12月26日 11:19:57   作者:刨红薯的小羊竿尔  
这篇文章主要为大家介绍了ImportBeanDefinitionRegistrar手动控制BeanDefinition创建注册详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪

一、什么是ImportBeanDefinitionRegistrar

ImportBeanDefinitionRegistrar接口是也是spring的扩展点之一,ImportBeanDefinitionRegistrar类只能通过其他类 @Import的方式来加载,通常是启动类或配置类。它可以支持我们自己写的代码封装成BeanDefinition对象;实现此接口的类会回调postProcessBeanDefinitionRegistry方法,注册到spring容器中。

把bean注入到spring容器不止有 @Service @Component等注解方式;还可以实现此接口,这种方式是最灵活的,能在registerBeanDefinitions方法中获取到BeanDefinitionRegistry容器注册对象,可以手动控制BeanDefinition的创建和注册。

public interface ImportBeanDefinitionRegistrar {
    default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry, BeanNameGenerator importBeanNameGenerator) {
        this.registerBeanDefinitions(importingClassMetadata, registry);
    }
    default void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
    }
}

二、ImportBeanDefinitionRegistrar使用很简单

加载指定类

定义一个类TestImportBeanDefinitionRegistrar实现ImportBeanDefinitionRegistrar接口,在配置类TestConfiguration加上注解@Import一个TestImportBeanDefinitionRegistrar实现类,重写registerBeanDefinitions方法,手动注册bean,实现注册BeanDefinition到容器中,也可以实现一些Aware接口,以便获取Spring的一些数据。加载指定DataSource类如下:

public class TestImportBeanDefinitionRegistrar implements ImportBeanDefinitionRegistrar,ResourceLoaderAware,BeanFactoryAware {
    private static ResourceLoader resourceLoader; 
    private static BeanFactory beanFactory;
    @Override
    public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
      //创建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 {
}

这样就注册了一个bean名字是dataSource,有两个属性分别是defaultTargetDataSource和targetDataSources。那么使用这个类直接用 @Autowired和@Resource注入即可。

加载扫描器类

如果我们并不知道需要register哪些bean。这里我们还需要借助一个扫描器类ClassPathBeanDefinitionScanner,通过扫描器获取我们需要注册的bean。首先创建一个@Mapper注解,再新建一个CountryMapper类,使用该Mapper注解。

@Documented
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
public @interface Mapper {
}
@Mapper
public class CountryMapper {
}

创建一个MyClassPathBeanDefinitionScanner类继承ClassPathBeanDefinitionScanner,扫描使用@Mapper的注解的类,ClassPathBeanDefinitionScanner又继承ClassPathScanningCandidateComponentProvider类,ClassPathScanningCandidateComponentProvider中有两个TypeFilter集合,includeFilters、excludeFilters。满足任意includeFilters会被加载,同样的满足任意excludeFilters不会被加载。

@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实现类中调用:

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导入的,所以要加载到TestImportBeanDefinitionRegistrar必然要先加载TestConfiguration才行;所以先看Configuration的代码,找到ConfigurationClassPostProcessor类,它实现了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容器中拿到实现了BeanDefinitionRegistryPostProcessor接口的类。拿到后执行对应的postProcessBeanDefinitionRegistry方法:

        //从invokeBeanFactoryPostProcessors方法调过来
	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);
		}
		//保存处理过的registry,避免重复处理
		this.registriesPostProcessed.add(registryId);
		 //处理java配置形式的bean定义
		processConfigBeanDefinitions(registry);
	}

最后调用了processConfigBeanDefinitions方法处理java配置形式的bean定义:

public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {
		Set<BeanDefinitionHolder> configCandidates = new LinkedHashSet<BeanDefinitionHolder>();
		//加载当前已知所有bean定义
		for (String beanName : registry.getBeanDefinitionNames()) {
			BeanDefinition beanDef = registry.getBeanDefinition(beanName);
			//   判断对应bean是否为配置类,如果是,则加入到configCandidates
			// @Configuration, @Component, @ComponentScan, @Import, @Bean等注解
			if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {
				configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));
			}
		}
		//如果找不到@configuration类,则立即返回
		if (configCandidates.isEmpty()) {
			return;
		}
...........
		 // 实例化ConfigurationClassParser 为了解析 各个配置类
		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 {
             //重点
             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));
                        //实现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);
                        }
                        //实现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);
                                //调用该类的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();
        }
    }
}

最终调用了DefaultListableBeanFactory的registerBeanDefinition方法,这里就是处理ImportBeanDefinitionRegistrar接口实现类的地方:

 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 覆盖” 这个配置吗?allowBeanDefinitionOverriding
        BeanDefinition oldBeanDefinition;
        synchronized (this.beanDefinitionMap) {
                  // 之后会看到,所有的 Bean 注册后会放入这个 beanDefinitionMap 中
                oldBeanDefinition = this.beanDefinitionMap.get(beanName);
                // 处理重复名称的 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维护的就是bean的定义,当需要获取的时候就从里面拿到对应的BeanDefinition,根据BeanDefinition生成一个对象

以上就是ImportBeanDefinitionRegistrar手动控制BeanDefinition创建注册详解的详细内容,更多关于BeanDefinition创建注册的资料请关注脚本之家其它相关文章!

相关文章

  • arthas排查jvm中CPU占用过高问题解决

    arthas排查jvm中CPU占用过高问题解决

    这篇文章主要介绍了arthas排查jvm中CPU占用过高问题解决,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步,早日升职加薪
    2022-09-09
  • Java SSM框架如何配置静态资源加载

    Java SSM框架如何配置静态资源加载

    这篇文章主要介绍了Java SSM框架如何配置静态资源加载,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下
    2020-04-04
  • 从Myeclipse 导入到eclipse中无法识别为 web项目 问题的解决步骤

    从Myeclipse 导入到eclipse中无法识别为 web项目 问题的解决步骤

    这篇文章主要介绍了从Myeclipse 导入到eclipse中无法识别为 web项目 问题的解决步骤,需要的朋友可以参考下
    2018-05-05
  • SpringBoot依赖及其作用分析

    SpringBoot依赖及其作用分析

    这篇文章主要介绍了SpringBoot依赖及其作用,具有很好的参考价值,希望对大家有所帮助,如有错误或未考虑完全的地方,望不吝赐教
    2024-03-03
  • SpringBoot使用AOP+注解实现简单的权限验证的方法

    SpringBoot使用AOP+注解实现简单的权限验证的方法

    这篇文章主要介绍了SpringBoot使用AOP+注解实现简单的权限验证的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧
    2019-05-05
  • Java数据结构之图的原理与实现

    Java数据结构之图的原理与实现

    图(Graph)是由顶点的有穷非空集合和顶点之间边的集合组成,通常表示为:G(V,E),其中,G表示一个图,V是图G中顶点的集合,E是图G中边的集合。本文将详细介绍图的原理及其代码实现,需要的可以参考一下
    2022-01-01
  • XFire构建web service客户端的五种方式

    XFire构建web service客户端的五种方式

    本篇文章主要介绍了XFire构建web service客户端的五种方式。具有很好的参考价值,下面跟着小编一起来看下吧
    2017-01-01
  • Java之OutputStreamWriter流案例详解

    Java之OutputStreamWriter流案例详解

    这篇文章主要介绍了Java之OutputStreamWriter流案例详解,本篇文章通过简要的案例,讲解了该项技术的了解与使用,以下就是详细内容,需要的朋友可以参考下
    2021-08-08
  • Java实现拖拽文件上传dropzone.js的简单使用示例代码

    Java实现拖拽文件上传dropzone.js的简单使用示例代码

    本篇文章主要介绍了Java实现拖拽文件上传dropzone.js的简单使用示例代码,具有一定的参考价值,有兴趣的可以了解一下
    2017-07-07
  • java使用JWT的方法

    java使用JWT的方法

    这篇文章主要介绍了java使用JWT的方法,JWT是token的一种,一个JWT字符串包含三个部分分别是Header、Payload和Signature,本文通过实例代码给大家介绍的非常详细,需要的朋友可以参考下
    2022-06-06

最新评论