目录
1. 引导加载自动配置类
我们都知道@SpringBootApplication注解修饰的类,是我们程序的入口。我们看@SpringBootApplication注解。如下所示:
......省略部分......
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
......省略部分......
}
可以看到@SpringBootConfiguration、@EnableAutoConfiguration、@ComponentScan这三个注解是我们关心的
这里用@ComponentScan,在AutoConfigurationExcludeFilter中,对重复的配置类和自动配置类进行了排查,不重复添加到IOC容器
1.1 @SpringBootConfiguration
这个注解只是说明,是一个配置类,如下所示:
......省略部分......
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
@Indexed
public @interface SpringBootConfiguration {
@AliasFor(
annotation = Configuration.class
)
boolean proxyBeanMethods() default true;
}
1.2 @ComponentScan
这个注解说明要扫描哪些路径。如下所示:
......省略部分......
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE})
@Documented
@Repeatable(ComponentScans.class)
public @interface ComponentScan {
......省略部分......
}
1.3 @EnableAutoConfiguration
这个只需关心@AutoConfigurationPackage、@Import({AutoConfigurationImportSelector.class})这两个注解。如下所示:
......省略部分......
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@AutoConfigurationPackage // 再加载这个
@Import({AutoConfigurationImportSelector.class}) // 先加载这个
public @interface EnableAutoConfiguration {
String ENABLED_OVERRIDE_PROPERTY = "spring.boot.enableautoconfiguration";
Class<?>[] exclude() default {};
String[] excludeName() default {};
}
1.3.1 @AutoConfigurationPackage
可以看到组件的导入是由AutoConfigurationPackages.Registrar.class完成,如下所示:
......省略部分......
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({AutoConfigurationPackages.Registrar.class})
public @interface AutoConfigurationPackage {
String[] basePackages() default {};
Class<?>[] basePackageClasses() default {};
}
如下所示。通过DEBUG,可以得到metadata中包含我们的程序入口全类名com.hh.springbootTest.MyApplication。所有这里做的主要是批量添加MyApplication所在包下的所有组件
......省略部分......
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
Registrar() {
}
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
AutoConfigurationPackages.register(registry, (String[])(new PackageImports(metadata)).getPackageNames().toArray(new String[0]));
}
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImports(metadata));
}
}
......省略部分......
1.3.2 @Import({AutoConfigurationImportSelector.class})
AutoConfigurationImportSelector类,主要是通过调用this.getAutoConfigurationEntry(annotationMetadata)加载组件。如下所示:
......省略部分......
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware, ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered {
......省略部分......
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return NO_IMPORTS;
} else {
AutoConfigurationEntry autoConfigurationEntry = this.getAutoConfigurationEntry(annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
}
......省略部分......
getAutoConfigurationEntry方法中,通过调用this.getCandidateConfigurations(annotationMetadata, attributes)获取到所有需要加载的配置类。然后将重复的和需要排查的进行移除。如下所示:
......省略部分......
protected AutoConfigurationEntry getAutoConfigurationEntry(AnnotationMetadata annotationMetadata) {
if (!this.isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
} else {
AnnotationAttributes attributes = this.getAttributes(annotationMetadata);
List<String> configurations = this.getCandidateConfigurations(annotationMetadata, attributes);
configurations = this.removeDuplicates(configurations);
Set<String> exclusions = this.getExclusions(annotationMetadata, attributes);
this.checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = this.getConfigurationClassFilter().filter(configurations);
this.fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
}
......省略部分......
getCandidateConfigurations方法,通过ImportCandidates.load(…省略部分…)从所有依赖包的jar包中的META-INF/spring.factories中,加载需要的组件
......省略部分......
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
List<String> configurations = new ArrayList(SpringFactoriesLoader.loadFactoryNames(this.getSpringFactoriesLoaderFactoryClass(), this.getBeanClassLoader()));
ImportCandidates.load(AutoConfiguration.class, this.getBeanClassLoader()).forEach(configurations::add);
Assert.notEmpty(configurations, "No auto configuration classes found in META-INF/spring.factories nor in META-INF/spring/org.springframework.boot.autoconfigure.AutoConfiguration.imports. If you are using a custom packaging, make sure that file is correct.");
return configurations;
}
......省略部分......
这里DEBUG得到的有144个组件。如下所示:
2. 按需添加组件到IOC容器
虽然我们前面加载了很多组件,但是很多其实是没有必要添加到IOC容器的
在spring-boot-autoconfigure-2.7.5.jar中,有针对很多情况的按需加载,如aop、cache、web等。我们这里以web为例进行讲解。如下所示:
在自动配置类上面,或自动配置类的方法上面,都有很多@Conditional注解,这个就是根据不同的条件,将需要的组件添加到IOC容器中
3. 修改添加到IOC容器的配置
这里有两种方式:
方式一:对于@ConditionalOnMissingxxxxxx等方式,IOC容器没有的,才进行springboot默认的加载。所以我们可以在我们自己的程序中,向IOC容器添加对应的组件,这样就不会进行springboot默认的加载了。如spring-boot-autoconfigure-2.7.5.jar\org\springframework\boot\autoconfigure\web\servlet\HttpEncodingAutoConfiguration.class所示:
......省略部分......
@Bean
@ConditionalOnMissingBean
public CharacterEncodingFilter characterEncodingFilter() {
CharacterEncodingFilter filter = new OrderedCharacterEncodingFilter();
filter.setEncoding(this.properties.getCharset().name());
filter.setForceRequestEncoding(this.properties.shouldForce(org.springframework.boot.web.servlet.server.Encoding.Type.REQUEST));
filter.setForceResponseEncoding(this.properties.shouldForce(org.springframework.boot.web.servlet.server.Encoding.Type.RESPONSE));
return filter;
}
......省略部分......
方式二:修改application.properties配置文件。因为@ConfigurationProperties + @EnableConfigurationProperties可以将application.properties中以指定前缀的配置绑定到配置类上,然后再将配置类添加到IOC容器。这样springboot就可以使用IOC容器的配置类组件。也就能达到修改IOC容器的行为。如spring-boot-autoconfigure-2.7.5.jar\org\springframework\boot\autoconfigure\web\servlet\DispatcherServletAutoConfiguration.class所示:
......省略部分......
@Configuration(
proxyBeanMethods = false
)
@Conditional({DefaultDispatcherServletCondition.class})
@ConditionalOnClass({ServletRegistration.class})
@EnableConfigurationProperties({WebMvcProperties.class})
protected static class DispatcherServletConfiguration {
......省略部分......
}
}