Spring boot 启动原理以及加载Bean(一)
2020-01-08 本文已影响0人
多喝热水丶
- 本文将探索学习Spring Boot2.0.x 启动原理以及加载Bean的源码
// 代码入口
// 调用SpringApplication.run 方法
public static void main(String[] args) throws IOException {
SpringApplication.run(SpringCloudBaseApplication.class, args);
}
- 经过几轮调用最终会进入SpringApplication构造函数
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) {
this.resourceLoader = resourceLoader;
// 判断加载主类是否为空
Assert.notNull(primarySources, "PrimarySources must not be null");
// 加载主类,这里使用Set 可知可以有多个主类实例
this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources));
// 1.判断是否是web环境(1.WebFlux 2.servlet 3.None)
this.webApplicationType = WebApplicationType.deduceFromClasspath();
// 初始化ApplicationContextInitializer的所有实现类
// ApplicationContextInitializer 会在
// ConfigurableApplicationContext(容器) refresh() 之前进行一些初始化
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
// 初始化ApplicationListener的监听器 run会触发这些监听器
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
// 推断引用主类,通过判断方法名称是否是Main方法判断主类
this.mainApplicationClass = deduceMainApplicationClass();
}
// 推断引用主类,通过判断方法名称是否是Main方法判断主类
private Class<?> deduceMainApplicationClass() {
try {
StackTraceElement[] stackTrace = new RuntimeException().getStackTrace();
for (StackTraceElement stackTraceElement : stackTrace) {
if ("main".equals(stackTraceElement.getMethodName())) {
return Class.forName(stackTraceElement.getClassName());
}
}
}
catch (ClassNotFoundException ex) {
// Swallow and continue
}
return null;
}
// 判断是否是web环境(1.WebFlux 2.servlet 3.None)
static WebApplicationType deduceFromClasspath() {
//判断当前ClassLoader 中是否存在webFlux类,并且不存在WebServlet
if (ClassUtils.isPresent(WEBFLUX_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(WEBMVC_INDICATOR_CLASS, null)
&& !ClassUtils.isPresent(JERSEY_INDICATOR_CLASS, null)) {
return WebApplicationType.REACTIVE;
}
// 判断是否是web项目,如果不是则返回None(简单的java程序)
for (String className : SERVLET_INDICATOR_CLASSES) {
if (!ClassUtils.isPresent(className, null)) {
return WebApplicationType.NONE;
}
}
// 如果以上条件不成立则返回是Servlet
return WebApplicationType.SERVLET;
}
// 监听器以及初始化类都调用getSpringFactoriesInstances
// 这个方法用于加载所有的spring.factories 文件中的类
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, Object... args) {
// 获取当前上下文的类加载器
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
// Use names and ensure unique to protect against duplicates
// 使用名称并确保唯一以防止重复
Set<String> names = new LinkedHashSet<>(
SpringFactoriesLoader.loadFactoryNames(type, classLoader));
// 创建加载过的Object实例
List<T> instances = createSpringFactoriesInstances(type, parameterTypes,
classLoader, args, names);
// 根据@Order(value) 注解所指定的值进行排序
AnnotationAwareOrderComparator.sort(instances);
return instances;
}
// 代码简单就不多做解释
private <T> List<T> createSpringFactoriesInstances(Class<T> type,
Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,
Set<String> names) {
List<T> instances = new ArrayList<>(names.size());
for (String name : names) {
try {
Class<?> instanceClass = ClassUtils.forName(name, classLoader);
Assert.isAssignable(type, instanceClass);
Constructor<?> constructor = instanceClass
.getDeclaredConstructor(parameterTypes);
T instance = (T) BeanUtils.instantiateClass(constructor, args);
instances.add(instance);
}
catch (Throwable ex) {
throw new IllegalArgumentException(
"Cannot instantiate " + type + " : " + name, ex);
}
}
return instances;
}
- 关于加载Spring.factories 的代码我们独立贴出来分析
- SpringFactoriesLoader.loadFactoryNames (加载文件并且以ConcurrentReferenceHashMap 的形式储存起来)
- spring.factories
public static List<String> loadFactoryNames(Class<?> factoryClass, @Nullable ClassLoader classLoader) {
// 用传入的Class type 作为 Key ,当所有的类加载成功后使用key进行筛选相应的类
String factoryClassName = factoryClass.getName();
// loadSpringFactories 加载spring.factories 文件的核心方法 getOrDefault 根据 key 获取相应的value
return loadSpringFactories(classLoader).getOrDefault(factoryClassName, Collections.emptyList());
}
// 缓存所有加载spring.factories的文件 (由于多次调用 loadSpringFactories) 2.0.x 这一块新增缓存功能
private static final Map<ClassLoader, MultiValueMap<String, String>> cache = new ConcurrentReferenceHashMap<>();
// 加载所有的spring.factories 并添加到缓存map中
private static Map<String, List<String>> loadSpringFactories(@Nullable ClassLoader classLoader) {
MultiValueMap<String, String> result = cache.get(classLoader);
// 如果当前map中有数据则直接返回
if (result != null) {
return result;
}
try {
// 所以的URL都将储存在这里
Enumeration<URL> urls = (classLoader != null ?
classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :
ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));
result = new LinkedMultiValueMap<>();
// 循环所有URL元素
while (urls.hasMoreElements()) {
URL url = urls.nextElement();
// 根据url 获取相应的资源
UrlResource resource = new UrlResource(url);
// 加载文件转换成Properties 对象 (接口路径为key,value 是所有的实现类逗号隔开)
Properties properties = PropertiesLoaderUtils.loadProperties(resource);
for (Map.Entry<?, ?> entry : properties.entrySet()) {
// 将所有的value,隔开的 解析为List对象 并且放入 result map对象中
List<String> factoryClassNames = Arrays.asList(
StringUtils.commaDelimitedListToStringArray((String) entry.getValue()));
result.addAll((String) entry.getKey(), factoryClassNames);
}
}
// 同时也加入cache对象中以便下次使用
cache.put(classLoader, result);
return result;
}
catch (IOException ex) {
throw new IllegalArgumentException("Unable to load factories from location [" +
FACTORIES_RESOURCE_LOCATION + "]", ex);
}
}
- 接下来开始正式分析 Run 方法的核心内容
public ConfigurableApplicationContext run(String... args) {
// 计时器,用于最后打印Spring boot 最终的运行时间
StopWatch stopWatch = new StopWatch();
stopWatch.start();
// 初始化容器
ConfigurableApplicationContext context = null;
// 错误信息报告集合
Collection<SpringBootExceptionReporter> exceptionReporters = new ArrayList<>();
// 配置属性 设置 java.awt.headless 系统属性为true
/*如果名字为java.awt.headless的系统属性被设置true,那么headless工具包就会被使用。应用程序可以执行如下操作:
(1)创建轻量级组件。
(2)收集关于可用的字体、字体指标和字体设置的信息。
(3)设置颜色来渲染准备图片。
(4)创造和获取图像,为渲染准备图片。
(5)使用java.awt.PrintJob,java.awt.print.*,和javax.print.*类里德打印。 */
configureHeadlessProperty();
// 获取运行时监听器 SpringApplicationRunListener
SpringApplicationRunListeners listeners = getRunListeners(args);
// 启动监听器
listeners.starting();
try {
// 设置参数
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args);
// 环境准备加载配置文件 另起文章->
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments);
// 设置 spring.beaninfo.ignore 为true
configureIgnoreBeanInfo(environment);
// 打印Spring Boot Banner
Banner printedBanner = printBanner(environment);
// 创建上下文对象了 -> 另起文章
context = createApplicationContext();
// 获取并打印Spring boot 在运行中的异常信息 ->另起文章
exceptionReporters = getSpringFactoriesInstances(
SpringBootExceptionReporter.class,
new Class[] { ConfigurableApplicationContext.class }, context);
// 环境上下文准备 ->核心点 另起文章
prepareContext(context, environment, listeners, applicationArguments,
printedBanner);
// 刷新容器扫描加载bean -> 另起文章
refreshContext(context);
// 后置处理
afterRefresh(context, applicationArguments);
// run方法 运行完毕 停止计时器
stopWatch.stop();
if (this.logStartupInfo) {
// 打印最终启动时间日志
new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch);
}
listeners.started(context);
callRunners(context, applicationArguments);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, listeners);
throw new IllegalStateException(ex);
}
try {
listeners.running(context);
}
catch (Throwable ex) {
handleRunFailure(context, ex, exceptionReporters, null);
throw new IllegalStateException(ex);
}
return context;
}
- 由于内容较多所以文章将分为多篇讲解