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创建注册的资料请关注脚本之家其它相关文章!
相关文章
从Myeclipse 导入到eclipse中无法识别为 web项目 问题的解决步骤
这篇文章主要介绍了从Myeclipse 导入到eclipse中无法识别为 web项目 问题的解决步骤,需要的朋友可以参考下2018-05-05SpringBoot使用AOP+注解实现简单的权限验证的方法
这篇文章主要介绍了SpringBoot使用AOP+注解实现简单的权限验证的方法,小编觉得挺不错的,现在分享给大家,也给大家做个参考。一起跟随小编过来看看吧2019-05-05Java实现拖拽文件上传dropzone.js的简单使用示例代码
本篇文章主要介绍了Java实现拖拽文件上传dropzone.js的简单使用示例代码,具有一定的参考价值,有兴趣的可以了解一下2017-07-07
最新评论