Spring-Boot 加载 Bean 的几种方式

本贴最后更新于 2035 天前,其中的信息可能已经事过境迁

Spring 从 3.0 之后,就逐步倾向于使用 java code config 方式来进行 bean 的配置,在 spring-boot 中,这种风格就更为明显了。

在查看 spring-boot 工程的时候,总想着探究一下 spring-boot 如何简单的声明一个 starter、Enable**,就能额外增加一个强大的功能,spring 是如何找到这些具体的实现 bean 的呢。

目前,大概有这么几种:

  1. 直接在工程中使用 @Configuration 注解

    这个就是基本的 java code 方式,在 @SpringBootApplication 里面就包含了 @ComponentScan,因而会把工程中的 @Configuration 注解找到,并加以解释。

  2. 通过 @Enable×× 注解里面的 @Import 注解

    我们在 Enable 某个功能时,实际上是通过 @Import 注解加载了另外的配置属性类。

    例如: 如果要给工程加上定时任务的功能,只需要在某个配置文件上加上 @EnableScheduling,实际上它是引入了 SchedulingConfiguration.class,代码如下:

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Import(SchedulingConfiguration.class)
    @Documented
    public @interface EnableScheduling {
    }
    

    而 SchedulingConfiguration 就是一个标准的配置文件了,里面定义了 ScheduledAnnotationBeanPostProcessor 这个 bean。

    @Configuration
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public class SchedulingConfiguration {
        @Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
        @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
        public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
    	return new ScheduledAnnotationBeanPostProcessor();
        }
    }
    

    有了 ScheduledAnnotationBeanPostProcessor 这 bean,就会在 context 初始化时候,查找我们代码中的 @Scheduled,并把它们转换为定时任务。

  3. 通过 @EnableAutoConfiguration 注解

    添加了这个异常强大的注解,spring-boot 会利用 AutoConfigurationImportSelector 搜索所有 jar 包中 META-INF 文件夹中 spring.factories,找到其中 org.springframework.boot.autoconfigure.EnableAutoConfiguration 的属性值,并把它作为需要解析的 @Configuration 文件。

    例如:spring-cloud-commons 里面的 spring.factories

    # AutoConfiguration
    org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
    org.springframework.cloud.client.CommonsClientAutoConfiguration,\
    org.springframework.cloud.client.discovery.noop.NoopDiscoveryClientAutoConfiguration,\
    org.springframework.cloud.client.hypermedia.CloudHypermediaAutoConfiguration,\
    org.springframework.cloud.client.loadbalancer.AsyncLoadBalancerAutoConfiguration,\
    org.springframework.cloud.client.loadbalancer.LoadBalancerAutoConfiguration,\
    org.springframework.cloud.client.serviceregistry.ServiceRegistryAutoConfiguration,\
    org.springframework.cloud.commons.util.UtilAutoConfiguration,\
    org.springframework.cloud.client.discovery.simple.SimpleDiscoveryClientAutoConfiguration
    
  4. 自己实现 ImportSelector

    AutoConfigurationImportSelector 显然有时候还是不够用的,这时候就可以自己实现 ImportSelector,实现更灵活的加载功能。

    public interface ImportSelector {
        String[] selectImports(AnnotationMetadata importingClassMetadata);
    }
    

    ImportSelector 接口定义了一个 selectImports()方法,根据程序里面定义的注解信息,动态返回能被加载的类列表。这个实现就非常灵活了,简单的可以自己判断注解,直接返回类名;复杂的可以自己定制类似 AutoConfigurationImportSelector 的功能。

    例如:我们看看 spring-cloud 的 @EnableCircuitBreaker

    @Target(ElementType.TYPE)
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    @Inherited
    @Import(EnableCircuitBreakerImportSelector.class)
    public @interface EnableCircuitBreaker {
    }
    

    发现,它引入了 EnableCircuitBreakerImportSelector,它本身并没有实现 ImportSelector,而是其父类 SpringFactoryImportSelector 实现的。

    @Override
    public String[] selectImports(AnnotationMetadata metadata) {
    if (!isEnabled()) {
        return new String[0];
    }
    AnnotationAttributes attributes = AnnotationAttributes.fromMap(
    	metadata.getAnnotationAttributes(this.annotationClass.getName(), true));
    Assert.notNull(attributes, "No " + getSimpleName() + " attributes found. Is "
    	+ metadata.getClassName() + " annotated with @" + getSimpleName() + "?");
    // Find all possible auto configuration classes, filtering duplicates
    // 调用SpringFactoriesLoader的loadFactoryNames去加载
    List<String> factories = new ArrayList<>(new LinkedHashSet<>(SpringFactoriesLoader
    	.loadFactoryNames(this.annotationClass, this.beanClassLoader)));
    //省略了错误判断和多于一个的log
    return factories.toArray(new String[factories.size()]);
    }
    

    这里,我们看到,实际加载的代码是传入了 this.annotationClass,那么对于 EnableCircuitBreakerImportSelector 来说,就是在 spring.factories 找它的全类名:
    org.springframework.cloud.client.circuitbreaker.EnableCircuitBreakerImportSelector 对应的值。

    最终在 spring-cloud-netflix-core-××.jar 的 pring.factories 中找到如下配置:

    org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker=\
    org.springframework.cloud.netflix.hystrix.HystrixCircuitBreakerConfiguration
    

    这样就完成了通过 @EnableCircuitBreaker 的注解,最终加载到 Hystrix 的实现 ystrixCircuitBreakerConfiguration,实现了功能定义和具体实现的分离。

  • Spring

    Spring 是一个开源框架,是于 2003 年兴起的一个轻量级的 Java 开发框架,由 Rod Johnson 在其著作《Expert One-On-One J2EE Development and Design》中阐述的部分理念和原型衍生而来。它是为了解决企业应用开发的复杂性而创建的。框架的主要优势之一就是其分层架构,分层架构允许使用者选择使用哪一个组件,同时为 JavaEE 应用程序开发提供集成的框架。

    942 引用 • 1459 回帖 • 96 关注

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...

