概述

  • Spring容器启动时,即ApplicationContext接口实现类的对象实例执行refresh方法时,主要是通过执行ConfigurationClassPostProcessor这个BeanFactoryPostProcessor,来开启整个@Configuration注解的系列类的加载的,即开启基于@Configuration的类配置代替beans标签的容器配置的相关bean的加载。

  • 而ConfigurationClassPostProcessor注册到Spring容器的BeanFactoryPostProcessor列表,主要是在AnnotationConfigUtils的registerAnnotationConfigProcessors定义的。如下:

    /*** Register all relevant annotation post processors in the given registry.* @param registry the registry to operate on* @param source the configuration source element (already extracted)* that this registration was triggered from. May be {@code null}.* @return a Set of BeanDefinitionHolders, containing all bean definitions* that have actually been registered by this call*/
    public static Set<BeanDefinitionHolder> registerAnnotationConfigProcessors(BeanDefinitionRegistry registry, @Nullable Object source) {...Set<BeanDefinitionHolder> beanDefs = new LinkedHashSet<>(8);if (!registry.containsBeanDefinition(CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME)) {// 注册ConfigurationClassPostProcessor// 用于处理@Configuration注解的类RootBeanDefinition def = new RootBeanDefinition(ConfigurationClassPostProcessor.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, CONFIGURATION_ANNOTATION_PROCESSOR_BEAN_NAME));}if (!registry.containsBeanDefinition(AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME)) {// 注册AutowiredAnnotationBeanPostProcessorRootBeanDefinition def = new RootBeanDefinition(AutowiredAnnotationBeanPostProcessor.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, AUTOWIRED_ANNOTATION_PROCESSOR_BEAN_NAME));}// Check for JSR-250 support, and if present add the CommonAnnotationBeanPostProcessor.if (jsr250Present && !registry.containsBeanDefinition(COMMON_ANNOTATION_PROCESSOR_BEAN_NAME)) {// 注册CommonAnnotationBeanPostProcessorRootBeanDefinition def = new RootBeanDefinition(CommonAnnotationBeanPostProcessor.class);def.setSource(source);beanDefs.add(registerPostProcessor(registry, def, COMMON_ANNOTATION_PROCESSOR_BEAN_NAME));}
    }
    
  • 而AnnotationConfigUtils的registerAnnotationConfigProcessors的被调用,则可以通过多种方式:

    1. ComponentScanBeanDefinitionParser:对应context:component-scan,在parse方法调用;
    2. AnnotationConfigBeanDefinitionParser:对应context:annotation-config,在parse方法调用;
    3. ClassPathBeanDefinitionScanner:在scan方法调用,该类在AnnotationConfigApplicationContext中使用;
    4. AnnotatedBeanDefinitionReader:在构造函数调用,该类在AnnotationConfigApplicationContext中使用。
  • 由以上四种方式可知,当在xml文件中配置了context:component-scan或者context:annotation-config;或者使用AnnotationConfigApplicationContext作为spring容器的ApplicationContext,这种方式通常在结合实现WebApplicationInitializer接口,来实现全基于Java类配置时用到;则可以自动激活@Configuration注解系列配置类,及其内部的@Bean方法对应bean的加载。

类结构设计

1. ConfigurationClassPostProcessor

  • 作为BeanFactoryPostProcessor,在spring容器ApplicationContext启动调用refresh方法时,遍历并调用BeanFactoryPostProcessor时被调用,执行postProcessBeanDefinitionRegistry方法,如下:

    public void refresh() throws BeansException, IllegalStateException {synchronized (this.startupShutdownMonitor) {// Prepare this context for refreshing.prepareRefresh();// Tell the subclass to refresh the internal bean factory.ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory();// Prepare the bean factory for use in this context.prepareBeanFactory(beanFactory);try {// Allows post-processing of the bean factory in context subclasses.postProcessBeanFactory(beanFactory);// 调用BeanFactoryPostProcessor// Invoke factory processors registered as beans in the context.invokeBeanFactoryPostProcessors(beanFactory);// Register bean processors that intercept bean creation.registerBeanPostProcessors(beanFactory);...}
    }
    

    invokeBeanFactoryPostProcessors方法实现:

    /*** Instantiate and invoke all registered BeanFactoryPostProcessor beans,* respecting explicit order if given.* <p>Must be called before singleton instantiation.*/
    protected void invokeBeanFactoryPostProcessors(ConfigurableListableBeanFactory beanFactory) {// 在这里会调用到// ConfigurationClassPostProcessor的postProcessBeanDefinitionRegistry方法PostProcessorRegistrationDelegate.invokeBeanFactoryPostProcessors(beanFactory, getBeanFactoryPostProcessors());// Detect a LoadTimeWeaver and prepare for weaving, if found in the meantime// (e.g. through an @Bean method registered by ConfigurationClassPostProcessor)if (beanFactory.getTempClassLoader() == null && beanFactory.containsBean(LOAD_TIME_WEAVER_BEAN_NAME)) {beanFactory.addBeanPostProcessor(new LoadTimeWeaverAwareProcessor(beanFactory));beanFactory.setTempClassLoader(new ContextTypeMatchClassLoader(beanFactory.getBeanClassLoader()));}
    }
    
  • 在该方法内先从BeanFactory中获取使用了@Configuration注解的类对应的beanDefinitions集合,即configCandidates。然后将configCandidates交给ConfigurationClassParser处理;

  • ConfigurationClassParser处理会获取这些类对应的ConfigurationClass类型的集合,然后交给ConfigurationClassBeanDefinitionReader处理,从中处理@Bean方法,将对应的bean注册到BeanFactory。

  • ConfigurationClassPostProcessor的processConfigBeanDefinitions相关代码如下:其中刚开始能从BeanFactory获取@Configuration的类对应beanDefinition,是因为@Configuration是@Component的一个子类,所以通常在配置component-scan时,需要能加载到@Configuration注解的类所在的包。

    /*** Build and validate a configuration model based on the registry of* {@link Configuration} classes.*/
    public void processConfigBeanDefinitions(BeanDefinitionRegistry registry) {List<BeanDefinitionHolder> configCandidates = new ArrayList<>();String[] candidateNames = registry.getBeanDefinitionNames();// 从BeanFactory中获取使用@Configuration注解的类for (String beanName : candidateNames) {BeanDefinition beanDef = registry.getBeanDefinition(beanName);if (ConfigurationClassUtils.isFullConfigurationClass(beanDef) ||ConfigurationClassUtils.isLiteConfigurationClass(beanDef)) {if (logger.isDebugEnabled()) {logger.debug("Bean definition has already been processed as a configuration class: " + beanDef);}}else if (ConfigurationClassUtils.checkConfigurationClassCandidate(beanDef, this.metadataReaderFactory)) {configCandidates.add(new BeanDefinitionHolder(beanDef, beanName));}}// 没有则直接返回// Return immediately if no @Configuration classes were foundif (configCandidates.isEmpty()) {return;}// Sort by previously determined @Order value, if applicableconfigCandidates.sort((bd1, bd2) -> {int i1 = ConfigurationClassUtils.getOrder(bd1.getBeanDefinition());int i2 = ConfigurationClassUtils.getOrder(bd2.getBeanDefinition());return Integer.compare(i1, i2);});...// 使用ConfigurationClassParser处理这些类上面的注解,// 如@ComponentScan,@Import,@PropertySource等// 并从这些类生成一个ConfigurationClass类型的列表// Parse each @Configuration classConfigurationClassParser parser = new ConfigurationClassParser(this.metadataReaderFactory, this.problemReporter, this.environment,this.resourceLoader, this.componentScanBeanNameGenerator, registry);Set<BeanDefinitionHolder> candidates = new LinkedHashSet<>(configCandidates);Set<ConfigurationClass> alreadyParsed = new HashSet<>(configCandidates.size());do {// 执行解析parser.parse(candidates);parser.validate();Set<ConfigurationClass> configClasses = new LinkedHashSet<>(parser.getConfigurationClasses());configClasses.removeAll(alreadyParsed);// 交给ConfigurationClassBeanDefinitionReader// 完成@Configuration注解的类内部注解的处理// 如@Bean,嵌套@Configuration等。// Read the model and create bean definitions based on its contentif (this.reader == null) {this.reader = new ConfigurationClassBeanDefinitionReader(registry, this.sourceExtractor, this.resourceLoader, this.environment,this.importBeanNameGenerator, parser.getImportRegistry());}// 注册@Bean方法对应的bean到BeanFactorythis.reader.loadBeanDefinitions(configClasses);...}
    

2. ConfigurationClassParser

  • 遍历并处理configCandidates集合:

    1. 针对集合中的每个@Configuration注解的类时,会处理该类上同时配置的其他注解,如@ComponentScan,扫描指定的包并注册有相关注解的类到BeanFactory中;还包括其他注解的处理,如@Import,@PropertySource等。
    2. 同时会创建该@Configuration注解的类对应的一个类型为ConfigurationClass的对象,将该类中@Bean注解的方式生成BeanMethod,放到类型为ConfigurationClass的对象内部的一个集合beanMethods(类型为LinkedHashSet)里面。并将该类型为ConfigurationClass的对象,放到ConfigurationClassParser的一个map(类型为LinkedHashMap),其中key和value,均为该类型为ConfigurationClass的对象。
  • 处理方法源码如下:

    /*** Apply processing and build a complete {@link ConfigurationClass} by reading the* annotations, members and methods from the source class. This method can be called* multiple times as relevant sources are discovered.* @param configClass the configuration class being build* @param sourceClass a source class* @return the superclass, or {@code null} if none found or previously processed*/
    @Nullable
    protected final SourceClass doProcessConfigurationClass(ConfigurationClass configClass, SourceClass sourceClass)throws IOException {if (configClass.getMetadata().isAnnotated(Component.class.getName())) {// Recursively process any member (nested) classes firstprocessMemberClasses(configClass, sourceClass);}// Process any @PropertySource annotationsfor (AnnotationAttributes propertySource : AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), PropertySources.class,org.springframework.context.annotation.PropertySource.class)) {if (this.environment instanceof ConfigurableEnvironment) {processPropertySource(propertySource);}else {logger.info("Ignoring @PropertySource annotation on [" + sourceClass.getMetadata().getClassName() +"]. Reason: Environment must implement ConfigurableEnvironment");}}// Process any @ComponentScan annotationsSet<AnnotationAttributes> componentScans = AnnotationConfigUtils.attributesForRepeatable(sourceClass.getMetadata(), ComponentScans.class, ComponentScan.class);if (!componentScans.isEmpty() &&!this.conditionEvaluator.shouldSkip(sourceClass.getMetadata(), ConfigurationPhase.REGISTER_BEAN)) {for (AnnotationAttributes componentScan : componentScans) {// The config class is annotated with @ComponentScan -> perform the scan immediatelySet<BeanDefinitionHolder> scannedBeanDefinitions =this.componentScanParser.parse(componentScan, sourceClass.getMetadata().getClassName());// Check the set of scanned definitions for any further config classes and parse recursively if neededfor (BeanDefinitionHolder holder : scannedBeanDefinitions) {BeanDefinition bdCand = holder.getBeanDefinition().getOriginatingBeanDefinition();if (bdCand == null) {bdCand = holder.getBeanDefinition();}if (ConfigurationClassUtils.checkConfigurationClassCandidate(bdCand, this.metadataReaderFactory)) {parse(bdCand.getBeanClassName(), holder.getBeanName());}}}}// Process any @Import annotationsprocessImports(configClass, sourceClass, getImports(sourceClass), true);// Process any @ImportResource annotationsAnnotationAttributes importResource =AnnotationConfigUtils.attributesFor(sourceClass.getMetadata(), ImportResource.class);if (importResource != null) {String[] resources = importResource.getStringArray("locations");Class<? extends BeanDefinitionReader> readerClass = importResource.getClass("reader");for (String resource : resources) {String resolvedResource = this.environment.resolveRequiredPlaceholders(resource);configClass.addImportedResource(resolvedResource, readerClass);}}// Process individual @Bean methodsSet<MethodMetadata> beanMethods = retrieveBeanMethodMetadata(sourceClass);for (MethodMetadata methodMetadata : beanMethods) {configClass.addBeanMethod(new BeanMethod(methodMetadata, configClass));}// Process default methods on interfacesprocessInterfaces(configClass, sourceClass);// Process superclass, if anyif (sourceClass.getMetadata().hasSuperClass()) {String superclass = sourceClass.getMetadata().getSuperClassName();if (superclass != null && !superclass.startsWith("java") &&!this.knownSuperclasses.containsKey(superclass)) {this.knownSuperclasses.put(superclass, configClass);// Superclass found, return its annotation metadata and recursereturn sourceClass.getSuperClass();}}// No superclass -> processing is completereturn null;
    }
    

3. ConfigurationClassBeanDefinitionReader

  • 调用ConfigurationClassParser的getConfigurationClasses方法,获取以上所说的map的keySet,即configCandidates对应的类型为ConfigurationClass的列表。

  • 然后遍历这个ConfigurationClass列表,针对每个ConfigurationClass,使用ConfigurationClassBeanDefinitionReader的loadBeanDefinitions方法,从该ConfigurationClass中获取其内部的@Bean注解的方法列表,即BeanMethod列表,或者嵌套@Configuration等包含的BeanMethod,然后将生成的bean,注册到BeanFactory中。

  • 实现源码如下:

    /*** Read a particular {@link ConfigurationClass}, registering bean definitions* for the class itself and all of its {@link Bean} methods.*/
    private void loadBeanDefinitionsForConfigurationClass(ConfigurationClass configClass, TrackedConditionEvaluator trackedConditionEvaluator) {if (trackedConditionEvaluator.shouldSkip(configClass)) {String beanName = configClass.getBeanName();if (StringUtils.hasLength(beanName) && this.registry.containsBeanDefinition(beanName)) {this.registry.removeBeanDefinition(beanName);}this.importRegistry.removeImportingClass(configClass.getMetadata().getClassName());return;}if (configClass.isImported()) {registerBeanDefinitionForImportedConfigurationClass(configClass);}// 将@Bean注解的方法注册到BeanFactory中for (BeanMethod beanMethod : configClass.getBeanMethods()) {loadBeanDefinitionsForBeanMethod(beanMethod);}loadBeanDefinitionsFromImportedResources(configClass.getImportedResources());loadBeanDefinitionsFromRegistrars(configClass.getImportBeanDefinitionRegistrars());
    }
    

Spring基于@Configuration的类配置的内部源码实现相关推荐

  1. 基于JAVA餐饮类网站计算机毕业设计源码+系统+lw文档+部署

    基于JAVA餐饮类网站计算机毕业设计源码+系统+lw文档+部署 基于JAVA餐饮类网站计算机毕业设计源码+系统+lw文档+部署 本源码技术栈: 项目架构:B/S架构 开发语言:Java语言 开发软件: ...

  2. spring 基于注解的控制器配置

    http://ttaale.iteye.com/blog/787586 spring 基于注解的控制器配置 博客分类: spring SpringBeanServletMVCWeb 13.12. 基于 ...

  3. 基于Spring Boot的个人博客系统(源码+数据库)

    目录 一.系统功能框架图 二.开发技术 三.开发环境 四.页面展示 1.登录页面 2.首页 3.文章详情页面 4.文章评论页面 ​5.后台页面 6.后台文件编辑页面 ​7.后台文章管理列表页面 五.文 ...

  4. springboot sessionfactory_Spring Boot从入门到精通(五)多数据源配置实现及源码分析...

    多数据源配置在项目软件中是比较常见的开发需求,Spring和Spring Boot中对此都有相应的解决方案可供大家参考.在Spring Boot中,如MyBatis.JdbcTemplate以及Jpa ...

  5. nginx详细文档 – 安装配置,深入源码,模块编写,编译器参数,系统函数

    nginx文档 吴东 April 28, 2009 Contents 1前言 5 2基本配置 7 2.1安装------- 7 2.2配置说明------. 10 2.3启动和控制------ 25 ...

  6. 基于ssm的超市管理系统java毕业设计源码

    一.源码描述   这是新款的ssm源码,开发工具:idea,也支持eclipse,数据库:MySQL功能也比较全面,比较适合作为毕业设计使用,感兴趣的朋友可以下载看看哦 二.功能介绍 本文以JSP的s ...

  7. 单机redis工具类的使用附源码

    单机redis工具类的使用附源码 问题背景 项目搭建 代码测试 总结 Lyric: 怎么隐藏我的悲伤 问题背景 redis常用的工具类 注意事项: 默认已安装redis,可以使用安装包安装看这篇文章, ...

  8. .Net Core 认证系统之基于Identity Server4 Token的JwtToken认证源码解析

    介绍JwtToken认证之前,必须要掌握.Net Core认证系统的核心原理,如果你还不了解,请参考.Net Core 认证组件源码解析,且必须对jwt有基本的了解,如果不知道,请百度.最重要的是你还 ...

  9. 基于Android+servlet的宠物商店【源码+文档+ppt】

    目录 1.文档目录 1.课题研究目的 2.开发技术 2.1 Android技术 2.2 servlet 3.需求分析 3.1 系统模型 3.2 系统用例图 3.3 顶层数据流图 3.4 零层数据流图 ...

最新文章

  1. djc加密数字货币_清华大学《区块链和加密数字货币》课程线上开课,与李礼辉、姚前等大佬一起交流学习吧...
  2. 计算程序运行时间(.net1.1 于.net2.0的区别)在.net2.0中提供了Stopwatch类,简单例子...
  3. Java代码性能优化技巧
  4. 编程python爬取网页数据教程_实例讲解Python爬取网页数据
  5. 移动通信蜂窝原理例题整理
  6. 图像处理之基础---特征向量的 几何意义
  7. 新年第一笔收入:支付宝开奖,你分了多少?
  8. pulseaudio之pa_simple_new()流程(十八)
  9. MySQL_Ubuntu安装
  10. 如何在代码里打开Android手机通知状态栏
  11. Aps.Net js ymPrompt消息提示组件4.0版
  12. xcode 4.0 以上版本安装新浪微博SDK
  13. 史上最全的Java从入门到精通,播放最多的Java视频教程
  14. 搭建微商城平台,新商云提供源码部署只需3分钟
  15. 怎么搜索代码里的所有中文汉字
  16. 三极管输出和输入特性曲线人类语言(共射为例)
  17. installshield 安装mysql数据库_使用 InstallShield 安装和卸载SQL Server 数据库
  18. WordPress必装插件推荐
  19. [转] Scalers:刻意练习的本质就是持续行动+刻意学习
  20. 智商捉急人士求金融数学与matlab相关指导

热门文章

  1. ps制作台式计算机图标,制作台式电脑图案的PS实例教程
  2. win10系统如何配置web服务器,win10系统利用iis搭建web服务器的设置教程
  3. 植物特征的图像检测方法
  4. 荣耀亚洲背景音乐-榜样铃声 荣耀亚洲背景音乐-榜样手机铃声免...
  5. 网页JavaScript代码常用小技巧
  6. Java Agent实例:方法监控
  7. 12八大排序算法的稳定性以及时间空间复杂度总结
  8. ORACLE错误一览表(8001-完)
  9. 新手学QT之书籍总结篇
  10. 大数据技术之HBase原理与实战归纳分享-下