從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)
版权声明:本站所有资料均为网友推荐收集整理而来,仅供学习和研究交流使用。
工作时间:8:00-18:00
客服电话
电子邮件
admin@qq.com
扫码二维码
获取最新动态