一、依赖管理
为什么导入dependency时不需要指定版本
org.springframework.boot
spring-boot-starter-parent
2.5.7
上述代码中,将spring-boot-starter-parent依赖作为Spring Boot项目的统一父项目依赖管理,并 将项目版本号统一为2.2.9.RELEASE,该版本号根据实际开发需求是可以修改的使用“Ctrl+鼠标左键”进入并查看spring-boot-starter-parent底层源文件,先看spring-boot- starter-parent做了哪些事
org.springframework.boot
spring-boot-dependencies
2.5.7
1.8
@
${java.version}
${java.version}
UTF-8
UTF-8
再来看 spring-boot-starter-parent 的「build」节点
${basedir}/src/main/resources
true
**/application*.yml
**/application*.yaml
**/application*.properties
${basedir}/src/main/resources
**/application*.yml
**/application*.yaml
**/application*.properties
最后来看spring-boot-starter-parent的父依赖 spring-boot-dependencies
- 定义了很多properties
- 还有
dependencyManagement
org.apache.activemq
activemq-amqp
${activemq.version}
org.apache.activemq
activemq-blueprint
${activemq.version}
org.apache.activemq
activemq-broker
${activemq.version}
.....
所以 spring-boot-starter-parent 通过继承 spring-boot-dependencies 从而实现了SpringBoot的版本依 赖管理,所以我们的SpringBoot工程继承spring-boot-starter-parent后已经具备版本锁定等配置了,这也 就是在 Spring Boot 项目中部分依赖不需要写版本号的原因
二、 源码剖析-自动配置
@SpringBootApplication // 能够扫描Spring组件并自动配置Spring Boot
public class SpringbootApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootApplication.class, args);
}
}
@Documented // 表示注解可以记录在javaDoc中
@Inherited // 表示可以被子类继承该注解
@SpringBootConfiguration // 标注这个类是一个配置类
@EnableAutoConfiguration // 启动自动配置功能
@ComponentScan(excludeFilters = { // 包扫描类
@Filter(type = FilterType.CUSTOM,classes = {TypeExcludeFilter.class}),
@Filter( type = FilterType.CUSTOM, classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
}
@Target({ElementType.TYPE}) // 表示该注解作用在类上面
@Retention(RetentionPolicy.RUNTIME) // 表示注解的生命周期,Runtime运行时
@Documented // 表示注解可以记录在javaDoc中
@Configuration // 表示这个是一个配置类
@Indexed
public @interface SpringBootConfiguration {
}
/**
* 最重要的注解
**/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage // 自动配置包
@Import({AutoConfigurationImportSelector.class})// 借助@import来 收集所有符合自动配置条件@Configuration的bean定义,并加载都IOC容器
public @interface EnableAutoConfiguration {
}
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class}) // 默认将主配置@SpringBootApplication 类所在的包以及子包里面的类加载到IOC容器
public @interface AutoConfigurationPackage {
}
class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
// 获取的是项目主程序启动类所在的目录
// metadata: 注解标注的元数据信息
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
// 默认扫描@SpringAplication标注的主配置类所在的包以及子包下的组件
AutoConfigurationPackages.register(registry, (String[])(new AutoConfigurationPackages.PackageImports(metadata)).getPackageNames().toArray(new String[0]));
}
public Set
总结:
- 从spring.factories配置文件中加载自动配置类;
- *加载的自动配置类中排除掉**
@EnableAutoConfiguration
注解的exclude
属性指定的自动配置类 - 然后再用
AutoConfigurationImportFilter
接口过滤自动配置类是否符合注解标准(若有标注的话)@ConditionOnClass
、ConditionOnBean
和ConditionOnWebApplication
的条件,若都符合条件返回匹配结果 - 然后出发
AutoConfigurationImportEvent
事件,告诉ConditionEvaluationReport
条件评估报告器对象分别记录符合条件和Exclude
的自动配置类 - 最后Spring再将筛选后自动配置的类注入到IOC容器
三、自定义Starter
SpringBoot starter机制
SpringBoot中的starter是一种非常重要的机制,能够抛弃以前繁杂的配置,将其统一集成进 starter,应用者只需要在maven中引入starter依赖,SpringBoot就能自动扫描到要加载的信息并 启动相应的默认配置。starter让我们摆脱了各种依赖库的处理,需要配置各种信息的困扰。 SpringBoot会自动通过classpath路径下的类发现需要的Bean,并注册进IOC容器。SpringBoot提 供了针对日常企业应用研发各种场景的spring-boot-starter依赖模块。所有这些依赖模块都遵循着 约定成俗的默认配置,并允许我们调整这些配置,即遵循“约定大于配置”的理念。
自定义starter的命名规则
SpringBoot提供的starter以 spring-boot-starter-xxx 的方式命名的。
官方建议自定义的starter使用 xxx-spring-boot-starter 命名规则。以区分SpringBoot生态提供 的starter
关于条件注解的讲解
- @ConditionalOnBean:仅仅在当前上下文中存在某个对象时,才会实例化一个Bean。
- @ConditionalOnClass:某个class位于类路径上,才会实例化一个Bean。
- @ConditionalOnExpression:当表达式为true的时候,才会实例化一个Bean。基于SpEL表 达式的条件判断。 =
- @ConditionalOnMissingBean:仅仅在当前上下文中不存在某个对象时,才会实例化一个 Bean
- @ConditionalOnMissingClass:某个class类路径上不存在的时候,才会实例化一个Bean
- @ConditionalOnNotWebApplication:不是web应用,才会实例化一个Bean。
- @ConditionalOnWebApplication:当项目是一个Web项目时进行实例化。
- @ConditionalOnProperty:当指定的属性有指定的值时进行实例化。
- @ConditionalOnJava:当JVM版本为指定的版本范围时触发实例化。
- @ConditionalOnResource:当类路径下有指定的资源时触发实例化。
- @ConditionalOnJndi:在JNDI存在的条件下触发实例化。
- @ConditionalOnSingleCandidate:当指定的Bean在容器中只有一个,或者有多个但是指定 了首选的Bean时触发实例化。
自定义starter代码实现
1.导入配依赖
org.springframework.boot
spring-boot-autoconfigure
2.2.9.RELEASE
2. 创建 SimpleBean
@EnableConfigurationProperties
@ConfigurationProperties(prefix = "simplebean")
public class SimpleBean {
private int id;
private String name;
.... getset方法
}
3.编写自动配置类
@Configuration
public class MyAutoConfiguration {
static {
System.out.println("MyAutoConfiguration init....");
}
@Bean
public SimpleBean simpleBean(){
return new SimpleBean();
}
}
4.使用starter
导入
com.wuzx zdy-springboot-stater 1.0-SNAPSHOT
5.测试
@RunWith(SpringRunner.class)
@SpringBootTest
public class TestStarter {
@Autowired
private SimpleBean simpleBean;
@Test
public void testStater() {
System.out.println(simpleBean);
}
}
// 结果
SimpleBean{id=666, name='wuzhixaun'}
6.热插拔技术
还记得我们经常会在启动类Application上面加@EnableXXX注解吗?
@SpringBootApplication
@EnableRegisterServer
public class SpringbootDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringbootDemoApplication.class, args);
}
}
改造
- 新增标记类ConfigMarker
public class ConfigMarker {
}
- 新增EnableRegisterServer 注解
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Import({ConfigMarker.class}) // 将ConfigMarker导入并创建给IOC容器
public @interface EnableRegisterServer {
}
- 在启动类上新增@EnableImRegisterServer注解
其实这个@Enablexxx注解就是一种热拔插技术,加了这个注解就可以启动对应的starter,当不需 要对应的starter的时候只需要把这个注解注释掉就行
总结
到此热插拔就实现好了,当你加了 @EnableImRegisterServer 的时候启动zdy工程就会自动装配 SimpleBean,反之则不装配。 让的原理也很简单,当加了 @EnableImRegisterServer 注解的时候,由于这个注解使用了 @Import({ConfigMarker.class}) ,所以会导致Spring去加载 ConfigMarker 到上下文中,而 又因为条件注解 @ConditionalOnBean(ConfigMarker.class) 的存在,所以 MyAutoConfiguration 类就会被实例化。