【Java开发】 Spring 10 :Spring Boot 自动配置原理及实现

news2024/10/5 18:29:37

用了这么久的 SpringBoot ,我们再来回顾一下它,本文介绍 Spring Boot 的自动配置,这是它区别于 Spring 的最大的点,本文的自动配置项目包含三个项目,建议拉取仓库里的代码进行实践:尹煜 / AutoConfigDemo · GitCode

 目录

1 介绍

2 自动配置原理

2.1 启动类注解

① @SpringBootApplication

② @SpringBootConfiguration

② @EnableAutoConfiguration

③ @ComponentScan(包扫描)

2.2 条件类注解

① 类条件

② 属性条件

③ Bean 条件

④ 资源条件

⑤ Web 应用条件

⑥ 其他条件

3 手动实现自动配置

3.1 资源配置

3.2 项目代码展示

① 主项目(autoconfigure-demo)

② 副项目(geektime-spring-boot-autoconfigure)

③ 副项目(greeting)

3.3 自动/手动配置实现

① 自动配置

② 手动配置

③ 关闭配置


1 介绍

Spring Boot 目的在于简化 Spring 繁琐的 XML 配置,本质依然还是Spring框架,使用 Spring Boot后可不再使用任何 XML 配置来启动一个服务,使得使用微服务架构时可以更加快速的建立一个应用。

Spring Boot 特点:

  • 提供了固定的配置来简化配置,即约定大于配置
  • 尽可能地自动配置 Spring 和第三方库,即能自动装配
  • 内嵌容器,创建独立的 Spring 应用
  • 让测试变的简单,内置了JUnit、Spring Boot Test等多种测试框架,方便测试
  • 提供可用于生产的特性,如度量、运行状况检查和外部化配置
  • 完全不需要生成代码,也不需要 XML 配置

2 自动配置原理

首先引用一张 Spring Boot 的自动配置原理流程图 👇

2.1 启动类注解

① @SpringBootApplication

这是 Spring Boot 启动类的注解,也是自动配置的开始 👇

@SpringBootApplication
public class SpringbootWorkApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringbootWorkApplication.class, args);
    }
}

@SpringBootApplication 用于标注在 SpringBoot 的主配置类上,也就是说项目应该运行该类的 main 方法来启动 SpringBoot 项目;其本质是一个组合注解,进入该注释的代码 👇

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
    excludeFilters = {@Filter(
    type = FilterType.CUSTOM,
    classes = {TypeExcludeFilter.class}
), @Filter(
    type = FilterType.CUSTOM,
    classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {

查看该类的元信息,其实主要包含以下注解:

② @SpringBootConfiguration

查看一下它的源码 👇

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
    @AliasFor(
        annotation = Configuration.class
    )
    boolean proxyBeanMethods() default true;
}
  • @Target用于定义注解的使用位置,如果没有该项,表示注解可以用于任何地方。ElementType.TYPE 表示类,接口或者枚举。
  • @Retention用于指明修饰的注解的生存周期,即会保留到哪个阶段。RUNTIME 表示运行级别保留,编译后的class文件中存在,在jvm运行时保留,可以被反射调用。
  • @Documented指明修饰的注解,可以被例如 javadoc 此类的工具文档化,只负责标记,没有成员取值。
  • @Configuration表明这个一个 Spring Boot 的配置类,可以向容器中注入组件。

② @EnableAutoConfiguration

顾名思义,该注释含义是启动自动配置~

查看它的源码:

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage   //自动导包
@Import({AutoConfigurationImportSelector.class}) //自动配置导入选择
public @interface EnableAutoConfiguration {
    String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";

    Class<?>[] exclude() default {};

    String[] excludeName() default {};
}
  • @Inherited:用于标注一个父类的注解是否可以被子类继承,如果一个注解需要被其子类所继承,则在声明时直接使用@Inherited注解即可。如果没有写此注解,则无法被子类继承。
  • @AutoConfigurationPackage:自动导入配置包
  • @Import({AutoConfigurationImportSelector.class}):开启自动配置类的导包的选择器,即是导入哪些类,有选择性的导入。

③ @ComponentScan(包扫描)

  • 该注释用于 Configuration 类的组件扫描指令
  • 提供与 Spring XML 的 context:component-scan 元素并行的支持
  • 可以通过 basePackageClasses 或 basePackages 来定义要扫描的特定包, 如果没有定义特定的包,将从声明该注解的类的包开始扫描
  • 也可在启动类做如下配置,用于排除某些特定类包的扫描
@SpringBootApplication(exclude = {DataSourceAutoConfiguration.class,
        DataSourceTransactionManagerAutoConfiguration.class,
        JdbcTemplateAutoConfiguration.class})
public class DataSourceDemoApplication implements CommandLineRunner {
    public static void main(String[] args) {
        SpringApplication.run(DataSourceDemoApplication.class, args);
    }
}

2.2 条件类注解

@Conditional :所有注解都基于它,是spring4引入的注解

后边的项目也将使用条件类注释来测试自动配置和手动配置~

① 类条件

  • @ConditionalOnClass:当存在一个类时,如何做
  • @ConditionalOnMissingClass:当不存在一个类时,如何做

② 属性条件

  • @ConditionalOnProperty特定的属性为什么时,做什么,如果不存在,赋给他一个默认的值

③ Bean 条件

  • @ConditionalOnBean:存在某个bean时,如何做
  • @ConditionalOnMissingBean:不存在某个bean时,如何做
  • @ConditionalOnSingleCandidate:Spring容器中是否存在且只存在一个对应的实例。只有3个属性value、type、search,跟ConditionalOnBean中的这3种属性值意义一样

④ 资源条件

  • @ConditionalOnResource:是否存在指定的资源文件。只有一个属性 resources,是个 String 数组,会从类加载器中去查询对应的资源文件是否存在

⑤ Web 应用条件

  • @ConditionalOnWebApplication:如果是web应用,如何做
  • @ConditionalOnNotWebApplication:如果不是web应用,如何做

⑥ 其他条件

  • @ConditionalOnExpression sper:表达式计算结果是怎么样的
  • @ConditionalOnJava:在特定的java版本上能做哪些事情
  • @ConditionalOnJndi Jndi:条件是如何的

3 手动实现自动配置

以下资源来自《玩转 Spring 全家桶》课程,项目源码:尹煜 / AutoConfigDemo · GitCode

3.1 资源配置

首先需要将三个项目通过 IDEA 合并在一起,主项目是 autoconfigure-demo:

首先通过 IDEA 打开主项目,然后按照下述流程逐一导入两外两个项目即可 👇

3.2 项目代码展示

项目源码:尹煜 / AutoConfigDemo · GitCode

因涉及到三个项目,且彼此呈依赖关系,因此会有点难以理解,本文希望尽可能讲得清楚一些~

① 主项目(autoconfigure-demo)

路径:geektime/spring/hello/AutoconfigureDemoApplication.java

@SpringBootApplication
public class AutoconfigureDemoApplication {

	public static void main(String[] args) {
		SpringApplication.run(AutoconfigureDemoApplication.class, args);
	}

//	@Bean
//	public GreetingApplicationRunner greetingApplicationRunner() {
//		return new GreetingApplicationRunner("Spring!");
//	}
}

路径:src/main/resources/application.properties

greeting.enabled=true 用于搭配 @ConditionalOnProperty 的条件判断,见于第2个项目的启动类

greeting.enabled=true

路径:autoconfigure-demo\pom.xml

	<parent>
		<groupId>org.springframework.boot</groupId>
		<artifactId>spring-boot-starter-parent</artifactId>
		<version>2.1.3.RELEASE</version>
		<relativePath/> <!-- lookup parent from repository -->
	</parent>
	<groupId>geektime.spring.hello</groupId>
	<artifactId>autoconfigure-demo</artifactId>
	<version>0.0.1-SNAPSHOT</version>
	<name>autoconfigure-demo</name>
	<description>Demo project for Spring Boot</description>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter</artifactId>
		</dependency>

		<!-- 以下是手工实现的自动配置 -->
		<dependency>
			<groupId>geektime.spring.hello</groupId>
			<artifactId>geektime-spring-boot-autoconfigure</artifactId>
			<version>0.0.1-SNAPSHOT</version>
		</dependency>

		<!-- 以下是基于Spring Boot的自动配置 -->
		<dependency>
			<groupId>geektime.spring.hello</groupId>
			<artifactId>greeting</artifactId>
			<version>0.0.1-SNAPSHOT</version>
		</dependency>

		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-starter-test</artifactId>
			<scope>test</scope>
		</dependency>
	</dependencies>

② 副项目(geektime-spring-boot-autoconfigure)

路径:geektime/spring/hello/greeting/GreetingAutoConfiguration.java

@Configuration
@ConditionalOnClass(GreetingApplicationRunner.class)//当classpath中 存在GreetingApplicationRunner时 才能生效
public class GreetingAutoConfiguration {
    @Bean
    @ConditionalOnMissingBean(GreetingApplicationRunner.class)//不存在GreetingApplicationRunner这个类型的bean的时候,才能生效
    @ConditionalOnProperty(name = "greeting.enabled", havingValue = "true", matchIfMissing = true)//greeting.enabled这个属性为true 才能执行,如果没有配置这个值,就默认值为true
    public GreetingApplicationRunner greetingApplicationRunner() {
        return new GreetingApplicationRunner();
    }
}

路径:src/main/resources/META-INF/spring.factories

模拟 Spring Boot 的自动配置(自动配置时,Spring Boot 会扫描到类路径下的META-INF/spring.factories 配置文件,把 EnableAutoConfiguration 对应的的 Bean 值添加到容器中)

org.springframework.boot.autoconfigure.EnableAutoConfiguration=\
geektime.spring.hello.greeting.GreetingAutoConfiguration

路径:geektime-spring-boot-autoconfigure\pom.xml

该副项目也依赖 greeting 项目

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-dependencies</artifactId>
				<version>${spring-boot.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot-autoconfigure</artifactId>
		</dependency>
		<dependency>
			<groupId>geektime.spring.hello</groupId>
			<artifactId>greeting</artifactId>
			<version>0.0.1-SNAPSHOT</version>
			<scope>provided</scope>
		</dependency>
	</dependencies>

③ 副项目(greeting)

路径:geektime/spring/hello/greeting/GreetingApplicationRunner.java

用于检验自动配置成果!

@Slf4j
public class GreetingApplicationRunner implements ApplicationRunner {

    private String name;

    public GreetingApplicationRunner(){
        this("OK!");
    }

    public GreetingApplicationRunner(String name) {
        this.name = name;
        log.info("Initializing GreetingApplicationRunner.{}",name);
    }

    public void run(ApplicationArguments args) throws Exception {
        log.info("Hello everyone! We all like Spring! ");
    }
}

路径:greeting\pom.xml

	<dependencyManagement>
		<dependencies>
			<dependency>
				<groupId>org.springframework.boot</groupId>
				<artifactId>spring-boot-dependencies</artifactId>
				<version>${spring-boot.version}</version>
				<type>pom</type>
				<scope>import</scope>
			</dependency>
		</dependencies>
	</dependencyManagement>

	<dependencies>
		<dependency>
			<groupId>org.springframework.boot</groupId>
			<artifactId>spring-boot</artifactId>
		</dependency>

		<dependency>
			<groupId>org.projectlombok</groupId>
			<artifactId>lombok</artifactId>
			<optional>true</optional>
		</dependency>

		<dependency>
			<groupId>org.slf4j</groupId>
			<artifactId>slf4j-api</artifactId>
		</dependency>
	</dependencies>

以上,所有文件展示完毕~

3.3 自动/手动配置实现

① 自动配置

Ⅰ 注解主项目(autoconfigure-demo)中 AutoconfigureDemoApplication 启动类中的 bean

Ⅱ application.properties 中 greeting.enabled=true

启动主项目的启动类,日志输出显示自动配置成功,使用的是使用空参构造的配置--自动配置 👇

② 手动配置

Ⅰ 启动主项目(autoconfigure-demo)中 AutoconfigureDemoApplication 启动类中的 bean 👇

Ⅱ application.properties 中 greeting.enabled=true

启动主项目的启动类,日志输出显示手动配置成功,使用的是有参构造--手动配置 👇

③ 关闭配置

Ⅰ 注解主项目(autoconfigure-demo)中 AutoconfigureDemoApplication 启动类中的 bean

Ⅱ application.properties 中 greeting.enabled=false

启动主项目的启动类,日志输出显示该 bean 未被创建--关闭配置 👇


参考文章

SpringBoot自动配置原理_程序员老石的博客-CSDN博客_springboot自动配置原理

动手实现自己的自动配置_L# S@的博客-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/77894.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

kafka之ranger插件的一个坑

之前文章写过kafka的鉴权&#xff0c;以及集成ranger插件的配置使用。但真正在用起来后&#xff0c;发现里面有个坑&#xff0c;本文就来聊聊这个坑的情况以及排查过程。【问题现象】kafka在集成了ranger插件实现鉴权功能后&#xff0c;发现过一段时间后&#xff0c;controller…

Gaussian 计算静电云图确定吸附位点

计算背景&#xff1a; 利用高分子有机物等活性材料对有毒分子、原子、离子在真空、水溶液、有机溶液等环境下吸附&#xff0c;已是当今环境科学、矿物学、土壤化学等学科领域研究的热点。但如何确定最佳吸附位点以计算其吸附能就显得尤为重要。 现阶段多数物质的吸附均依据粒…

Eureka自我保护模式和InstanceID的配置

本节我们主要介绍 Eureka 自我保护模式的开启和关闭和自定义 Eureka 的 InstanceID 的配置。 关闭自我保护 保护模式主要在一组客户端和 Eureka Server 之间存在网络分区场景时使用。一旦进入保护模式&#xff0c;Eureka Server 将会尝试保护其服务的注册表中的信息&#xff…

PMP内容1

PMP目录概述需求&#xff1a;设计思路实现思路分析1.2.确认范围3.控制范围4.进度管理5.规划进度管理成本管理估算成本参考资料和推荐阅读Survive by day and develop by night. talk for import biz , show your perfect code,full busy&#xff0c;skip hardness,make a bette…

Yocto系列讲解[驱动篇]89 - 内核通知事件notifier chain驱动示例

By: fulinux E-mail: fulinux@sina.com Blog: https://blog.csdn.net/fulinus 喜欢的盆友欢迎点赞和订阅! 你的喜欢就是我写作的动力! 目录 内核通知事件知识Yocto中添加recipe第一个驱动模块:notifier chain provider驱动第二个驱动模块:notifier chain customer驱动两个驱…

使用 `laravel-nestedset` 实现动态权限路由

laravel-nestedset 是一款基于嵌套集合模型&#xff08;Nested Set Model&#xff09;的用于实现有序树的 laravel 扩展包。 什么是嵌套集合模型&#xff1f; 嵌套集合或嵌套集合模型是一种在关系数据库表中高效存储分层数据的方法&#xff0c;理论基础为预排序遍历树算法&am…

开源SCRM营销平台-MarketGo产品介绍(一)

1、MarketGo概述 MarketGo中国式营销自动化开源项目标杆。 MarketGo更像是一个 SDK 、引擎&#xff0c;通过提供的标准化功能和基础能力&#xff0c;让开发者能快速搭建一个营销自动化系统&#xff0c;快速完成从0-1的过程&#xff0c;并且能基于开放的能力和源码&#xff0c…

【Unity3D】使用GL绘制线段

1 前言 线段渲染器LineRenderer、拖尾TrailRenderer、绘制物体表面三角形网格从不同角度介绍了绘制线段的方法&#xff0c;本文再介绍一种新的绘制线段的方法&#xff1a;使用 GL 绘制线段。 Graphics Library&#xff08;简称 GL&#xff09;&#xff0c;包含一系列类似 OpenG…

python tk 小案例:制作一个问题搜索器

前言 嗨喽&#xff0c;大家好呀~这里是爱看美女的茜茜呐 又到了学Python时刻~ 在逛百度搜东西的时候&#xff0c;有一些杂乱的词条容易混入进来‘ 那么&#xff1f;我们能不能自己创建一个类似百度的搜索器呢&#xff1f; 当然是可以的&#xff0c;今天博主就来分享一下如何…

Dell电脑搭配Win10休眠 = 黑屏

应该是Dell的硬件和win10操作系统的适配性不行。 Dell黑屏现象 刚买Dell笔记本的时候&#xff0c;就有“黑屏”问题。刚买的一个周、一个月、一年、两年都有这样的问题&#xff0c;而且一个月至少发生一次&#xff1a; 摁了开机键&#xff0c;也开机成功了&#xff0c;电脑就…

Python——旋转字符串

题目描述 给定两个字符串s和goal&#xff0c;如果在若干次旋转操作后s能够变成goal&#xff0c;那么就返回True s的旋转操作就是把s最左面的字符放到最右面 例如&#xff1a; s ‘abcde’ 旋转一次就是‘bceda’ 而如果goal是bceda&#xff0c;那么goal就是s的旋转字符串 P…

[附源码]JAVA毕业设计心理学网站(系统+LW)

[附源码]JAVA毕业设计心理学网站&#xff08;系统LW&#xff09; 项目运行 环境项配置&#xff1a; Jdk1.8 Tomcat8.5 Mysql HBuilderX&#xff08;Webstorm也行&#xff09; Eclispe&#xff08;IntelliJ IDEA,Eclispe,MyEclispe,Sts都支持&#xff09;。 项目技术&#…

MySQL数据库的索引以及事务详解

课前导读&#xff1a; 本章设计到MySQL数据库的索引和事务操作&#xff0c;索引操作设计的概念内容比较多&#xff0c;但是他涉及到数据库的内部运行效率和使用空间等多方面知识&#xff0c;相比比较重要&#xff0c;也需要进行相关学习。而事务操作更不用多说&#xff0c;是我…

WEB进销存管理系统源码带操作手册和源码安装说明文档

适用于大众化普通商品&#xff0c;包括鞋服、眼镜店等&#xff0c;前端使用js、html、css最基本的技术&#xff0c;后端使用sql、存储过程&#xff0c;前后端才有json交互&#xff0c;不依赖于任何第三方框架&#xff0c;简单易用易学&#xff0c;适合扩展。 源码类型&#…

Java十年功力还是涨不了薪,推荐必看《Java核心技术及面试指南》

很多程序员工作努力&#xff0c;但表现一般&#xff0c;导致面试失败或者涨不了薪资&#xff0c;在我看来&#xff0c;这种情况出现的原因有两个&#xff1a;一个是“盲人摸象&#xff0c;只知其一不知其二”&#xff0c;埋头做技术或者“CV工程师”&#xff0c;再做几年也没成…

JavaScript期末大作业 基于HTML+CSS+JavaScript技术制作web前端开发个人博客(48页)

&#x1f389;精彩专栏推荐 &#x1f4ad;文末获取联系 ✍️ 作者简介: 一个热爱把逻辑思维转变为代码的技术博主 &#x1f482; 作者主页: 【主页——&#x1f680;获取更多优质源码】 &#x1f393; web前端期末大作业&#xff1a; 【&#x1f4da;毕设项目精品实战案例 (10…

MOSFET 和 IGBT 栅极驱动器电路的基本原理学习笔记(四)高侧非隔离栅极驱动

高侧非隔离栅极驱动 1.适用于P沟道的高侧驱动器 2.适用于N沟道的高侧直接驱动器 1.适用于P沟道的高侧驱动器 高侧非隔离栅极驱动可按照所驱动的器件类型或涉及的驱动电路类型来分类。相应地&#xff0c;无论是使用P沟道还是 N沟道器件&#xff0c;是实施直接驱动、电平位移驱…

项目设置统一返回结果对象

一、统一返回数据格式 项目中我们会将响应封装成json返回&#xff0c;一般我们会将所有接口的数据格式统一&#xff0c; 使前端(iOS,Android, Web)对数据的操作更一致、轻松。 一般情况下&#xff0c;统一返回数据格式没有固定的格式&#xff0c;只要能描述清楚返回的数据状态以…

十四、使用 Vue Router 开发单页应用(4)

本周概要 导航守卫 全局守卫路由独享的守卫组件内守卫导航解析流程 14.10 导航守卫 在 14.4 嵌套路由 小节中已经使用过一个组件内的导航守卫&#xff1a;beforeRouteUpdate 。Vue Router 提供的导航守卫主要用于在导航过程中重定向或取消路由&#xff0c;或添加权限验证、…

python基于用户画像和协同过滤实现电影推荐系统

1、概要 传统电影推荐系统大多使用协同过滤算法实现电影推荐&#xff0c;主要实现机理是通过用户评分及用户观影历史数据抽象为多维向量利用欧式距离或其他向量计算公式实现推荐&#xff0c;本文中将采用常用的机器学习算法Kmeans聚类算法协同过滤算法word2vec搜索推荐模型多模…