当前位置: 首页 > news >正文

SpringBoot(八) --- SpringBoot原理

目录

一、配置优先级

二、Bean的管理

1. Bean的作用域

2. 第三方Bean

三、SpringBoot原理

1. 起步依赖

2. 自动配置

3.  自动配置原理分析

3.1 源码解析

3.2 @Conditional


一、配置优先级

SpringBoot项目当中支持三类配置文件:

  • application.properties

  • application.yml

  • application.yaml

如果这三种配置文件同时存在,并且都配置了同一属性,如:Tomcat端口号,到底哪一份配置文件生效呢?

如下图所示,在application.properties配置文件中端口号设置为8081,在application.yml和application.yaml中端口号设置为8082。发现运行的端口号为8081.

properties、yaml、yml三种配置文件,优先级最高的是properties。

当properties不存在时,yaml、yml两种配置文件同时存在。会发现端口号为8083。

yaml、yml 两种配置文件,优先级最高的是yml。

总结:

配置文件的优先级为:properties配置文件、yml配置文件、yaml配置文件。但是目前主流使用的的配置文件为yml格式的。

二、Bean的管理

IOC容器中管理的就是Bean对象。

1. Bean的作用域

IOC容器中,默认bean对象是单例的 (只有一个实例对象)。Spring中支持五种作用域,后三种作用域在Web环境下才生效。(Web环境指的是传统Web开发,Spring环境指的是利用SSM框架开发的Web项目)。

可以借助Spring中的@Scope注解来配置作用域。

当设置Bean对象为单例时,例如下述代码:

//默认bean的作用域为:singleton (单例)
@RestController
@RequestMapping("/depts")
public class DeptController {@Autowiredprivate DeptService deptService;public DeptController(){System.out.println("DeptController constructor ....");}//省略其他代码...
}

测试类如下:

@SpringBootTest
class SpringbootWebConfig2ApplicationTests {@Autowiredprivate ApplicationContext applicationContext; //IOC容器对象//bean的作用域@Testpublic void testScope(){for (int i = 0; i < 10; i++) {DeptController deptController = applicationContext.getBean(DeptController.class);System.out.println(deptController);}}
}

结果如下图,可以发现,每一次创建的Bean实例都是同一个。

如果设置Bean的作用域非单例:

@Scope("prototype") //bean作用域为非单例
@RestController
@RequestMapping("/depts")
public class DeptController {@Autowiredprivate DeptService deptService;public DeptController(){System.out.println("DeptController constructor ....");}//省略其他代码...
}

结果如下图,可以发现,IOC容器创建了多个Bean实例,每一次使用Bean对象就会创建一个新的实例。

2. 第三方Bean

之前所配置的Bean,像controller、service,dao三层体系下编写的类,这些类都是我们在项目当中自己定义的类(自定义类)。当我们要声明这些bean,也非常简单,我们只需要在类上加上@Component以及它的这三个衍生注解(@Controller@Service@Repository),就可以来声明这个bean对象了。

当某个类不是我们自己编写的,而是我们引入的第三方依赖当中提供的,那么此时我们是无法使用 @Component 及其衍生注解来声明bean的,此时就需要使用@Bean注解来声明bean 了。

此时有两种方式来声明第三方类的Bean,一种是在启动类中直接声明这个Bean【不推荐】,另一种是将第三方的Bean对象进行集中管理,通过@Configuration 注解声明一个配置类。【推荐】。

这里只介绍第二种方式。创建一个包,专门用来存在配置类,下面代码是将阿里云操作的工具类进行Bean注入。

@Configuration
public class OSSConfig {@Beanpublic AliyunOSSOperator aliyunOSSOperator(AliyunOSSProperties ossProperties) {return new AliyunOSSOperator(ossProperties);}
}

这是将Redis的相关第三方类进行Bean注入:

@Configuration
@Slf4j
public class RedisConfiguration {@Beanpublic RedisTemplate redisTemplate(RedisConnectionFactory redisConnectionFactory) {log.info("开始创建redis模版对象..");RedisTemplate redisTemplate = new RedisTemplate<>();// 设置redis的连接工厂对象redisTemplate.setConnectionFactory(redisConnectionFactory);// 设置redis key的序列化器redisTemplate.setKeySerializer(new StringRedisSerializer());return redisTemplate;}
}

然后,就可以直接在Controller层使用这个第三方类,例如下面代码:

@RestController("userShopController")
@RequestMapping("/user/shop")
@Slf4j
public class ShopController {private static final String KET = "SHOP_STATUS";@Autowiredprivate RedisTemplate redisTemplate;/*** 获取店铺营业状态*/@GetMapping("/status")public Result getStatus() {Integer status = (Integer) redisTemplate.opsForValue().get(KET);log.info("获取营业状态为:{}", status == 1? "营业中" : "已打样");return Result.success(status);}
}

三、SpringBoot原理

Spring中的所有框架都是基于一个基础框架SpringFramework(也就是Spring框架)。SpringBoot框架的底层同样也是SpringFramework。

SpringBoot框架之所以使用起来更简单更快捷,是因为SpringBoot框架底层提供了两个非常重要的功能:一个是起步依赖,一个是自动配置

通过SpringBoot所提供的起步依赖,就可以大大的简化pom文件当中依赖的配置,从而解决了Spring框架当中依赖配置繁琐的问题。通过自动配置的功能就可以大大的简化框架在使用时bean的声明以及bean的配置。我们只需要引入程序开发时所需要的起步依赖,项目开发时所用到常见的配置都已经有了,我们直接使用就可以了。

1. 起步依赖

假如我们没有使用SpringBoot,用的是Spring框架进行web程序的开发,此时我们就需要引入web程序开发所需要的一些依赖。

但是如果使用SpringBoot,当我们引入了 spring-boot-starter-web 之后,maven会通过依赖传递特性,将web开发所需的常见依赖都传递下来。就不需要我们一个一个的去引用了。

简单理解就是,假如你想吃一份火锅。传统方式(老Spring)下,你需要单独下单锅底、肉、蔬菜等等东西,同时还要考虑选的菜品适不适合这个锅底等等。但是如果有了SpringBoot的起步依赖,就相当于你直接点了一个火锅套餐,你不需要考虑任何东西,只需要说一声“我要一个spring-boot-starter-web套餐”,后台就会自动将Web开发所需要的东西打包好。不需要一个一个找依赖,也不用担心版本冲突。

2. 自动配置

SpringBoot的自动配置就是当spring容器启动后,一些配置类、bean对象就自动存入到了IOC容器中,不需要我们手动去声明,从而简化了开发,省去了繁琐的配置操作。

例如,我们要将一个对象转为json,直接注入一个Gson,然后就可以直接使用了。而我们在我们整个项目中,也并未配置Gson这个类型的bean,为什么可以直接注入使用呢? 原因就是因为这个bean,springboot中已经帮我们自动配置完毕了,我们是可以直接使用的。

自动配置是Spring Boot在启动时,根据你项目的实际依赖​(类路径上有啥)和当前环境​(比如有没有配数据源URL),​自动判断并创建好一大堆常用的Bean(对象)和配置。它背后靠一堆@ConditionalOn...(条件满足才生效)的智能开关实现。

3.  自动配置原理分析

3.1 源码解析

要搞清楚SpringBoot的自动配置原理,要从SpringBoot启动类上使用的核心注解@SpringBootApplication开始分析:

元注解就是在注解上的注解,是必需的,不再解释。

先来看第一个注解:@SpringBootConfiguration:

@SpringBootConfiguration注解上使用了@Configuration,表明SpringBoot启动类就是一个配置类。

@Indexed注解,是用来加速应用启动的(不用关心)。

再来看@ComponentScan注解:

@ComponentScan注解是用来进行组件扫描的,扫描启动类所在的包及其子包下所有被@Component及其衍生注解声明的类。

SpringBoot启动类,之所以具备扫描包功能,就是因为包含了@ComponentScan注解。

最后来看@EnableAutoConfiguration注解(自动配置核心注解):

@EnableXxxxx这种格式的注解说白了就是第三方依赖中提供的注解,指定要导入哪些Bean对象或配置类,在使用的时候在启动类上加上@EnableXxxxx注解即可,@SpringBootApplication注解上加了@EnableAutoConfiguration注解,就会自动传递到启动类上。

@EnableAutoConfiguration注解上使用了@Import注解(Import注解才是真正用来导入类的注解,@EnableXxxx只是将其包装了一下)。

AutoConfigurationImportSelector类是ImportSelector接口的实现类。

AutoConfigurationImportSelector类中重写了ImportSelector接口的selectImports()方法,selectImports()方法底层调用getAutoConfigurationEntry()方法,获取可自动配置的配置类信息集合。

getAutoConfigurationEntry()方法通过调用getCandidateConfigurations(annotationMetadata, attributes)方法获取在配置文件中配置的所有自动配置类的集合。

META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports配置文件中会指定许多第三方的配置类,例如第三方依赖Gson的配置类:GsonAutoConfiguration。

打开第三方依赖中提供的GsonAutoConfiguration类,可以发现GsonAutoConfiguration类上,添加了注解@AutoConfiguration,因此该类是个配置类

自动配置的原理就是在配置类中定义一个@Bean标识的方法,而Spring会自动调用配置类中使用@Bean标识的方法,并把方法的返回值注册到IOC容器中。

自动配置源码小结:

自动配置原理源码入口就是 @SpringBootApplication 注解,在这个注解中封装了3个注解,分别是:

