主要了解和学习下SpringBoot启动的大致原理是如何,以及知道几个注解的真正含义和用途是什么,SpringBoot就可以以SpringApplication.run(Bootstrap.class);这样的一句代码作为启动的入口
1、SpringApplication 对象实例化SpringApplication 文件
public static ConfigurableApplicationContext run(Object[] sources, String[] args) { // 传递的source其实就是类Bootstrap
return new SpringApplication(sources).run(args); // 实例化一个SpringApplication对象执行run方法}实例化的时候又会执行initialize 方法
private void initialize(Object[] sources) { // 这个source依旧是上文说的Bootstrap.class 类
if (sources != null && sources.length > 0) { this.sources.addAll(Arrays.asList(sources)); // 添加到source资源列表里面去
} this.webEnvironment = deduceWebEnvironment(); // 设置其是否为web环境
setInitializers((Collection) getSpringFactoriesInstances(
ApplicationContextInitializer.class));
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); // 拆分为两步,一步是getSpringFactoriesInstances,再者就是set操作
// set操作很简单,就是设置当前对象的初始化对象以及监听器
this.mainApplicationClass = deduceMainApplicationClass(); // 通过堆栈信息,推断 main方法的类对象为当前的主程序类}private static final String[] WEB_ENVIRONMENT_CLASSES = { "javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext" };private boolean deduceWebEnvironment() { for (String className : WEB_ENVIRONMENT_CLASSES) { // 遍历包含上述两个类名称的数组
if (!ClassUtils.isPresent(className, null)) { // 一旦发现不存在该类,就立即返回 deduce 推断不是web环境
return false;
}
} // 必须同时包含两个类,才推断出为web环境
return true;
}getSpringFactoriesInstances 方法操作
privateCollection extends T> getSpringFactoriesInstances(Class type, Class>[] parameterTypes, Object... args) { // 传递的type就是上面说的ApplicationContextInitializer.class以及ApplicationListener.class类 // 类型以及参数目前都没有具体指 ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); // Use names and ensure unique to protect against duplicates Set names = new linkedHashSet ( SpringFactoriesLoader.loadFactoryNames(type, classLoader)); // 通过SpringFactoriesLoader 获取对应的名称,具体详情可以看下面的代码块 // 这点需要重点关注下!!! // 结果就是返回一个set集合 List instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); // 看样子就是创建一个实例的集合 AnnotationAwareOrderComparator.sort(instances); // 然后通过AnnotationAwareOrderComparator 的排序规则跪实例集合进行排序 // 排序就是看是否存在Order或者Priority注解,然后取得注解的值,排在集合前面 return instances; }private List createSpringFactoriesInstances(Class type, Class>[] parameterTypes, ClassLoader classLoader, Object[] args, Set names) { List instances = new ArrayList (names.size()); for (String name : names) { // 遍历上面取到的name 集合 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); } } // 最后生成name映射的实例集合 return instances; }
SpringFactoriesLoader.loadFactoryNames 方法
public static final String FACTORIES_RESOURCE_LOCATION = "meta-INF/spring.factories";public static ListloadFactoryNames(Class> factoryClass, ClassLoader classLoader) { // 传递的factoryClass 就是上面的ApplicationContextInitializer、ApplicationListener.等 String factoryClassName = factoryClass.getName(); // 获取类的全名称 try { Enumeration urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) : ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION)); // 如果类加载器为null,则使用系统默认的方法,否则使用当前传递的类加载器读取 // 当前类加载器可以获取到的所有文件路径为“meta-INF/spring.factories” 的地址 List result = new ArrayList (); while (urls.hasMoreElements()) { // 迭代遍历url URL url = urls.nextElement(); Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url)); // 读取映射的spring.factories 文件的KV键值对,存放到properties对象中 String factoryClassNames = properties.getProperty(factoryClassName); // 类似于map一般,获取对应的值 result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames))); // 对值使用逗号分隔,生成list,然后去重添加到result } // 总结下来就是遍历当前类环境中的所有路径为“meta-INF/spring.factories”的文件 // 读取文件,然后获取k为当前类名称的所有值,然后存储到set中返回 return result; } catch (IOException ex) { throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex); } }
到这里整个的initialize操作就已经清楚了,通过类加载器可获取的所有为“meta-INF/spring.factories” 的地址的文件内容,然后获取key为ApplicationContextInitializer.class以及ApplicationListener.class的类名称的值集合
然后依次就行实例化,最后排序返回,最后保存到当前对象的初始化集合以及监听器集合中,便于后续操作
需要注意到SpringFactoriesLoader.loadFactoryNames 后面很多地方都需要使用该方法去获取相关内容
当然现在只是完成了SpringApplication构造器里面的方法,还剩下后面的run(args)方法执行
如下代码块就是SpringBoot的执行过程(最后的套路依旧是Spring framework的执行策略)
2、SpringApplication的run方法启动public ConfigurableApplicationContext run(String... args) {
StopWatch stopWatch = new StopWatch();
stopWatch.start(); // 记录当前服务开始启动
ConfigurableApplicationContext context = null; // 上下文context,非常关键
FailureAnalyzers analyzers = null;
configureHeadlessProperty(); // 给系统设置headless属性值
SpringApplicationRunListeners listeners = getRunListeners(args); // 就是通过SpringFactoriesLoader 获取到所有SpringApplicationRunListener.class的对象
// 其中args是用来进行实例化SpringApplicationRunListener对应的对象的构造器参数
// 最后返回listener是整个系统的监听器
listeners.starting(); // 监听器开始执行
try {
ApplicationArguments applicationArguments = new DefaultApplicationArguments(
args); // 默认程序参数
ConfigurableEnvironment environment = prepareEnvironment(listeners,
applicationArguments); // 准备运行的环境上下文
Banner printedBanner = printBanner(environment); // 打印banner,默认输出当前springboot版本等内容,可以自定义设置文本或者图片
// 具体看下面的方法详解
context = createApplicationContext(); // 创建SpringBoot最重要的上下文容器
analyzers = new FailureAnalyzers(context); // 分析上下文出现问题的点,便于使用者可以直观的发现问题出现在哪里
// 其实套路类似,就是使用SpringFactoriesLoader获取所有的FailureAnalyzer实例对象,然后设置其bean工厂为context的bean工厂上下文
prepareContext(context, environment, listeners, applicationArguments,
printedBanner); // 看名称就是对context的前置准备工作,细节在后面说
refreshContext(context); // 切入到spring framework的方式去完成context内容的装载
// 如果需要注册终止钩子,则注册一个
afterRefresh(context, applicationArguments); // 基本上认为springboot所需的服务都加载完成,进行最后的处理操作
// 里面常用的就是CommandLineRunner
listeners.finished(context, null); // 监听器的启动结束事件,
stopWatch.stop(); // 表示SpringBoot服务启动步骤完成,统计下启动时间等操作
if (this.logStartupInfo) { new StartupInfoLogger(this.mainApplicationClass)
.logStarted(getApplicationLog(), stopWatch); // 打印SpringBoot启动成功的消息,例如 Started xxx in 12.4 seconds 等信息
} return context;
} catch (Throwable ex) {
handleRunFailure(context, listeners, analyzers, ex); // 启动失败了就会输出Application startup failed 日志
// 并且会输出具体的错误内容信息
throw new IllegalStateException(ex);
}
}private ConfigurableEnvironment prepareEnvironment(
SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments) { // Create and configure the environment
ConfigurableEnvironment environment = getOrCreateEnvironment(); // 如果当前环境值不为null,直接返回
// 否则根据上文推断出的webEnvironment boolean 值 生成对象的环境对象
// 当为true的时候,生成StandardServletEnvironment
// 否则生成的是StandardEnvironment
configureEnvironment(environment, applicationArguments.getSourceArgs());
listeners.environmentPrepared(environment); if (!this.webEnvironment) { // 如果不是web的环境,再对当前的环境进行包装,生成一个新的运行环境对象
environment = new EnvironmentConverter(getClassLoader())
.convertToStandardEnvironmentIfNecessary(environment);
} return environment;
}private Banner printBanner(ConfigurableEnvironment environment) { // 参数environment就是上面生成的环境对象
if (this.bannerMode == Banner.Mode.OFF) { // 如果设置了banner关闭模式,则不进行打印输出操作
return null;
}
ResourceLoader resourceLoader = this.resourceLoader != null ? this.resourceLoader
: new DefaultResourceLoader(getClassLoader()); // 资源加载器生成
SpringApplicationBannerPrinter bannerPrinter = new SpringApplicationBannerPrinter(
resourceLoader, this.banner); // 后续使用SpringApplicationBannerPrinter 类的print进行输出操作
if (this.bannerMode == Mode.LOG) { // 打印模式,如果是log则输出到log中,否则输出到终端中
return bannerPrinter.print(environment, this.mainApplicationClass, logger);
} return bannerPrinter.print(environment, this.mainApplicationClass, System.out); // 大致操作就是先看是否存在自定义的图片类型或者文字类型 banner,如果有就优先确定banner对象
// 否则就默认使用SpringBootBanner的banner(这个里面就包含了常规的springboot输出内容)
// 然后解析banner的资源,得出将要输出的字符串内容(利用日志直接输出),存储到PrintedBanner}public static final String DEFAULT_WEB_CONTEXT_CLASS = "org.springframework."
+ "boot.context.embedded.AnnotationConfigEmbeddedWebApplicationContext";public static final String DEFAULT_CONTEXT_CLASS = "org.springframework.context."
+ "annotation.AnnotationConfigApplicationContext";protected ConfigurableApplicationContext createApplicationContext() {
Class> contextClass = this.applicationContextClass; if (contextClass == null) { try {
contextClass = Class.forName(this.webEnvironment
? DEFAULT_WEB_CONTEXT_CLASS : DEFAULT_CONTEXT_CLASS); // 如果是web环境,则使用AnnotationConfigEmbeddedWebApplicationContext
// 否则就使用AnnotationConfigApplicationContext
} catch (ClassNotFoundException ex) { throw new IllegalStateException( "Unable create a default ApplicationContext, "
+ "please specify an ApplicationContextClass",
ex);
}
} return (ConfigurableApplicationContext) BeanUtils.instantiate(contextClass); // 直接通过类,反射生成无构造参数的对象,一般情况就是AnnotationConfigEmbeddedWebApplicationContext对象了}private void prepareContext(ConfigurableApplicationContext context,
ConfigurableEnvironment environment, SpringApplicationRunListeners listeners,
ApplicationArguments applicationArguments, Banner printedBanner) { // 传递上下文、环境、上下文参数等数据
context.setEnvironment(environment);
postProcessApplicationContext(context); // 前置处理context上下文,包含了beanNameGenerator和resourceLoader
// 其中beanNameGenerator 可以自定义规则约定bean的名称功能
applyInitializers(context); // 应用ApplicationContextInitializer去初始化完成对context的操作
// 具体的ApplicationContextInitializer对象就是在SpringApplication对象的构造方法中实例化创建的
// 可以给context添加额外的操作,同时也可以很方便的自定义完成自己需要的功能
listeners.contextPrepared(context); // 执行contextPrepared 上下文准备工作的事件
if (this.logStartupInfo) { // 日志启动标志位,默认为true
logStartupInfo(context.getParent() == null);
logStartupProfileInfo(context); // 明确当前执行的主函数log,输出SpringBoot的开始启动信息
} // 注册springApplicationArguments 这个bean到context中去
context.getBeanFactory().registerSingleton("springApplicationArguments",
applicationArguments); if (printedBanner != null) {
context.getBeanFactory().registerSingleton("springBootBanner", printedBanner); // 同样是注册,打印早就完成了
} // Load the sources
Set
作者:jwfy
链接:https://www.jianshu.com/p/8dff56465dbf



