现在java项目基本都是springboot项目构建的,我们本着他们提出的约定大于配置的思想,使用起来都特别简单,很多情况下我们主需要引入几个start的jar包,写几个注解就能用,可是你们知道这里面的原理吗?那我们今天就来分析一下springBoot的自动配置原理和start的原理,顺便再自己写一个start来加深印象。
1.@Import是什么
我们之前创建一个Bean可以使用@Configuration与@Bean这两个注解配合使用来将原来配置在xml文件里的bean通过java代码的方式进行描述。
@Import注解提供了@Bean注解的功能,可以把一个Bean加入到IOC容器里面,这次@Import的源码分析我就不做了,后续的Spring源码会有这里面的讲解的,这次我们就单纯的看下这个注解是怎样使用的吧。
2.@Import怎么用
第一种用法:
语法:
@Import({ 类名.class , 类名.class... })
public class TestDemo {
}
代码如下:
这个Import是没有加上@Controller,Service等等任何注解的
我们看下IOC容器里面有啥东西吧。
第二种用法:ImportSelector方式【重点】
这种方式的前提就是一个类要实现ImportSelector接口,假如我要用这种方法,目标对象是Myclass这个类,分析具体如下:
第三种用法:ImportBeanDefinitionRegistrar方式
同样是一个接口,类似于第二种ImportSelector用法,相似度80%,只不过这种用法比较自定义化注册,具体如下:
这个就是这三种用法,在SpringBoot里面大量使用的哦。
我想说一点就是,我们可以看下我们第一种和第二种用法,输出的beanDefinitionName是类的全路径名,而第三种是类名的首字母小写哦。
进入今天的主题之一:Springboot的自动配置原理
首选从一个注解:@SpringBootApplication看起:
既然是自动配置,我猜大家应该知道是哪一个类的作用吧?没错,就是@EnableAutoConfiguration
我看看这个注解,利用我们上面讲的@Import知识,注入了一个AutoConfigurationImportSelector的Bean
我们可以详细的分析一下字段源码做的事情:
首先去loadMetadata,返回AutoConfigurationMetadata:
这从这个path下获取到这些Metadata,这其实是spi机制,等下再介绍这个。
我们看下这个路径下面都有神马东西:
我们可以看到,这里面只要是左面的key带有ConditionaXXX的后面都有一个value值,我们知道ConditionaonXXX代表着条件注入,就是如果有这个条件,我们就注入对应的Bean。
在分析 AutoConfigurationImportSelector 的源码时,会 先扫描 spring-autoconfiguration-metadata.properties 文件,最后在扫描 spring.factories 对应的类时,会结合 前面的元数据进行过滤,为什么要过滤呢? 原因是很多 的@Configuration 其实是依托于其他的框架来加载的, 如果当前的 classpath 环境下没有相关联的依赖,则意味 着这些类没必要进行加载,所以,通过这种条件过滤可以 有效的减少@configuration 类的数量从而降低 SpringBoot 的启动时间。
我们再来看下面的源码:getAutoConfigurationEntry
分析:
首先把我们上一步获取到的Metadata信息,变成AnnotationAttributes
主要看这个方法:getCandidateConfigurations
这里面加载META-INF/spring.factories文件中寻找目前jar包中是否配置了相关支持自动配置的类的集合
这个spring.factories文件也是一组一组的key=value的形式,其中一个key是EnableAutoConfiguration类的全类名,而它的value是一个xxxxAutoConfiguration的类名的列表,这些类名以逗号分隔,如下图所示:
这个@EnableAutoConfiguration注解通过@SpringBootApplication被间接的标记在了Spring Boot的启动类上。在SpringApplication.run(...)的内部就会执行selectImports()方法,找到所有JavaConfig自动配置类的全限定名对应的class,然后将所有自动配置类加载到Spring容器中。
然后做了一个Set去重,又做了一个我们主动排除的自动注入的类:
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
最后在把这些类剔除掉,返回。
最后返回可以自动注入的那些IOC容器的Bean
这就是这个源码的解析,为了能够让大家能够更明白一点,我打算写一个案例来让大家明白这个自动配置的原理东西。
我们创建一个新的spring项目,需要有spring的依赖,用spring的注解,里面东西超级简单。
我们在resources目录下,创建一个spring.factories文件:
然后install这个项目,另外一个项目导入这个项目的pom地址。
我们就可以自动注入这个类了
啥都没弄,就自动进入这个IOC容器里面了,现在大家应该懂了吧。我们知道spring加载这些自动配置的类是先加载META-INF/spring-autoconfigure-metadata.properties文件里面的配置的,我们同样可以演示一波:
在新创建的那个项目里面,加上这个配置:
加上这个代表啥意思?
就是想要依赖这个jar包,你需要在carry.CarryConfig下面有这个TestClass这个类。
我们install这个项目,另一个项目不做任何改动,我们看下会不会调用成功:
报错了,在IOC容器里面找不到这个bean,那咋办呢?我们只需要在我们的项目里面创建一个我们再另一个项目要求配置的类就可以了
哈哈,我这个案例虽然很简单,但是对于帮助理解这个自动配置的源码还是有很大的帮助,是不是呢?
我们下面来看下starter
Starter 是 Spring Boot 中的一个非常重要的概念,Starter相当于模块,它能将模块所需的依赖整合起来并对模块内的 Bean 根据环境( 条件)进行自动配置。使用者只需要依赖相应功能的 Starter,无需做过多的配置和依赖,Spring
Boot 就能自动扫描并加载相应的模块。我们在 Maven 的依赖中加入 spring-bootstarter-web 就能使项目支持 Spring MVC,并且 SpringBoot 还为我们做了很多默认配置,无需再依赖 spring-web、spring-webmvc 等相关包及做相关配置就能够立即使用起来存在很多开箱即用的 Starter 依赖,使得我们在开发业务代码时能够非常方便的、不需要过多关注框架
的配置,而只需要关注业务即可。
下面我们就来自定义一个starter来感受一下:
1创建一个项目:
2.pom文件
3.定义一个实体类映射的配置信息
@ConfigurationProperties(prefix = "carry.hello") 它可以把相同前缀的配置信息通过配置项名称映射成实体类,比如我们这里指定 prefix = "carry.hello" 这样,我们就能将以carry.hello为前缀的配置项拿到了。 ps:其实这个注解很强大,它不但能映射成String或基本类型的变量。还可以映射为List,Map等数据结构。
4.定义一个service
5.定义一个配置类
这里,我们将HelloService类定义为一个Bean,交给Ioc容器。
▲ @Configuration 注解就不多说了。
▲ @EnableConfigurationProperties 注解。该注解是用来开启对3步骤中 @ConfigurationProperties 注解配置Bean的支持。也就是@EnableConfigurationProperties注解告诉Spring Boot 能支持@ConfigurationProperties。
当然了,也可以在 @ConfigurationProperties 注解的类上添加 @Configuration 或者 @Component 注解
6.spring.factories文件:
让这个能够自动注入
7.install这个项目
8.在另外一个项目引入这个项目的pom地址
9.属性设置:
这在yml文件是有提示的,这就是我们可以在yml配置端口啥的原因,以后你想看可以配置哪些属性,就可以找ConfigurationProperties里面配置了哪些属性了
10.调用:
11.运行结果:
这就是一个简单的自定义的starter,你们get到了吗?