標籤:val cto 代碼 main 抽象 sas null 源碼 TE
SpringApplication.run(Main.class, args);
從這個方法開始講吧:
public static ConfigurableApplicationContext run(Class<?> primarySource, String... args) { return run(new Class<?>[] { primarySource }, args);}
ConfigurableApplicationContext 這個介面,熟悉spring源碼的童鞋們肯定一眼就會有親切感
至於不熟悉的童鞋們嘛...未來我可能會再開一個spring的坑
這裡不詳細介紹了
回到這個方法本身:
方法內容很簡單,指向了另一個run方法:
public static ConfigurableApplicationContext run(Class<?>[] primarySources, String[] args) { return new SpringApplication(primarySources).run(args);}
先建立了一個SpringApplication的對象,
然後再次指向了一個新的run方法。
不過在我們看下一個run方法之前,按照順序,
我們先來看一下SpringApplication的這個構造方法:
public SpringApplication(Class<?>... primarySources) { this(null, primarySources);}
public SpringApplication(ResourceLoader resourceLoader, Class<?>... primarySources) { //將resourceLoader屬性設定為同名參數 this.resourceLoader = resourceLoader; //判定primarySources是否為空白,若為空白則 throw new IllegalArgumentException("PrimarySources must not be null"); Assert.notNull(primarySources, "PrimarySources must not be null"); //確定primarySources不為空白的前提下,將primarySource注入到屬性primarySources中 this.primarySources = new LinkedHashSet<>(Arrays.asList(primarySources)); //設定屬性webApplicationType //關於deduceWebApplicationType()這個方法,暫且按下不表,後面會講到 this.webApplicationType = deduceWebApplicationType(); //通過setter方法設定了initializers和listeners兩個屬性 //至於他到底設定了個什麼東西嘛,後面會講到 setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class)); setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class)); //最後,設定屬性mainApplicationClass //deduceMainApplicationClass()這個方法我們後面來講 this.mainApplicationClass = deduceMainApplicationClass();}
簡單總結一下就是:
進行了一輪初始化;
設定了一大堆不知道什麼鬼用的參數
對象產生了以後,我們來看看這個run方法...
等等
本著刨根問底的精神,我們先解決前面懸而未決的問題:
關於deduceWebApplicationType();這個方法:
private WebApplicationType deduceWebApplicationType() { //這裡有三個參數 //REACTIVE_WEB_ENVIRONMENT_CLASS : dispatcherHandler的全類名 //MVC_WEB_ENVIRONMENT_CLASS : dispatcherServlet的全類名 //WEB_ENVIRONMENT_CLASSES : 是一個String[],裡面有兩項: //一個是Servlet的全類名,一個是ConfigurableWebApplicationContext的全類名 //而isPresent方法,簡單講就是判斷這個類是否存在,存在則返回true,反之返回false //所以說spring boot項目中maven的dependency不能隨便搞,不能多加也不能少加。 //spring boot會根據你的依賴來判斷你這個項目的性質 //後面還會有很多這種根據依賴的不同來判定的地方 //然後說一下這個WebApplicationType: //是一個枚舉類,裡面有三項: //SERVLET,REACTIVE,NONE //只起一個標識作用,標明你這個項目到底是一個servlet項目,還是reactive項目,還是不連網(none)的項目 if (ClassUtils.isPresent(REACTIVE_WEB_ENVIRONMENT_CLASS, null) && !ClassUtils.isPresent(MVC_WEB_ENVIRONMENT_CLASS, null)) { return WebApplicationType.REACTIVE; } for (String className : WEB_ENVIRONMENT_CLASSES) { if (!ClassUtils.isPresent(className, null)) { return WebApplicationType.NONE; } } return WebApplicationType.SERVLET;}
所以,前面構造器中:
this.webApplicationType = deduceWebApplicationType();
就是根據你的jar包依賴,設定了這個項目的類型
然後是我們的兩個setter方法:
setInitializers 和 setListeners
你可能會很奇怪,為什麼都有構造器了,還要用setter方法。
答案很簡單啦,因為這個setter方法顯然不會是那種簡單的:
this.xx = xx;
這點東西而已啦。
雖然,倒也沒複雜到哪去:
public void setInitializers(Collection<? extends ApplicationContextInitializer<?>> initializers) { this.initializers = new ArrayList<>(); this.initializers.addAll(initializers);}
由以上代碼可知,這個initializers就是一個ArrayList,把你參數中的itializers裡面的所有項都放了進去。就醬;
public void setListeners(Collection<? extends ApplicationListener<?>> listeners) { this.listeners = new ArrayList<>(); this.listeners.addAll(listeners);}
除了變數名和參數泛型之外,好像就跟上面那個一模一樣= =
兩個setter方法講完了,不過構造器中這兩個setter方法的參數,是由一個叫getSpringFactoriesInstances這個方法
下一步我們來看這個方法:
private <T> Collection<T> getSpringFactoriesInstances(Class<T> type) { return getSpringFactoriesInstances(type, new Class<?>[] {});}
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(這個是官方注釋,意思是說用互不相同的名字來確保不發生重複) //至於這個loadFactoryNames這個方法,簡單講就是根據介面或者抽象類別的class對象和類載入器,尋找所有對應的工廠類的全類名 Set<String> names = new LinkedHashSet<>(SpringFactoriesLoader.loadFactoryNames(type, classLoader)); //顧名思義,createSpringFactoriesInstances就是建立Spring工廠的執行個體 //這個方法的具體代碼我們等下來看 List<T> instances = createSpringFactoriesInstances(type, parameterTypes, classLoader, args, names); //簡單講,這就是排了個序。 //稍微詳細點講,根據對象的order value進行排序 //更詳細的講,等我開spring坑再說 //事先聲明,這坑我不一定會開= = //這坑太大了 //寫spring是春天,講spring是冬天 AnnotationAwareOrderComparator.sort(instances); //排完序了,返回回去。 return instances;}
所以上面的方法,實際重點就是那個createSpringFactoriesInstances。
再次說明一下上面那個方法中,我們調用createSpringFactoriesInstances方法時的五個參數:
type:一個Class對象。在前面的構造器中:
setInitializers方法裡注入的是ApplicationContextInitializer.class這個Class對象,
setListeners方法裡注入的是ApplicationListener.class這個Class對象。
paramerterTypes:一個Class數組。
在getSpringFactoriesInstances(Class<T> type)方法中,他注入的是一個空數組。
classLoader:類載入器。方法中用的是線程的類載入器。
args:一堆參數。
在getSpringFactoriesInstances(Class<T> type)方法中,他注入的是null。
names:一個LinkedHashSet<String>
由上面的type和classloader找到這個type所有對應的工廠類的全類名
好的請記住這些,來看下面的方法:
private <T> List<T> createSpringFactoriesInstances( Class<T> type, Class<?>[] parameterTypes, ClassLoader classLoader, Object[] args, Set<String> names) { //很顯然,instances就是我們最終要返回的list //從這個arraylist的範圍來看,很顯然這個list裡的每一項都跟names有關 List<T> instances = new ArrayList<>(names.size()); for (String name : names) { try { //根據name和classloader來找類對象 //所以前面一定要保證這裡的Names不一樣不然list裡面就會有重複的了。 Class<?> instanceClass = ClassUtils.forName(name, classLoader); //簡單講,看看type是不是instanceClass的超類或者介面。 //如果是,往下走;如果不是,報錯。 Assert.isAssignable(type, instanceClass); //如果代碼執行到此,說明type是instanceClass的超類。 //parameterTypes由此可見是instanceClass的構造器的參數類型表 Constructor<?> constructor = instanceClass .getDeclaredConstructor(parameterTypes); //簡單講,就是用剛剛得到的構造器,和args來構造一個T的執行個體 //因此args和parameterTypes是一一對應的。 //前面已經判定過,type是instanceClass的超類或者介面。 //因此這個強制類型轉換是成立的。 T instance = (T) BeanUtils.instantiateClass(constructor, args); //把這個執行個體加到最開始聲明的list裡面 instances.add(instance); } catch (Throwable ex) { //前面如果報錯了,那就再報個錯 //spring向來如此,報錯一個接著一個 //最終的效果就是,不管出了什麼p大點兒錯,都要先給你報滿滿一大長串的錯 //然後每次看console...哎,頭大 throw new IllegalArgumentException( "Cannot instantiate " + type + " : " + name, ex); } } //最終把最上面那個list返回回去 return instances;}
綜上所述,上面的方法就是建立了所有type所對應的工廠類的類對象。
此之謂createSpringFactoriesInstances
回到前面的構造方法:
setInitializers((Collection) getSpringFactoriesInstances(ApplicationContextInitializer.class));
就是將initializers初始化為ApplicationContextInitializer.class 所對應的所有工廠類的對象組成的集合
setListeners((Collection) getSpringFactoriesInstances(ApplicationListener.class));
就是將listeners初始化為ApplicationListener.class所對應的所有工廠類的對象組成的集合
至此,構造方法完畢。
然後我們看run方法。。。
看個鬼啊,寫這麼多,你們看著累不?
我反正累了。
剩下的下次再說。
拜拜~~~~~~~~~
Spring boot 源碼分析(一)SpringApplication.run(上)