微信號(hào):GitShare
微信公眾號(hào):愛折騰的稻草
如有問題或建議,請(qǐng)?jiān)诠娞?hào)留言[1]網(wǎng)站建設(shè)哪家好,找創(chuàng)新互聯(lián)!專注于網(wǎng)頁設(shè)計(jì)、網(wǎng)站建設(shè)、微信開發(fā)、重慶小程序開發(fā)公司、集團(tuán)企業(yè)網(wǎng)站建設(shè)等服務(wù)項(xiàng)目。為回饋新老客戶創(chuàng)新互聯(lián)還提供了漢中免費(fèi)建站歡迎大家使用!
為幫助廣大SpringBoot用戶達(dá)到“知其然,更需知其所以然”的境界,作者將通過SpringBoot系列文章全方位對(duì)SpringBoot2.0.0.RELEASE版本深入分解剖析,讓您深刻的理解其內(nèi)部工作原理。
1、[SpringBoot]利用SpringBoot快速構(gòu)建并啟動(dòng)項(xiàng)目
2、[SpringBoot]詳解SpringBoot應(yīng)用的啟動(dòng)過程
SpringBoot啟動(dòng)時(shí),在創(chuàng)建SpringApplication對(duì)象時(shí),會(huì)自動(dòng)去識(shí)別并設(shè)置當(dāng)前應(yīng)用是普通web應(yīng)用、響應(yīng)式web應(yīng)用還是非web應(yīng)用,其內(nèi)部實(shí)現(xiàn)原理是什么?
首先看看源代碼
/**
* 推斷應(yīng)用的類型
*/
private WebApplicationType deduceWebApplicationType() {
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;
}
ClassUtils.isPresent()方法:
其作用是判斷所提供的類名的類是否存在,且可以被加載。源代碼如下:
public static boolean isPresent(String className, @Nullable ClassLoader classLoader) {
try {
forName(className, classLoader);
return true;
}
catch (Throwable ex) {
// Class or one of its dependencies is not present...
return false;
}
}
調(diào)用了forName()方法,如果出現(xiàn)異常,則返回false,也就是提供目標(biāo)類不存在。
forName()方法的源碼剖析:
public static Class<?> forName(String name, @Nullable ClassLoader classLoader)
throws ClassNotFoundException, LinkageError {
Assert.notNull(name, "Name must not be null");
//根據(jù)基本類的JVM命名規(guī)則(如果合適的話),將給定的類名name解析為基本類型的包裝類
Class<?> clazz = resolvePrimitiveClassName(name);
if (clazz == null) {
//commonClassCache是包含java.lang包下所有類,將類的類名作為鍵,對(duì)應(yīng)類作為值的一個(gè)Map集合。
clazz = commonClassCache.get(name); //根據(jù)類名,獲取commonClassCache集合中的值,如果為空,表示目標(biāo)類不是java.lang包的下類,即不是原始類型。
}
if (clazz != null) {
//如果根據(jù)類名,已經(jīng)獲取到了類,則直接返回該類。
return clazz;
}
//判斷clas屬性值是否為數(shù)組對(duì)象。比如:java.lang.String[]
// "java.lang.String[]" style arrays
if (name.endsWith(ARRAY_SUFFIX)) {
//如果是,則將類名后的“[]”方括號(hào)截去,返回java.lang.String,遞歸查找類名,找到后,將Class類型轉(zhuǎn)換為數(shù)組。
String elementClassName = name.substring(0, name.length() - ARRAY_SUFFIX.length());
Class<?> elementClass = forName(elementClassName, classLoader);
return Array.newInstance(elementClass, 0).getClass();
}
//class屬性值是否為數(shù)組對(duì)象的二進(jìn)制表示。比如:[Ljava.lang.String
// "[Ljava.lang.String;" style arrays
if (name.startsWith(NON_PRIMITIVE_ARRAY_PREFIX) && name.endsWith(";")) {
//如果是,則將值的“[L”部分截去,遞歸查找類名,找到后,將對(duì)應(yīng)的Class類型轉(zhuǎn)換為數(shù)組
String elementName = name.substring(NON_PRIMITIVE_ARRAY_PREFIX.length(), name.length() - 1);
Class<?> elementClass = forName(elementName, classLoader);
return Array.newInstance(elementClass, 0).getClass();
}
//class屬性值是否為二維數(shù)組
// "[[I" or "[[Ljava.lang.String;" style arrays
if (name.startsWith(INTERNAL_ARRAY_PREFIX)) {
String elementName = name.substring(INTERNAL_ARRAY_PREFIX.length());
Class<?> elementClass = forName(elementName, classLoader);
return Array.newInstance(elementClass, 0).getClass();
}
//獲取classLoader
ClassLoader clToUse = classLoader;
if (clToUse == null) {
//如果classLoader為空,則獲取默認(rèn)的classLoader對(duì)象。
clToUse = getDefaultClassLoader();
}
try {
//返回加載后的類
return (clToUse != null ? clToUse.loadClass(name) : Class.forName(name));
}
catch (ClassNotFoundException ex) {
//用于處理內(nèi)部類的情況。
int lastDotIndex = name.lastIndexOf(PACKAGE_SEPARATOR);
if (lastDotIndex != -1) {
//拼接內(nèi)部類的名字。
String innerClassName = name.substring(0, lastDotIndex) + INNER_CLASS_SEPARATOR + name.substring(lastDotIndex + 1);
try {
return (clToUse != null ? clToUse.loadClass(innerClassName) : Class.forName(innerClassName));
}
catch (ClassNotFoundException ex2) {
// Swallow - let original exception get through
}
}
throw ex;
}
}
再來看看使用到的常量值:
REACTIVE_WEB_ENVIRONMENT_CLASS:org.springframework.web.reactive.DispatcherHandler
MVC_WEB_ENVIRONMENT_CLASS:org.springframework.web.servlet.DispatcherServlet
WEB_ENVIRONMENT_CLASSES:{"javax.servlet.Servlet", "org.springframework.web.context.ConfigurableWebApplicationContext" }
也就是說,
1、如果應(yīng)用程序中存在org.springframework.web.reactive.DispatcherHandler這個(gè)類,則表示是一個(gè)響應(yīng)式web應(yīng)用,項(xiàng)目在啟動(dòng)時(shí),需要去
加載啟動(dòng)內(nèi)嵌的響應(yīng)式web服務(wù)器。
2、如果應(yīng)用程序中既不存在javax.servlet.Servlet類,也不存在org.springframework.web.context.ConfigurableWebApplicationContext這個(gè)類,則
表示當(dāng)前應(yīng)用不是一個(gè)web應(yīng)用,啟動(dòng)時(shí)無需加載啟動(dòng)內(nèi)嵌的web服務(wù)器。
3、除上述兩種情況外,則表示當(dāng)前應(yīng)用是一個(gè)servlet的web應(yīng)用,啟動(dòng)時(shí)需要加載啟動(dòng)內(nèi)嵌的servlet的web服務(wù)器(比如Tomcat)。
SpringBoot啟動(dòng)時(shí),在創(chuàng)建SpringApplication對(duì)象時(shí),最后會(huì)推斷并設(shè)置main方法的定義類(啟動(dòng)類),其實(shí)現(xiàn)原理是什么呢?
先看看源代碼;
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;
}
java.lang.StackTraceElement是什么?
類元素代表一個(gè)堆棧幀。除了一個(gè)在堆棧的頂部所有的棧幀代表一個(gè)方法調(diào)用。在堆棧頂部的幀表示在將其生成的堆棧跟蹤的執(zhí)行點(diǎn)。
stackTraceElement.getMethodName() 返回一個(gè)包含由該堆棧跟蹤元素所表示的執(zhí)行點(diǎn)的方法的名稱。
stackTraceElement.getClassName() 返回一個(gè)包含由該堆棧跟蹤元素所表示的執(zhí)行點(diǎn)類的完全限定名。
java.lang.Class.forName()的作用是什么?
java.lang.Class.forName(String name, boolean initialize, ClassLoader loader) 方法返回與給定字符串名的類或接口的Class對(duì)象,使用給定的類加載器。
參數(shù)說明
- name :這是所需類的完全限定名稱。
- initialize : 這說明這個(gè)類是否必須初始化。
- loader : 這是必須加載的類的類加載器。
異常說明
- LinkageError : 如果聯(lián)動(dòng)失敗。
- ExceptionInInitializerError : 如果這種方法所引發(fā)的初始化失敗。
- ClassNotFoundException : 如果類不能位于由指定的類加載器。
參數(shù)使用
- ClassLoader loader:如果該參數(shù)加載器loader 為空,通過引導(dǎo)類加載器加載類。
- boolean initialize:如果它沒有被初始化,則initialize參數(shù)為true。
通過上面知識(shí)點(diǎn)的講解,deduceMainApplicationClass的作用就非常清晰了,主要是獲取當(dāng)前方法調(diào)用棧,遍歷調(diào)用堆棧信息找到main函數(shù)的類,并返回該類。
1、SpringBoot是通過調(diào)用ClassUtils類的isPresent方法,檢查classpath中是否存在org.springframework.web.reactive.DispatcherHandler類、
javax.servlet.Servlet類和org.springframework.web.context.ConfigurableWebApplicationContext類來判斷當(dāng)前應(yīng)用是響應(yīng)式Web應(yīng)用,還是普通的Servlet的Web應(yīng)用,還是非Web應(yīng)用。
2、SpringBoot是通過獲取當(dāng)前方法的調(diào)用棧信息,來判斷當(dāng)前main函數(shù)所在的類。
為幫助廣大SpringBoot用戶達(dá)到“知其然,更需知其所以然”的境界,作者將通過SpringBoot系列文章全方位對(duì)SpringBoot2.0.0.RELEASE版本深入分解剖析,讓您深刻的理解其內(nèi)部工作原理。
敬請(qǐng)關(guān)注[愛折騰的稻草(GitShare)]公眾號(hào),為您提供更多更優(yōu)質(zhì)的技術(shù)教程。
分享文章:[SpringBoot]深入淺出剖析SpringBoot的應(yīng)用類型識(shí)別機(jī)制
本文網(wǎng)址:http://www.rwnh.cn/article48/ipcjhp.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供做網(wǎng)站、網(wǎng)站營(yíng)銷、小程序開發(fā)、自適應(yīng)網(wǎng)站、網(wǎng)站收錄、品牌網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)
營(yíng)銷型網(wǎng)站建設(shè)知識(shí)