源码👇
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
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());
}
context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
// 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);
}
1.对参数列表分析
1.1
DefaultBootstrapContext bootstrapContext
1.2 run()方法中330行对其进行初始化启动上下文
1.3查看createBootstrapContext()方法源码
private DefaultBootstrapContext createBootstrapContext() { DefaultBootstrapContext bootstrapContext = new DefaultBootstrapContext(); this.bootstrapRegistryInitializers.forEach((initializer) -> initializer.initialize(bootstrapContext)); return bootstrapContext; }
可以看到首先获取了一个DefaultBootstrapContext类实例 -> bootstrapContext,然后遍历this.bootstrappers对bootstrapContext进行初始化,由于this.bootstrappers中无任何元素,故对bootstrapContext未执行任何操作,最后将bootstrapContext返回。
我们查看bootstrapRegistryInitializers,发现其并无任何元素,所以直接返回。
1.2
ConfigurableApplicationContext context
我们发现context在run()方法的第340行进行了创建容器操作,继续进入createApplicationContext()👇
protected ConfigurableApplicationContext createApplicationContext() { return this.applicationContextFactory.create(this.webApplicationType); }
this.webApplicationType 此属性通过debug发现目前的值为SERVLET,当然这一部分有三个枚举属性然后返回上下文对象即可;关于容器,上下文对象,这些含义和区别https://www.cnblogs.com/chenbenbuyi/p/8166304.html这篇文章写的很好;
1.3
ConfigurableEnvironment environment
environment是我们真正要使用的环境对象,已经完成了配置文件等的装载
private ConfigurableEnvironment prepareEnvironment(SpringApplicationRunListeners listeners, DefaultBootstrapContext bootstrapContext, ApplicationArguments applicationArguments) { // Create and configure the environment //根据应用类型,创建应用环境:如得到系统的参数、JVM及Servlet等参数,等 ConfigurableEnvironment environment = getOrCreateEnvironment(); configureEnvironment(environment, applicationArguments.getSourceArgs()); ConfigurationPropertySources.attach(environment); listeners.environmentPrepared(bootstrapContext, environment); DefaultPropertiesPropertySource.moveToEnd(environment); Assert.state(!environment.containsProperty("spring.main.environment-prefix"), "Environment prefix cannot be set via properties."); bindToSpringApplication(environment); if (!this.isCustomEnvironment) { environment = convertEnvironment(environment); } ConfigurationPropertySources.attach(environment); return environment; }
getOrCreateEnvironment()👇private ConfigurableEnvironment getOrCreateEnvironment() { if (this.environment != null) { return this.environment; } switch (this.webApplicationType) { case SERVLET: return new ApplicationServletEnvironment(); case REACTIVE: return new ApplicationReactiveWebEnvironment(); default: return new ApplicationEnvironment(); } }
我们debug发现environment为null,所以if()判断不走,去下面的switch,当前的webApplicationType为“SERVLRET”,所以他返回的是ApplicationServletEnvironment。
//装配 configureEnvironment(environment, applicationArguments.getSourceArgs());
//发布环境已准备事件 listeners.environmentPrepared(bootstrapContext, environment);
1.4
SpringApplicationRunListeners listeners
在源码中333行, 跟踪一下
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); }
我们发现其中new了一个SpringApplicationRunListeners,其构造方法的参数又使用了getSpringFactoriesInstances()方法,我们继续跟一下👇
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type, Class<?>[] parameterTypes, Object... args) { ClassLoader classLoader = getClassLoader(); // Use names and ensure unique to protect against duplicates Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }
我们发现熟悉的代码,SpringFactoriesLoader.loadFactoryNames(type, classLoader),之前在@EnableAutoConfiguration中就使用过loadFactoryNames方法对Spring.factory配置文件进行获取,并且我们发现值是Set集合的形式,继续跟👇
public static List<String> loadFactoryNames(Class<?> factoryType, @Nullable ClassLoader classLoader) { ClassLoader classLoaderToUse = classLoader; if (classLoaderToUse == null) { classLoaderToUse = SpringFactoriesLoader.class.getClassLoader(); } String factoryTypeName = factoryType.getName(); return loadSpringFactories(classLoaderToUse).getOrDefault(factoryTypeName, Collections.emptyList()); }
重点看他使用的 loadSpringFactories(classLoaderToUse)👇
private static Map<String, List<String>> loadSpringFactories(ClassLoader classLoader) { Map<String, List<String>> result = cache.get(classLoader); if (result != null) { return result; } result = new HashMap<>(); try { Enumeration<URL> urls = classLoader.getResources(FACTORIES_RESOURCE_LOCATION); while (urls.hasMoreElements()) { URL url = urls.nextElement(); UrlResource resource = new UrlResource(url); Properties properties = PropertiesLoaderUtils.loadProperties(resource); for (Map.Entry<?, ?> entry : properties.entrySet()) { String factoryTypeName = ((String) entry.getKey()).trim(); String[] factoryImplementationNames = StringUtils.commaDelimitedListToStringArray((String) entry.getValue()); for (String factoryImplementationName : factoryImplementationNames) { result.computeIfAbsent(factoryTypeName, key -> new ArrayList<>()) .add(factoryImplementationName.trim()); } } } // Replace all lists with unmodifiable lists containing unique elements result.replaceAll((factoryType, implementations) -> implementations.stream().distinct() .collect(Collectors.collectingAndThen(Collectors.toList(), Collections::unmodifiableList))); cache.put(classLoader, result); } catch (IOException ex) { throw new IllegalArgumentException("Unable to load factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } return result; }
这个方法在我的@EnableAutoConfiguration中重点跟过,可以去那里了解细节,这里不赘叙。此方法就是将spring.properties配置文件中所包含的配置进行获取,返回的是Map集合类型,有很多配置,也就是我们使用@EnableAutoConfiguration自动配置获取到的全部配置!
回到1.4第一步的getRunListeners()中的getSpringFactoriesInstances()。现在,再结合getSpringFactoriesInstances()中createSpringFactoriesInstances()创建了SpringApplicationRunListener实例,并且在创建的时候,参数parameterTypes就是👇
Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };
List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names);
获取的实例是getSpringFactoriesInstances()的形参SpringApplicationRunListener.class!
那么获取到持有SpringApplication的引用的这个new出来的SpringApplicationRunListeners()对象(getRunListeners里),返回
至此,👇方法返回的其获取到的监听器就跟完了。
这个监听器就是SpringApplicationRunListener,最后将其放在了SpringApplicationRunListeners容器中进行保存!
1.5
ApplicationArguments applicationArguments
ApplicationArguments实例化是调用其实现类DefaultApplicationArguments,再传入main方法中args参数
我们继续跟踪new DefaultApplicationArguments(args)👇
public DefaultApplicationArguments(String... args) { Assert.notNull(args, "Args must not be null"); this.source = new Source(args); this.args = args; }
源码我们发现,传递过来的参数ars被封装成了source对象
最终我们可以理解为,在run()方法中,监听器开启之后,获取main方法的参数args,会被封装进ApplicationArguments中。
1.6
Banner printedBanner
printerBanner是上面打印过的启动banner,进入方法实现,就是个图,没啥大用。
小结:
我们发现,run()方法上面几乎所有的方法的目的似乎都是为了prepareContext()方法服务
2.对prepareContext()进行分析
private void prepareContext(DefaultBootstrapContext bootstrapContext, ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) {
context.setEnvironment(environment);
postProcessApplicationContext(context);
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());
}
context.addBeanFactoryPostProcessor(new PropertySourceOrderingBeanFactoryPostProcessor(context));
// 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);
}
1.1首先看👇,他是将传递进来的environment设置到了容器中,替换新建容器时创建的environment.
1.2
postProcessApplicationContext(context);
首先看形参是我们获取到的AnnotationConfigServletWebServerApplicationContext,
进入方法内部👇
protected void postProcessApplicationContext(ConfigurableApplicationContext context) { if (this.beanNameGenerator != null) { context.getBeanFactory().registerSingleton(AnnotationConfigUtils.CONFIGURATION_BEAN_NAME_GENERATOR, this.beanNameGenerator); } if (this.resourceLoader != null) { if (context instanceof GenericApplicationContext) { ((GenericApplicationContext) context).setResourceLoader(this.resourceLoader); } if (context instanceof DefaultResourceLoader) { ((DefaultResourceLoader) context).setClassLoader(this.resourceLoader.getClassLoader()); } } if (this.addConversionService) { context.getBeanFactory().setConversionService(context.getEnvironment().getConversionService()); } }
首先看SpringApplication对象中有没有自定义的BeanNameGenerator,有的话就注册到容器的单例池,这个对象是用来给容器中的Bean生成名字的,Spring容器new出来的时候会默认生成一个,默认的命名策略就是类名小写,不过SpringApplication中的该对象默认是null的
然后看SpringApplication对象有没有自定义ResourceLoader,有的话就赋值给容器,默认也是null的,不走判断,直接向下走
最后一个if分支,addConversionService在SpringApplication对象的构造函数里就默认设置为true,所以会走if,它为容器设置了一个ConversonService,这个类是用来做类型转换的,比如String转Integer等等。
这个方法拿到了上下文对象,主要的操作就是查看自定义BeanNameGenerator,有则注册到单例池和设置ConversonService,用来进行类型转换。
1.3
applyInitializers(context);protected void applyInitializers(ConfigurableApplicationContext context) { for (ApplicationContextInitializer initializer : getInitializers()) { Class<?> requiredType = GenericTypeResolver.resolveTypeArgument(initializer.getClass(), ApplicationContextInitializer.class); Assert.isInstanceOf(requiredType, context, "Unable to call initializer."); initializer.initialize(context); } }
它取出了SpringApplication对象创建时,到META-INF/spring.factories中加载到的ApplicationContextInitializer列表,并依次调用其initialize方法
1.4
listeners.contextPrepared(context);...太多了,不写了
SpringBoot源码解析(十四)prepareContext_一元咖啡的博客-CSDN博客_preparecontext