
我们一般SpringBoot的启动类入口为以下代码:
public static void main(String[] args) {
new SpringApplication().run(Application.class,args);
}
第一个参数传@SpringBootApplication注解所在类,大部分情况下应该和main函数同类,第二个参数一般传空数组。
Debug进入run方法,我们可以看到以下代码:
public static ConfigurableApplicationContext run(Object[] sources, String[] args) {
return (new SpringApplication(sources)).run(args);
}
所以分为两部分:
- new一个SpringApplication对象
- 运行SpringApplication对象的run方法
接下来一步步看。
2.创建SpringApplication对象Debug进入SpringApplication对象的构造方法如下:
public SpringApplication(Object... sources) {
this.bannerMode = Mode.CONSOLE;
this.logStartupInfo = true;
this.addCommandLineProperties = true;
this.headless = true;
this.registerShutdownHook = true;
this.additionalProfiles = new HashSet();
this.initialize(sources);
}
构造方法里初始化了SpringApplication的一些属性,重点看initialize()方法:
private void initialize(Object[] sources) {
if (sources != null && sources.length > 0) {
this.sources.addAll(Arrays.asList(sources));
}
this.webEnvironment = this.deduceWebEnvironment();
this.setInitializers(this.getSpringFactoriesInstances(ApplicationContextInitializer.class));
this.setListeners(this.getSpringFactoriesInstances(ApplicationListener.class));
this.mainApplicationClass = this.deduceMainApplicationClass();
}
以上方法的属性我们一个个看
- sources对象,这边sources参数就是我们一开始传的带@SpringBootApplication注解的类。
- webEnvironment对象,就是判断javax.servlet.Servlet,org.springframework.web.context.ConfigurableWebApplicationContext两个类是否存在。
- mainApplicationClass对象,就是根据调用堆栈来获取我们main方法所在的类。
- 剩下的Initializers和Listeners对象初始化方法一样,同样该方法也是SpringBoot启动过程中一个非常重要的方法。拎出来单独讲讲。
privateCollection extends T> getSpringFactoriesInstances(Class type, Class>[] parameterTypes, Object... args) { ClassLoader classLoader = Thread.currentThread().getContextClassLoader(); Set names = new linkedHashSet(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); List instances = this.createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); AnnotationAwareOrderComparator.sort(instances); return instances; }
根据上面的函数名随便猜一猜,SpringFactoriesLoader.loadFactoryNames(type, classLoader)一看就是通过classLoader去获取对应的Class集合,createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names)这个一看就是通过上面获取的Class集合去实例化一些东西,顺便提一嘴getContextClassLoader()获取到的ClassLoader为AppClassLoader,这个又和那个双亲委派模型有关系了,后面再提,接着验证下刚才两个函数。
public static ListloadFactoryNames(Class> factoryClass, ClassLoader classLoader) { String factoryClassName = factoryClass.getName(); try { Enumeration urls = classLoader != null ? classLoader.getResources("meta-INF/spring.factories") : ClassLoader.getSystemResources("meta-INF/spring.factories"); ArrayList result = new ArrayList(); while(urls.hasMoreElements()) { URL url = (URL)urls.nextElement(); Properties properties = PropertiesLoaderUtils.loadProperties(new UrlResource(url)); String factoryClassNames = properties.getProperty(factoryClassName); result.addAll(Arrays.asList(StringUtils.commaDelimitedListToStringArray(factoryClassNames))); } return result; } catch (IOException var8) { throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() + "] factories from location [" + "meta-INF/spring.factories" + "]", var8); } }
可以看到loadFactoryNames就是去加载"meta-INF/spring.factories",然后获取factories文件里的配置,StringUtils.commaDelimitedListToStringArray(factoryClassNames)这个函数以前没用过,挺好用的,记一下。看下factories文件的格式,可以看到是key-value(list)组成的配置文件,具体如下:
# Application Context Initializers org.springframework.context.ApplicationContextInitializer= org.springframework.boot.context.ConfigurationWarningsApplicationContextInitializer, org.springframework.boot.context.ContextIdApplicationContextInitializer, org.springframework.boot.context.config.DelegatingApplicationContextInitializer, org.springframework.boot.context.embedded.ServerPortInfoApplicationContextInitializer # Application Listeners org.springframework.context.ApplicationListener= org.springframework.boot.ClearCachesApplicationListener, org.springframework.boot.builder.ParentContextCloserApplicationListener, org.springframework.boot.context.FileEncodingApplicationListener, org.springframework.boot.context.config.AnsiOutputApplicationListener, org.springframework.boot.context.config.ConfigFileApplicationListener, org.springframework.boot.context.config.DelegatingApplicationListener, org.springframework.boot.liquibase.LiquibaseServiceLocatorApplicationListener, org.springframework.boot.logging.ClasspathLoggingApplicationListener, org.springframework.boot.logging.LoggingApplicationListener
再看下createSpringFactoriesInstances方法
privateList createSpringFactoriesInstances(Class type, Class>[] parameterTypes, ClassLoader classLoader, Object[] args, Set names) { List instances = new ArrayList(names.size()); Iterator var7 = names.iterator(); while(var7.hasNext()) { String name = (String)var7.next(); try { Class> instanceClass = ClassUtils.forName(name, classLoader); Assert.isAssignable(type, instanceClass); Constructor> constructor = instanceClass.getDeclaredConstructor(parameterTypes); T instance = BeanUtils.instantiateClass(constructor, args); instances.add(instance); } catch (Throwable var12) { throw new IllegalArgumentException("Cannot instantiate " + type + " : " + name, var12); } } return instances; }
可以看到基本思路就是拿到Class然后通过反射来拿构造方法实例化对象。
好了SpringApplication的初始化写完了,接下来就是该run方法了,这个太长,分两篇写好了。
欢迎分享,转载请注明来源:内存溢出
微信扫一扫
支付宝扫一扫
评论列表(0条)