  • @SpringBootConfiguration

    • 声明当前类是一个配置类

  • @ComponentScan

    • 进行组件扫描(SpringBoot中默认扫描的是启动类所在的当前包及其子包)

  • @EnableAutoConfiguration

      在实现类重写的selectImports()方法,读取当前项目下所有依赖jar包中META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports两个文件里面定义的配置类(配置类中定义了@Bean注解标识的方法)。

    • 封装了@Import注解(Import注解中指定了一个ImportSelector接口的实现类)

当SpringBoot程序启动时,就会加载配置文件当中所定义的配置类,并将这些配置类信息(类的全限定名)封装到String类型的数组中,最终通过@Import注解将这些配置类全部加载到Spring的IOC容器中,交给IOC容器管理。

但是并不是把文件中的所有Bean都注册到IOC容器当中,因为在声明Bean对象的时候,会有一个@Conditional开头的注解,这个注解的作用就是按照指定的条件进行装配,只有满足条件,,响应的Bean才会注册到IOC容器当中。

3.2 @Conditional

  • 作用:按照一定的条件进行判断,在满足给定条件后才会注册对应的bean对象到Spring的IOC容器中。

  • 位置:方法、类

  • @Conditional本身是一个父注解,派生出大量的子注解:

    • @ConditionalOnClass:判断环境中有对应字节码文件,才注册bean到IOC容器。

    • @ConditionalOnMissingBean:判断环境中没有对应的bean(类型或名称),才注册bean到IOC容器。

    • @ConditionalOnProperty:判断配置文件中有对应属性和值,才注册bean到IOC容器。

@ConditionalOnClass注解

@Configuration
public class HeaderConfig {@Bean@ConditionalOnClass(name="io.jsonwebtoken.Jwts")//环境中存在指定的这个类,才会将该bean加入IOC容器public HeaderParser headerParser(){return new HeaderParser();}//省略其他代码...
}

@ConditionalOnMissingBean注解(最常用)

@Configuration
public class HeaderConfig {@Bean@ConditionalOnMissingBean //不存在该类型的bean,才会将该bean加入IOC容器public HeaderParser headerParser(){return new HeaderParser();}//省略其他代码...
}

此时再来看看GsonAutoConfiguration这个配置类,@EnableConfigurationProperties(GsonProperties.class),这个注解明确告知SpringBoot框架,GsonProperties类是一个配置属性类(标注了 @ConfigurationProperties需要将配置文件(如application.yml)中前缀匹配的属性注入到该类的字段中:        

自动配置原理简要可体现为下图:

相关文章:

  • 电工基础【7】往返运动控制、星三角形降压启动
  • 深度学习N2周:构建词典
  • 【LeetCode】1061. 按字典序排列最小的等效字符串(并查集)
  • Qt 按钮类控件(Push Button 与 Radio Button)(1)
  • SLG游戏分析
  • opencv学习笔记1:图像基础、图像操作、直方图均衡化详解
  • python打卡训练营打卡记录day45
  • 【C++高阶二】STL的map和set
  • rapidocr 3.0 在线demo来了
  • 插入排序,二分查找,字符数组 day8
  • 如何配置Git LFS?
  • [蓝桥杯]倍数问题
  • ZooKeeper 安装教程(Windows + Linux 双平台)
  • 26考研 | 王道 | 计算机组成原理 | 三、存储系统
  • [蓝桥杯]堆的计数
  • 2023年全国研究生数学建模竞赛华为杯A题WLAN网络信道接入机制建模求解全过程文档及程序
  • function call到MCP技术演进
  • 44、web实验-后台管理系统基本功能
  • 使用 minicom 录制串口报文并回放
  • 黑马Java面试笔记之 并发编程篇(线程池+使用场景)
  • wordpress 无法在线升级/免费seo课程
  • 仿券妈妈网站开发/seo指的是什么意思
  • 做网站有什么用/广州seo代理计费
  • 昆山公司网站建设/重庆seo推广运营
  • 宁波 网站建设/日本进口yamawa
  • php网站开发心得/优化网络推广外包