文章目录
1. SpringBoot自动加载原理
@SpringBootApplication
public class Application {
public static void main(String[] args) {
SpringApplication.run(Application.class);
}
}
首先看@SpringBootApplication注解
@Target({ElementType.TYPE}) // 标记在类上
@Retention(RetentionPolicy.RUNTIME) // 作用于运行时
@Documented // 生成javadoc文档
@Inherited // 允许被继承
@SpringBootConfiguration
@EnableAutoConfiguration
@ComponentScan(
excludeFilters = {@Filter(
type = FilterType.CUSTOM,
classes = {TypeExcludeFilter.class}
), @Filter(
type = FilterType.CUSTOM,
classes = {AutoConfigurationExcludeFilter.class}
)}
)
public @interface SpringBootApplication {
// ...
}
其中包含了@SpringBootConfiguration和@EnableAutoConfiguration重要注解
@SpringBootConfiguration
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Configuration
public @interface SpringBootConfiguration {
// ...
}
该注解实际上起的还是@Configuration的作用,指定配置类
@EnableAutoConfiguration
@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 {};
}
EnableAutoConfiguration中有两个注解,分别是
- @AutoConfigurationPackage
- @Import({AutoConfigurationImportSelector.class})
@AutoConfigurationPackage
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Inherited
@Import({Registrar.class})
public @interface AutoConfigurationPackage {
}
这个注解的关键是@Import({Registrar.class}),@Import的作用是让Spring加载Registrar.class作为bean,那么看看Registrar中有什么
Registrar.class
static class Registrar implements ImportBeanDefinitionRegistrar, DeterminableImports {
@Override
public void registerBeanDefinitions(AnnotationMetadata metadata, BeanDefinitionRegistry registry) {
register(registry, new PackageImport(metadata).getPackageName());
}
@Override
public Set<Object> determineImports(AnnotationMetadata metadata) {
return Collections.singleton(new PackageImport(metadata));
}
}
@Import加载的类如果实现了ImportBeanDefinitionRegistrar
接口,那么就会调用该接口的方法并注册成bean,再看一下该方法做了什么
register(registry, new PackageImport(metadata).getPackageName());
在这段代码上打断点然后debug项目,可以看到getPackageName()最终返回的是整个项目启动类所在的包,即该类的作用就是扫描包下面的组件注册成bean
@Import({AutoConfigurationImportSelector.class})
public class AutoConfigurationImportSelector implements DeferredImportSelector, BeanClassLoaderAware,
ResourceLoaderAware, BeanFactoryAware, EnvironmentAware, Ordered{
// ...
}
@Import导入的类如果实现了DeferredImportSelector或ImportSelector接口,则会执行selectImports方法,看一下selectImports方法的内容
@Override
public String[] selectImports(AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return NO_IMPORTS;
}
AutoConfigurationMetadata autoConfigurationMetadata = AutoConfigurationMetadataLoader
.loadMetadata(this.beanClassLoader);
// 跟到这一段里面去
AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,
annotationMetadata);
return StringUtils.toStringArray(autoConfigurationEntry.getConfigurations());
}
这里重点看AutoConfigurationEntry autoConfigurationEntry = getAutoConfigurationEntry(autoConfigurationMetadata,annotationMetadata);这段代码,跟到方法里面去
protected AutoConfigurationEntry getAutoConfigurationEntry(AutoConfigurationMetadata autoConfigurationMetadata,
AnnotationMetadata annotationMetadata) {
if (!isEnabled(annotationMetadata)) {
return EMPTY_ENTRY;
}
AnnotationAttributes attributes = getAttributes(annotationMetadata);
// 这里是重点
List<String> configurations = getCandidateConfigurations(annotationMetadata, attributes);
configurations = removeDuplicates(configurations);
Set<String> exclusions = getExclusions(annotationMetadata, attributes);
checkExcludedClasses(configurations, exclusions);
configurations.removeAll(exclusions);
configurations = filter(configurations, autoConfigurationMetadata);
fireAutoConfigurationImportEvents(configurations, exclusions);
return new AutoConfigurationEntry(configurations, exclusions);
}
在跟到getCandidateConfigurations方法
protected List<String> getCandidateConfigurations(AnnotationMetadata metadata, AnnotationAttributes attributes) {
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;
}
此处有SpringFactoriesLoader.loadFactoryNames,这个方法比较关键了,跟进去
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-sgPO2OOS-1646748601337)(/Users/zhangxing/Library/Application Support/typora-user-images/image-20220228230648913.png)]
可以看到自动配置实际上会加载jar包中的spring.factories文件
所以自动配置实际上分2种情况
- 启动类同包下以及子包下的所有组件都会进行扫描并按需加载(如@ConditionXXXX),通过@ComponentScan可以修改扫描位置
- 读取resource目录下的META-INF/spring.factories文件中配置的类型
2.SpringApplication.run()
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) {
return new SpringApplication(primarySources).run(args);
}
这一句代码包含两步骤
- 创建SpringApplication的实例
- 使用实例调用run方法
创建SpringApplication实例
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
Assert.notNull(primarySources, "PrimarySources must not be null");
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 推断web项目使用的mvc还是webflux,通过判断dispatcher的全限定名属于reactive包还是servlet包
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// bootstrap层的初始化,这里我们知道配置问价加载顺序是bootstrap>application,所以此处先初始化bootstrap
this.bootstrappers = new ArrayList<>(getSpringFactoriesInstances(Bootstrapper.class));
// 初始化ApplicationContextInitializer,从spring.factories中加载
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
// 初始化ApplicationListener,从spring.factories中加载
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 通过栈 判断调用main方法的class
this.mainApplicationClass = deduceMainApplicationClass();
}
在这个构造方法中,由于容器还没有进行refresh
方法(bean的初始化方法),所以可以通过实现ApplicationContextInitializer类来修改配置等上下文信息
run方法
public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start();
DefaultBootstrapContext bootstrapContext = createBootstrapContext();
ConfigurableApplicationContext context = null;
configureHeadlessProperty();
// 获取SpringApplicationRunListener类型的启动监听器并实例化,从spring.factories中获取
SpringApplicationRunListeners listeners = getRunListeners(args);
// 发送启动事件
listeners.starting(bootstrapContext, this.mainApplicationClass);
try {
// 准备环境,包括了环境变量、java环境、spring环境、配置文件配置
ApplicationArguments applicationArguments = new DefaultApplicationArguments(args);
ConfigurableEnvironment environment = prepareEnvironment(listeners, bootstrapContext, applicationArguments);
configureIgnoreBeanInfo(environment);
// 打印banner
Banner printedBanner = printBanner(environment);
context = createApplicationContext();
context.setApplicationStartup(this.applicationStartup);
// 准备上下文
prepareContext(bootstrapContext, context, environment, listeners, applicationArguments, printedBanner);
// 刷新上下文
refreshContext(context);
// 预留刷新上下文后的扩展,默认没有实现
afterRefresh(context, applicationArguments);
stopWatch.stop();
if (this.logStartupInfo) {
new StartupInfoLogger(this.mainApplicationClass).logStarted(getApplicationLog(), stopWatch);
}
// 发送已启动事件
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, listeners);
throw new IllegalStateException(ex);
}
try {
// 发送run事件
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, null);
throw new IllegalStateException(ex);
}
return context;
}
-
获取并启动监听器
private SpringApplicationRunListeners getRunListeners(String[] args) { Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class }; return new SpringApplicationRunListeners(logger, getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args), this.applicationStartup); }
这里实际上找的是SpringApplicationRunListener类型的监听器,用于监听应用的启用
监听器实际上最终使用的是spring的事件机制实现
-
构造应用上下文环境
包括系统环境变量、java环境、spring环境、spring配置文件 -
初始化应用上下文
通过SpringApplication实例化的时候对webType进行的推断,当webType为MVC类型的时候,创建的上下文实际上是AnnotationConfigServletWebServerApplicationContext
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-OQiSdlJh-1646748601338)(/Users/zhangxing/Library/Application Support/typora-user-images/image-20220301213421653.png)]
AnnotationConfigServletWebServerApplicationContext
实现了BeanFactory,具有IOC容器能力,其次这个类也是GenericApplicationContext的子类,GenericApplicationContext具备上下文能力,他使用一个BeanFactory字段保存了IOC容器,所以说,AnnotationConfigServletWebServerApplicationContext是上下文,并且持有了容器,对容器能力可以进行扩展 -
刷新应用上下文前的准备阶段
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context, ConfigurableEnvironment environment, SpringApplicationRunListeners listeners, ApplicationArguments applicationArguments, Banner printedBanner) { // 设置环境参数 context.setEnvironment(environment); // 上下文创建好的后置处理 postProcessApplicationContext(context); // springapplication初始化时找到的initializers做调用 applyInitializers(context); listeners.contextPrepared(context); bootstrapContext.close(context); if (this.logStartupInfo) { logStartupInfo(context.getParent() == null); logStartupProfileInfo(context); } // Add boot specific singleton beans ConfigurableListableBeanFactory beanFactory = context.getBeanFactory(); beanFactory.registerSingleton("springApplicationArguments", applicationArguments); if (printedBanner != null) { beanFactory.registerSingleton("springBootBanner", printedBanner); } if (beanFactory instanceof DefaultListableBeanFactory) { ((DefaultListableBeanFactory) beanFactory) .setAllowBeanDefinitionOverriding(this.allowBeanDefinitionOverriding); } if (this.lazyInitialization) { context.addBeanFactoryPostProcessor(new LazyInitializationBeanFactoryPostProcessor()); } // Load the sources Set<Object> sources = getAllSources(); Assert.notEmpty(sources, "Sources must not be empty"); // 这里是重点跟进去 load(context, sources.toArray(new Object[0])); listeners.contextLoaded(context); }
上下文对象创建出来之后,给上下文设置环境参数,以及调用之前找到的initializer做调用,跟入load方法
protected void load(ApplicationContext context, Object[] sources) { if (logger.isDebugEnabled()) { logger.debug("Loading source " + StringUtils.arrayToCommaDelimitedString(sources)); } // 创建bean定义的加载器,跟进去看如何创建的 BeanDefinitionLoader loader = createBeanDefinitionLoader(getBeanDefinitionRegistry(context), sources); if (this.beanNameGenerator != null) { loader.setBeanNameGenerator(this.beanNameGenerator); } if (this.resourceLoader != null) { loader.setResourceLoader(this.resourceLoader); } if (this.environment != null) { loader.setEnvironment(this.environment); } // bean定义加载器执行加载 loader.load(); }
BeanDefinitionLoader(BeanDefinitionRegistry registry, Object... sources) { Assert.notNull(registry, "Registry must not be null"); Assert.notEmpty(sources, "Sources must not be empty"); this.sources = sources; // 注解读取器 this.annotatedReader = new AnnotatedBeanDefinitionReader(registry); // xml读取器 this.xmlReader = (XML_ENABLED ? new XmlBeanDefinitionReader(registry) : null); this.groovyReader = (isGroovyPresent() ? new GroovyBeanDefinitionReader(registry) : null); // 包扫描读取器 this.scanner = new ClassPathBeanDefinitionScanner(registry); // 排除bean过滤器 this.scanner.addExcludeFilter(new ClassExcludeFilter(sources)); }
说明会通过注解方式、包扫描方式和xml方式读取哪些地方配置了需要交给spring管理的bean,最终执行.load()方法,将这三种读取方式分别执行,拿到所有的bean定义,这里只是读取到这个项目一共有哪些bean定义,然后给保存起来,并不是说这些bean定义一定都要载入到上下文中去,至于为什么在瞎main的刷新上下文中讲解
BeanDefinition其实就是bean的基本信息,比如这个bean是什么class类型的,bean是否是懒加载,这个bean是否是primary修饰的,这个bean是单例还是property(每次都创新对象)模式等等信息…
-
刷新应用上下文
public void refresh() throws BeansException, IllegalStateException { synchronized (this.startupShutdownMonitor) { StartupStep contextRefresh = this.applicationStartup.start("spring.context.refresh"); // Prepare this context for refreshing. // 刷新前的准备,主要校验了环境参数是否完整,比如某些必填配置没有写 prepareRefresh(); // Tell the subclass to refresh the internal bean factory. // 告诉子类刷新内部的beanFactory,其实里面没做啥事,主要就是刷新BeanFactory,获取BeanFactory,返回BeanFactory ConfigurableListableBeanFactory beanFactory = obtainFreshBeanFactory(); // 准备beanFactory,添加spel表达式的解析器、设置后置处理器等设置一堆东西 prepareBeanFactory(beanFactory); try { // Allows post-processing of the bean factory in context subclasses. // 添加bean的后置处理器,bean的后置处理器执行时机是beanDefinition创建之后,bean实例化之前,这样就拥有修改beanDefinition信息的能力 postProcessBeanFactory(beanFactory); StartupStep beanPostProcess = this.applicationStartup.start("spring.context.beans.post-process"); // Invoke factory processors registered as beans in the context. // 这里是springboot自动加载的入口,通过主类转换成的BeanDefinition,拿到basePackage,然后找到basePackage下的所有@Component,然后将beanDefinition加入到容器中 invokeBeanFactoryPostProcessors(beanFactory); // Register bean processors that intercept bean creation. registerBeanPostProcessors(beanFactory); beanPostProcess.end(); // Initialize message source for this context. initMessageSource(); // Initialize event multicaster for this context. initApplicationEventMulticaster(); // Initialize other special beans in specific context subclasses. onRefresh(); // Check for listener beans and register them. registerListeners(); // Instantiate all remaining (non-lazy-init) singletons. finishBeanFactoryInitialization(beanFactory); // Last step: publish corresponding event. finishRefresh(); } catch (BeansException ex) { if (logger.isWarnEnabled()) { logger.warn("Exception encountered during context initialization - " + "cancelling refresh attempt: " + ex); } // Destroy already created singletons to avoid dangling resources. destroyBeans(); // Reset 'active' flag. cancelRefresh(ex); // Propagate exception to caller. throw ex; } finally { // Reset common introspection caches in Spring's core, since we // might not ever need metadata for singleton beans anymore... resetCommonCaches(); contextRefresh.end(); } } }
-
刷新应用上下文后的扩展接口
方便扩展