推荐标签 标签

  • 互联网

    互联网(Internet),又称网际网络,或音译因特网、英特网。互联网始于 1969 年美国的阿帕网,是网络与网络之间所串连成的庞大网络,这些网络以一组通用的协议相连,形成逻辑上的单一巨大国际网络。

    98 引用 • 344 回帖
  • Markdown

    Markdown 是一种轻量级标记语言,用户可使用纯文本编辑器来排版文档,最终通过 Markdown 引擎将文档转换为所需格式(比如 HTML、PDF 等)。

    166 引用 • 1486 回帖
  • BookxNote

    BookxNote 是一款全新的电子书学习工具,助力您的学习与思考,让您的大脑更高效的记忆。

    笔记整理交给我,一心只读圣贤书。

    1 引用 • 1 回帖 • 4 关注
  • 设计模式

    设计模式(Design pattern)代表了最佳的实践,通常被有经验的面向对象的软件开发人员所采用。设计模式是软件开发人员在软件开发过程中面临的一般问题的解决方案。这些解决方案是众多软件开发人员经过相当长的一段时间的试验和错误总结出来的。

    198 引用 • 120 回帖 • 1 关注
  • 思源笔记

    思源笔记是一款隐私优先的个人知识管理系统,支持完全离线使用,同时也支持端到端加密同步。

    融合块、大纲和双向链接,重构你的思维。

    20648 引用 • 80710 回帖
  • 微信

    腾讯公司 2011 年 1 月 21 日推出的一款手机通讯软件。用户可以通过摇一摇、搜索号码、扫描二维码等添加好友和关注公众平台,同时可以将自己看到的精彩内容分享到微信朋友圈。

    130 引用 • 793 回帖
  • 快应用

    快应用 是基于手机硬件平台的新型应用形态;标准是由主流手机厂商组成的快应用联盟联合制定;快应用标准的诞生将在研发接口、能力接入、开发者服务等层面建设标准平台;以平台化的生态模式对个人开发者和企业开发者全品类开放。

    15 引用 • 127 回帖 • 3 关注
  • Mobi.css

    Mobi.css is a lightweight, flexible CSS framework that focus on mobile.

    1 引用 • 6 回帖 • 721 关注
  • 爬虫

    网络爬虫(Spider、Crawler),是一种按照一定的规则,自动地抓取万维网信息的程序。

    106 引用 • 275 回帖
  • 创造

    你创造的作品可能会帮助到很多人,如果是开源项目的话就更赞了!

    175 引用 • 994 回帖
  • Dubbo

    Dubbo 是一个分布式服务框架,致力于提供高性能和透明化的 RPC 远程服务调用方案,是 [阿里巴巴] SOA 服务化治理方案的核心框架,每天为 2,000+ 个服务提供 3,000,000,000+ 次访问量支持,并被广泛应用于阿里巴巴集团的各成员站点。

    60 引用 • 82 回帖 • 605 关注
  • CodeMirror
    1 引用 • 2 回帖 • 127 关注
  • 宕机

    宕机,多指一些网站、游戏、网络应用等服务器一种区别于正常运行的状态,也叫“Down 机”、“当机”或“死机”。宕机状态不仅仅是指服务器“挂掉了”、“死机了”状态,也包括服务器假死、停用、关闭等一些原因而导致出现的不能够正常运行的状态。

    13 引用 • 82 回帖 • 51 关注
  • 单点登录

    单点登录(Single Sign On)是目前比较流行的企业业务整合的解决方案之一。SSO 的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。

    9 引用 • 25 回帖 • 5 关注
  • 小说

    小说是以刻画人物形象为中心,通过完整的故事情节和环境描写来反映社会生活的文学体裁。

    28 引用 • 108 回帖
  • jsDelivr

    jsDelivr 是一个开源的 CDN 服务,可为 npm 包、GitHub 仓库提供免费、快速并且可靠的全球 CDN 加速服务。

    5 引用 • 31 回帖 • 45 关注
  • Kotlin

    Kotlin 是一种在 Java 虚拟机上运行的静态类型编程语言,由 JetBrains 设计开发并开源。Kotlin 可以编译成 Java 字节码,也可以编译成 JavaScript,方便在没有 JVM 的设备上运行。在 Google I/O 2017 中,Google 宣布 Kotlin 成为 Android 官方开发语言。

    19 引用 • 33 回帖 • 54 关注
  • 电影

    这是一个不能说的秘密。

    120 引用 • 598 回帖
  • QQ

    1999 年 2 月腾讯正式推出“腾讯 QQ”,在线用户由 1999 年的 2 人(马化腾和张志东)到现在已经发展到上亿用户了,在线人数超过一亿,是目前使用最广泛的聊天软件之一。

    45 引用 • 557 回帖 • 137 关注
  • C

    C 语言是一门通用计算机编程语言,应用广泛。C 语言的设计目标是提供一种能以简易的方式编译、处理低级存储器、产生少量的机器码以及不需要任何运行环境支持便能运行的编程语言。

    83 引用 • 165 回帖 • 4 关注
  • Webswing

    Webswing 是一个能将任何 Swing 应用通过纯 HTML5 运行在浏览器中的 Web 服务器,详细介绍请看 将 Java Swing 应用变成 Web 应用

    1 引用 • 15 回帖 • 621 关注
  • Electron

    Electron 基于 Chromium 和 Node.js,让你可以使用 HTML、CSS 和 JavaScript 构建应用。它是一个由 GitHub 及众多贡献者组成的活跃社区共同维护的开源项目,兼容 Mac、Windows 和 Linux,它构建的应用可在这三个操作系统上面运行。

    15 引用 • 136 回帖 • 10 关注
  • FlowUs

    FlowUs.息流 个人及团队的新一代生产力工具。

    让复杂的信息管理更轻松、自由、充满创意。

    1 引用 • 1 关注
  • wolai

    我来 wolai:不仅仅是未来的云端笔记!

    2 引用 • 14 回帖 • 4 关注
  • 域名

    域名(Domain Name),简称域名、网域,是由一串用点分隔的名字组成的 Internet 上某一台计算机或计算机组的名称,用于在数据传输时标识计算机的电子方位(有时也指地理位置)。

    43 引用 • 208 回帖
  • V2EX

    V2EX 是创意工作者们的社区。这里目前汇聚了超过 400,000 名主要来自互联网行业、游戏行业和媒体行业的创意工作者。V2EX 希望能够成为创意工作者们的生活和事业的一部分。

    17 引用 • 236 回帖 • 370 关注
  • TextBundle

    TextBundle 文件格式旨在应用程序之间交换 Markdown 或 Fountain 之类的纯文本文件时,提供更无缝的用户体验。

    1 引用 • 2 回帖 • 42 关注