敞开成长之旅!这是我参与「日新方案 2 月更文应战」的第 1 天,点击查看活动概况
咱们新建一个SpringBoot项目都会引入一个pom然后新建一个这样发动类,就能运转一个JAVA项目,而不需要像传统的SSM结构那样编写各种XML以及装备项,那么SpringBoot是怎么完结咱们之前那些事情的呢?
pom
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
发动类
@SpringBootApplication
public class SpringBootDemoApplication {
public static void main(String[] args) {
SpringApplication.run(SpringBootDemoApplication.class, args);
}
}
新建的发动类只做了两件事
- 增加了一个@SpringBootApplication注解
- 调用了SpringApplication类的run办法
首先咱们先剖析@SpringBootApplication注解做了什么
@SpringBootApplication注解上组合了其他注解,红色框子中的元注解不必关注,让咱们看看其他几个注解各自有什么效果
一、@SpringBootConfiguration
@SpringBootConfiguration组合了@Configuration
1.1 @Configuration的效果
@Configuration是Spring的中的注解,表明当时类是一个装备类而且它组合@Component注解,意味也将会注册为bean
由此咱们能够得出的定论的是咱们新建的发动类是一个装备类,而且会作为一个Bean导入到Spring容器中
二 @EnableAutoConfiguration
@EnableAutoConfiguration组合了@AutoConfigurationPackage注解和向容器中导入了AutoConfigurationImportSelector这个类,咱们分隔来看这两个注解的效果
2.1 @AutoConfigurationPackage
@AutoConfigurationPackage 向容器中导入了AutoConfigurationPackages.Registrar这个类, 进入Registrar这个类看他究竟干了啥
2.1.1AutoConfigurationPackages.Registrar
Registrar完成了ImportBeanDefinitionRegistrar接口和DeterminableImports(测验运用不必关心)接口
假如不清楚ImportBeanDefinitionRegistrar效果的能够看下面的事例
2.1.1.1 ImportBeanDefinitionRegistrar接口
这个接口配合@Import注解向容器中注册Bean的 运用事例:
定义测验Bean
public class Teacher {
private String[] name;
public Teacher(String... name) {
this.name = name;
}
public void hello(){
System.out.println(name + "你好啊!");
}
}
注册Bean信息
public class AutoConfiguredTeacherRegistrar implements ImportBeanDefinitionRegistrar {
@Override
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
//设置Bean的类别
beanDefinition.setBeanClass(Teacher.class);
//增加结构参数
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0, "张三");
//注册bean
registry.registerBeanDefinition("teacher", beanDefinition);
}
}
导入注册类
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.TYPE)
@Documented
@Import(AutoConfiguredTeacherRegistrar.class)
public @interface EnableTeacherBean {
}
测验发动类
@EnableTeacherBean
@SpringBootApplication
public class SpringBootDemoApplication {
public static void main(String[] args) {
ConfigurableApplicationContext context = SpringApplication.run(SpringBootDemoApplication.class);
context.getBean(Teacher.class).hello();
}
}
所以咱们接下来就看看Registrar向容器中注册了什么
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata,
BeanDefinitionRegistry registry) {
register(registry, new PackageImport(metadata).getPackageName());
}
入参出创建了一个PackageImport类,调取了getPackageName办法 咱们看看这个类是什么效果
这个类的结构办法中会经过类工具获取当元数据地点类的包途径,也就发动类的包途径
继续进入register办法
public static void register(BeanDefinitionRegistry registry, String... packageNames) {
//假如容器中包括了AutoConfigurationPackages这个类
if (registry.containsBeanDefinition(BEAN)) {
//获取Bean信息
BeanDefinition beanDefinition = registry.getBeanDefinition(BEAN);
ConstructorArgumentValues constructorArguments = beanDefinition
.getConstructorArgumentValues();
//增加并合并结构参数
constructorArguments.addIndexedArgumentValue(0,
addBasePackages(constructorArguments, packageNames));
}
else {
GenericBeanDefinition beanDefinition = new GenericBeanDefinition();
//设置Bean类型
beanDefinition.setBeanClass(BasePackages.class);
//增加结构参数
beanDefinition.getConstructorArgumentValues().addIndexedArgumentValue(0,
packageNames);
beanDefinition.setRole(BeanDefinition.ROLE_INFRASTRUCTURE);
//注册Bean信息
registry.registerBeanDefinition(BEAN, beanDefinition);
}
}
这个办法是向容器中注入了一个BasePackages类,而且将根途径作为入参传入进去
所以@AutoConfigurationPackage的效果是向容器中注册了一个BasePackages的类,而且这个类存储了发动类的途径
虽然Spring和SpringBoot没有运用到这个类 可是能够作为三方Starter中运用到了,比如Mybatis集成SpringBoot的主动装备
AutoConfiguredMapperScannerRegistrar在注册Mapper装备信息的时分会读取发动类的包途径作为扫描途径
public void registerBeanDefinitions(AnnotationMetadata importingClassMetadata, BeanDefinitionRegistry registry) {
if (!AutoConfigurationPackages.has(this.beanFactory)) {
MybatisAutoConfiguration.logger.debug("Could not determine auto-configuration package, automatic mapper scanning disabled.");
} else {
MybatisAutoConfiguration.logger.debug("Searching for mappers annotated with @Mapper");
//获取发动类的根途径
List<String> packages = AutoConfigurationPackages.get(this.beanFactory);
BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(MapperScannerConfigurer.class);
builder.addPropertyValue("processPropertyPlaceHolders", true);
//设置注解类型
builder.addPropertyValue("annotationClass", Mapper.class);
//设置包扫描途径
builder.addPropertyValue("basePackage", StringUtils.collectionToCommaDelimitedString(packages));
//注册Bean信息
registry.registerBeanDefinition(MapperScannerConfigurer.class.getName(), builder.getBeanDefinition());
}
}
2.2 AutoConfigurationImportSelector
熟悉Spring的都知道Aware接口是获取Spring加载中的一些信息没有实质效果,Ordered接口是用来排序的,所以只剩下了一个接口DeferredImportSelector,那么这个接口是用来干什么的?
它是ImportSelector
的子接口,它的文档注释原文和翻译
A variation of ImportSelector that runs after all @Configuration beans have been processed. This type of selector can be particularly useful when the selected imports are @Conditional . Implementations can also extend the org.springframework.core.Ordered interface or use the org.springframework.core.annotation.Order annotation to indicate a precedence against other DeferredImportSelectors . Implementations may also provide an import group which can provide additional sorting and filtering logic across different selectors.
ImportSelector 的一种扩展,在处理完一切 @Configuration 类型的Bean之后运转。当所选导入为 @Conditional 时,这种类型的选择器特别有用。
完成类还能够扩展 Ordered 接口,或运用 @Order 注解来指示相对于其他 DeferredImportSelector 的优先级。
完成类也能够供给导入组,该导入组能够供给跨不同选择器的其他排序和挑选逻辑。
DeferredImportSelectorHandler中维护了DeferredImportSelector, DeferredImportSelectorGrouping调用getImportGroup获取一切SelectorHandler拿到待处理的DeferredImportSelector(其中就包括AutoConfigurationGroup),接着调用 process 办法,AutoConfigurationGroup 会调用咱们导入的AutoConfigurationImportSelector类的getAutoConfigurationEntry办法,拿到要引入的装备集合(Entry类型的集合),最终遍历这个集合逐个解析装备类。
接下来让咱们看下是怎么拿到一切主动的装备的
protected AutoConfigurationEntry getAutoConfigurationEntry(
AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
//1.获取@EnableAutoConfiguration注解一切特点信息
AnnotationAttributes attributes = getAttributes(annotationMetadata);
//2.加载主动装备类
List<String> configurations = getCandidateConfigurations(annotationMetadata,
attributes);
//3.移除重复装备
configurations = removeDuplicates(configurations);
//4.根据注解的特点信息获取被排除的装备类
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
//5.移除一切被排除的主动装备类
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
进入getCandidateConfigurations看看是怎么拿到需要导入的主动装备类
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
//运用Spring的SPI机制获取META-INF/spring.factories下一切key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的类型
List<String> configurations = SpringFactoriesLoader.loadFactoryNames(getSpringFactoriesLoaderFactoryClass(),
getBeanClassLoader());
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories. If you "
+ "are using a custom packaging, make sure that file is correct.");
return configurations;
}
获取@EnableAutoConfiguration注解的类信息
protected Class<?> getSpringFactoriesLoaderFactoryClass() {
return EnableAutoConfiguration.class;
}
所以AutoConfigurationImportSelector类的效果就是获取一切META-INF/spring.factories下的主动装备类信息,之后这些主动装备类会被装配到Spring容器中,所以即使没有任何装备文件,SpringBoot的Web使用都能正常运转。
三、@ComponentScan
@ComponentScan这个注解在Spring中的效果是指定包扫描的根途径,让Spring来扫描指定包及子包下的组件,也能够不指定途径,默许扫描当时装备类地点包及子包里的一切组件,所以Springboot只会扫描发动类当时包和以下的包
excludeFilters特点的效果是指定包扫描的时分根据规矩指定要排除的组件,type = FilterType.CUSTOM是自定义过滤,classes 指定的类要完成TypeFilter接口,在match办法中能够获取当时扫描到的类的信息,比如注解、类名和类途径。接下来咱们看看这两个类的match的效果
3.1 TypeExcludeFilter
它是一种扩展机制,能让咱们向IOC容器中注册一些自定义的组件过滤器,以在包扫描的过程中过滤它们。首要用于测验
3.2AutoConfigurationExcludeFilter
它是用来判断是否是装备类和主动装备类
总结
1. 当时类被@Configuration标示,所以发动类也是一个装备类
2. 当时类被@ComponentScan标示,默许扫描当时装备类地点包及子包里的一切组件,所以Springboot只会扫描发动类当时包和以下的包
3. 当时类被@EnableAutoConfiguration标示,导入了AutoConfigurationImportSelector类,这个类会使用Spring的SPI机制扫描加载META-INF/spring.factories下一切key为org.springframework.boot.autoconfigure.EnableAutoConfiguration的主动装备类