如何閱讀java源碼,SpringBoot-源碼閱讀

 2023-10-15 阅读 31 评论 0

摘要:從SpringApplication.run方法閱讀,本文中編號順序與代碼執行順序完全相同 0.SpringApplication類deduceWebEnvironment方法 private boolean deduceWebEnvironment() {for (String className : WEB_ENVIRONMENT_CLASSES) {//就是通過class.forname判斷,如果拋異

從SpringApplication.run方法閱讀,本文中編號順序與代碼執行順序完全相同

0.SpringApplication類deduceWebEnvironment方法

private boolean deduceWebEnvironment() {for (String className : WEB_ENVIRONMENT_CLASSES) {//就是通過class.forname判斷,如果拋異常了,就說明不包含該類if (!ClassUtils.isPresent(className, null)) {return false;}}return true;}

說白了就是判斷當前這個項目是不是一個web項目,判斷的方式很簡單,如果引用的jar包中,包含了javax.servlet.Servlet或者spring自己的ConfigurableWebApplicationContext,則認為是個web項目,將是不是的結果存到SpringApplication類webEnvironment中,true是web項目flase不是web項目

如何閱讀java源碼,1.SpringFactoriesLoader類loadFactoryNames方法
(也可參考這篇文章)

//此時參數factoryClass=ApplicationContextInitializer.class,spring寫死的,從run方法跟蹤可以很快發現
public static List<String> loadFactoryNames(Class<?> factoryClass, ClassLoader classLoader) {String factoryClassName = factoryClass.getName();try {//獲取所有引用jar包classpath下的MATE-INF文件夾的spring.factories文件Enumeration<URL> urls = (classLoader != null ? classLoader.getResources(FACTORIES_RESOURCE_LOCATION) :ClassLoader.getSystemResources(FACTORIES_RESOURCE_LOCATION));List<String> result = new ArrayList<String>();//循環創建每個spring.factories文件的Properties實例,并且從這些實例中只找key//為ApplicationContextInitializer的,取其value值,因為value值多個的時候,是用逗號分隔的,所以使用逗號拆分,然后添加到一個list中,最后返回while (urls.hasMoreElements()) {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 ex) {throw new IllegalArgumentException("Unable to load [" + factoryClass.getName() +"] factories from location [" + FACTORIES_RESOURCE_LOCATION + "]", ex);}}

首先獲取_所有引用jar包classpath下_的MATE-INF文件夾的spring.factories(可以認為就是properties)文件,讀取其內容,如果key值是ApplicationContextInitializer,則取其value,并放到一個list中,返回,之后會用到value(其實這些value都是接口ApplicationContextInitializer的實現類)

注:在spring cloud中,每個組件的jar包,都會有spring.factories文件,比如eurake的jar包下,存在該文件,記錄的是關于eurake相關的內容,zuul的jar包下,存在該文件,記錄的是關于zuul的相關內容

2.SpringApplication類createSpringFactoriesInstances方法

//沒什么好說的,就是根據全限定名,反射創建對象,放到List里,然后返回
//type=ApplicationContextInitializer.class
//names=上個方法的返回值
private <T> List<T> createSpringFactoriesInstances(Class<T> type,Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args,Set<String> names) {List<T> instances = new ArrayList<T>(names.size());for (String name : names) {try {//通過反射創建個對象Class<?> instanceClass = ClassUtils.forName(name, classLoader);//判斷上邊這個對象是不是ApplicationContextInitializer接口的實現類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;}

如何高效閱讀開源項目源碼?根據1的返回值,通過反射,創建ApplicationContextInitializer接口的實例,無其他操作

3.SpringApplication類addInitializers方法

//initializers=2的返回值
public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) {this.initializers = new ArrayList<ApplicationContextInitializer<?>>();this.initializers.addAll(initializers);
}

該方法就是將2的返回值保存到SpringApplication類的initializers中

4.SpringApplication類setListeners方法

setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));

Spring源碼,該方法重復123的步驟,不過123是加載ApplicationContextInitializer實例,而本方法是加載ApplicationListener接口的實例,然后賦值到SpringApplication類listeners屬性中

5.SpringApplication類deduceMainApplicationClass方法

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;
}

判斷程序執行到此為止,是不是從某個類的main方法開始的,如果是,則返回該class對象(也就是springboot的啟動類),并將該class對象賦值到SpringApplication類mainApplicationClass屬性中

6.SpringApplication類configureHeadlessProperty()方法

private void configureHeadlessProperty() {System.setProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS,System.getProperty(SYSTEM_PROPERTY_JAVA_AWT_HEADLESS, Boolean.toString(this.headless)));}

轉發閱讀源碼。這個沒什么好說的,就是將程序設置成headless模式,相當于代碼這樣寫

System.setProperty("java.awt.headless","true");

7.SpringApplication類getRunListeners方法

private SpringApplicationRunListeners getRunListeners(String[] args) {Class<?>[] types = new Class<?>[] { SpringApplication.class, String[].class };return new SpringApplicationRunListeners(logger,getSpringFactoriesInstances(SpringApplicationRunListener.class, types, this, args));}

再次使用SpringFactoriesLoader.loadFactoryNames()方法,將META-INF/spring.factories文件中SpringApplicationRunListener接口的實現類都取出來,之后調用SpringApplicationRunListeners(注意有S)的starting()方法,此處就是觀察者模式,for循環調用每一個SpringApplicationRunListener(沒有S)

版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。

原文链接:https://hbdhgg.com/3/137117.html

发表评论:

本站为非赢利网站,部分文章来源或改编自互联网及其他公众平台,主要目的在于分享信息,版权归原作者所有,内容仅供读者参考,如有侵权请联系我们删除!

Copyright © 2022 匯編語言學習筆記 Inc. 保留所有权利。

底部版权信息