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)中前缀匹配的属性注入到该类的字段中:
自动配置原理简要可体现为下图: