เมื่อเร็ว ๆ นี้ฉันมีเวลาในการเพิ่มประสิทธิภาพแพลตฟอร์ม API แบบเปิดของ บริษัท เมื่อฉันพิมพ์แพ็คเกจ Jar ฉันก็รู้ว่าฉันมีทักษะในการใช้รองเท้าสปริงมาก่อน แต่ฉันไม่เคยรู้ว่าหลักการเริ่มต้นของขวดที่ผลิตโดย Spring Boot จากนั้นฉันก็คลายซิปขวดในครั้งนี้และดูมัน มันแตกต่างจากสิ่งที่ฉันจินตนาการจริงๆ ต่อไปนี้เป็นการวิเคราะห์ที่สมบูรณ์ของโถที่คลายซิป
แอปพลิเคชันของสปริงบูตไม่ได้โพสต์ โครงสร้างของการสาธิตที่ง่ายกว่านั้นคล้ายกัน นอกจากนี้เวอร์ชันของ Spring Boot ที่ฉันใช้คือ 1.4.1 มีบทความอื่นบนอินเทอร์เน็ตเพื่อวิเคราะห์การเริ่มต้นของ Spring Boot Jar ซึ่งควรต่ำกว่า 1.4 และวิธีการเริ่มต้นก็แตกต่างจากเวอร์ชันปัจจุบันมาก
หลังจากการติดตั้ง MVN Clean เมื่อเราดูในไดเรกทอรีเป้าหมายเราจะพบแพ็คเกจสองขวดดังต่อไปนี้:
xxxx.jarxxx.jar.original
นี่เป็นผลมาจากกลไกปลั๊กอินสปริงบูตซึ่งเปลี่ยนขวดธรรมดาให้กลายเป็นแพ็คเกจ Jar ที่ใช้งานได้และ xxx.jar.original เป็นแพ็คเกจ Jar ที่ผลิตโดย Maven สิ่งเหล่านี้สามารถพบได้ในการอ้างอิงถึงบทความบนเว็บไซต์ฤดูใบไม้ผลิอย่างเป็นทางการดังนี้:
http://docs.spring.io/spring-boot/docs/current/reference/htmlsingle/#executable-jar
ต่อไปนี้เป็นส่วนหนึ่งของโครงสร้างไดเรกทอรีของขวดที่ผลิตโดยแอปพลิเคชันสปริงบูตซึ่งส่วนใหญ่จะถูกละเว้นและมีเพียงส่วนสำคัญเท่านั้นที่แสดง
.├── boot-inf ├──คลาส├── Application-dev.properties│─ Application-prod.properties│├─7แอปพลิเคชันproperties││└││││ ││││││││││││บางที swaggerconfig │││││││ swaggerconfig.class│││││││││├├└└│││││ └──guru.css│├images│──fbcover1200x628.png│─ $ newbannerboots_2.png│─Llib│─ accessors-smart-12.1 com.weibangong.open│──เปิด-เซิร์ฟเวอร์-openapi│──pom.properties│──pom.xml└─ org └─ springframework └──บูต└─ loader ├──เปิดตัว Urlclassloader $ 1.class ├─LANKEDURLCLASSLOADER.CLASS├─LAY -LAINKER.CLASS ├THALSarch─เก็บถาวร│──การเก็บถาวร $ entry.class │── Archive $ ententfilter.class ExplodeDarchive $ fileEntry.class │── ExplodeDarchive $ fileentryiterator $ entryComparator.class ├── ExplodeDarchive $ fileentryiterator.class
นอกเหนือจากชั้นเรียนที่เราเขียนไว้ในแอปพลิเคชันแล้วขวดนี้ยังมีแพ็คเกจ ORG แยกต่างหาก ควรเป็นไปได้ว่าแอปพลิเคชันสปริงบูตใช้ปลั๊กอินสปริงบูตเพื่อเข้าสู่แพ็คเกจนี้ซึ่งคือการปรับปรุงเวทีแพ็คเกจในวงจรชีวิต MVN มันเป็นแพ็คเกจนี้ที่มีบทบาทสำคัญในกระบวนการเริ่มต้น นอกจากนี้แอปพลิเคชันที่ต้องการจะถูกป้อนใน JAR และป้อนแพ็คเกจการบูตสปริงเพิ่มเติม ขวดนี้ที่สามารถทั้งหมดได้เรียกว่า fat.jar ที่นี่เรามักจะใช้ fat.jar เพื่อแทนที่ชื่อของขวด
ในเวลานี้เราจะดูไฟล์ manifest.mf ใน meta-inf ต่อไปดังนี้:
Manifest-version: 1.0 การสร้าง-title: Open :: Server :: Openapiimplementation-Version: 1.0-Snapshotarchiver-Version: Plexus ArchiverBuilt-by: Xiaxuanimplementation-Vendor-Id: Com.weibangong.openspring Inc.Main-Class: org.springframework.boot.loader.propertieslauncherstart-class: com.weibangong.open.openapi.springbootwebapplicationspring-boot-classes: boot-inf/classes/spring-boot-lib- 1.8.0_20Implementation-url: http://maven.apache.org/open-server-openapi
คลาสหลักที่ระบุไว้ที่นี่คือไฟล์คลาสในแพ็คเกจที่ถูกป้อนแยกต่างหากแทนโปรแกรมเริ่มต้นของเราจากนั้นไฟล์ Manifest.mf จะมีคลาสเริ่มต้นแยกต่างหากที่ระบุโปรแกรมเริ่มต้นของแอปพลิเคชันของเรา
ก่อนอื่นเราจะพบคลาส org.springframework.boot.load.propertieslauncher โดยที่วิธีหลักคือ:
โมฆะคงที่สาธารณะหลัก (สตริง [] args) โยนข้อยกเว้น {propertieslauncher launcher = คุณสมบัติใหม่ Launcher (); args = launcher.getargs (args); launcher.launch (args);}ตรวจสอบวิธีการเปิดตัว วิธีนี้พบได้ในตัวเรียกใช้คลาสแม่และวิธีการเปิดตัวคลาสแม่มีดังนี้:
การป้องกันการเปิดตัว (สตริง [] args, สตริง mainclass, classloader classloader) พ่นข้อยกเว้น {thread.currentthread (). setContextClassLoader (classloader); this.createmainmethodrunner (MainClass, args, classloader) .run (); } ป้องกัน MainMethodrunner createMainMetHodrunner (สตริง mainclass, string [] args, classloader classloader) {ส่งคืน mainmethodrunner ใหม่ (MainClass, args); -วิธีการเปิดตัวในที่สุดเรียกใช้เมธอด createMainMethodrunner ซึ่งสร้างอินสแตนซ์วัตถุ MainMethodrunner และเรียกใช้วิธีการเรียกใช้ เราไปที่ซอร์สโค้ด MainMethodrunner ดังต่อไปนี้:
แพ็คเกจ org.springframework.boot.loader; นำเข้า java.lang.reflect.method; MainMethodrunner ระดับสาธารณะ {สตริงสุดท้ายส่วนตัว MainClassName; สตริงสุดท้ายส่วนตัว [] args; Public MainMethodrunner (สตริง MainClass, String [] args) {this.mainClassName = MainClass; this.args = args == null? null: (string []) args.clone (); } public void run () พ่นข้อยกเว้น {class mainClass = tread.currentThread (). getContextClassLoader (). loadClass (this.mainClassName); วิธีการ mainmethod = mainclass.getDeclaredMethod ("Main", คลาสใหม่ [] {String []. class}); mainmethod.invoke ((วัตถุ) null, วัตถุใหม่ [] {this.args}); -การตรวจสอบวิธีการเรียกใช้มันง่ายมากที่จะเรียกใช้ขวดสปริงบูตและการวิเคราะห์นั้นจบลงแล้ว
5. กระบวนการเริ่มต้นของโปรแกรมหลัก
หลังจากพูดคุยเกี่ยวกับกระบวนการเริ่มต้นของ JAR มาพูดคุยเกี่ยวกับกระบวนการเริ่มต้นและการโหลดของโปรแกรมหลักในแอปพลิเคชัน Spring Boot ก่อนอื่นมาดูวิธีหลักของแอปพลิเคชันสปริงบูต
แพ็คเกจ cn.com.devh; นำเข้า org.springframework.boot.springapplication; นำเข้า org.springframework.boot.autoconfigure.springbootapplication; นำเข้า org.springframework.cloud.client.discovery.enablecoverycovery org.springframework.cloud.netflix.eureka.enableeurekaclient นำเข้า org.springframework.cloud.netflix.feign.enablefeignclients;/*** สร้างโดย Xiaxuan เมื่อวันที่ 17/8/25 */@springbootapplication@enablefeignclients@enableeurekaclientpublic คลาส A1ServiceApplication {โมฆะคงที่สาธารณะหลัก (สตริง [] args) {springapplication.run (a1ServiceApplication.class, args); -ไปที่วิธีการเรียกใช้ใน SpringApplication ดังนี้:
/** * ตัวช่วยคงที่ที่สามารถใช้ในการเรียกใช้ {@link springapplication} จากแหล่งที่ระบุ * ที่ระบุโดยใช้การตั้งค่าเริ่มต้น * @Param Source แหล่งที่มาเพื่อโหลด * @param args อาร์กิวเมนต์แอปพลิเคชัน (โดยปกติจะส่งผ่านจากวิธีการหลัก Java) * @ @@Link @link applicationcontext} */ public configurableapplicationContext (แหล่งที่มาของสตริง ... args) {return run } /** * ตัวช่วยคงที่ที่สามารถใช้ในการเรียกใช้ {@link springapplication} จากแหล่งที่ระบุ * ที่ระบุโดยใช้การตั้งค่าเริ่มต้นและอาร์กิวเมนต์ที่ผู้ใช้ให้มา * @param แหล่งที่มาแหล่งที่จะโหลด * @param args อาร์กิวเมนต์แอปพลิเคชัน (โดยปกติจะส่งผ่านจากวิธีหลัก Java) * @ @ @@link @link applicationcontext} */ public configurableapplicationContext (แหล่งที่มา [] แหล่งที่มา, สตริง [] args) -นี่คือการสร้างอินสแตนซ์ของ SpringApplication เป็นกุญแจสำคัญเราไปที่ Constructor SpringApplication
/*** สร้างอินสแตนซ์ {@link springapplication} ใหม่ บริบทของแอปพลิเคชันจะโหลด * ถั่วจากแหล่งที่มา (ดู {@link SpringApplication ระดับคลาส} * เอกสารสำหรับรายละเอียดอินสแตนซ์สามารถปรับแต่งได้ก่อนโทร * {@link #run (สตริง ... )}. * @param แหล่งที่มาของแหล่งที่มา ... เริ่มต้น (แหล่งที่มา); ApplicationContextializer.class));deduceWebenvironment () ในวิธีการเริ่มต้นที่นี่กำหนดว่าปัจจุบันเริ่มต้นด้วยเว็บแอปพลิเคชันหรือขวดปกติดังนี้:
บูลีนส่วนตัว deduceWebenvironment () {สำหรับ (String className: Web_environment_classes) {ถ้า (! classutils.ispresent (classname, null)) {return false; }} ส่งคืนจริง; -web_environment_classes คือ:
สตริงสุดท้ายคงที่ส่วนตัว [] web_environment_classes = {"javax.servlet.servlet", "org.springframework.web.context.configurableWebapplicationContext"};ตราบใดที่ไม่มีของพวกเขาใด ๆ แอปพลิเคชันปัจจุบันจะเริ่มต้นในรูปแบบของขวดปกติ
จากนั้นเมธอด setInitializers จะเริ่มต้น applicationContextializers ทั้งหมด
/** * ตั้งค่า {@Link ApplicationContextInitializer} ที่จะนำไปใช้กับ Spring * {@Link ApplicationContext} * @Param Initializers ตัวเริ่มต้นที่จะตั้งค่า */ โมฆะสาธารณะ setInitializers (คอลเลกชัน <? ขยาย ApplicationContextInitializer <? >> initializers) {this.initializers = new ArrayList <ApplicationContextIalIsializer <? >> (); this.initializers.addall (Initializers); } setlisteners ((คอลเลกชัน) getSpringFactoriesinstances (ApplicationListener.class)) **ขั้นตอนนี้เริ่มต้นผู้ฟังทั้งหมด
กลับไปที่ SpringApplication ก่อนหน้า (แหล่งที่มา) .Run (args); และป้อนวิธีการเรียกใช้รหัสมีดังนี้:
/** * เรียกใช้แอปพลิเคชันสปริงการสร้างและรีเฟรชใหม่ * {@link ApplicationContext} * @param args อาร์กิวเมนต์แอปพลิเคชัน (โดยปกติจะผ่านจากวิธีการหลัก Java) * @return การทำงาน {@link applicationcontext} */ public configurableapplicationContext Run (สตริง ... args) {stopWatch stopWatch = new StopWatch (); stopwatch.start (); ConfiguRableApplicationContext Context = NULL; configureheadlessProperty (); SpringApplicationRunListeners Listeners = getRunListeners (args); ผู้ฟังเริ่มต้น (); ลอง {ApplicationArguments ApplicationArguments = ใหม่ defaultApplicationArguments (ARGS); บริบท = createandrefreshcontext (ผู้ฟัง, ApplicationArguments); AfterRefresh (บริบท, ApplicationArguments); ผู้ฟังเสร็จสิ้น (บริบท, null); stopwatch.stop (); if (this.logstartupInfo) {ใหม่ startupInfologger (this.mainapplicationClass) .logstarted (getApplicationLog (), stopwatch); } คืนบริบท; } catch (throwable ex) {handlerunfailure (บริบท, ผู้ฟัง, อดีต); โยน unlueLstateException ใหม่ (EX); -ขั้นตอนนี้ดำเนินการสร้างบริบท createandrefreshcontext (ผู้ฟัง, ApplicationArguments)
private configurablepaplicationcontext createandrefreshcontext (SpringApplicationRunListeners ผู้ฟัง, ApplicationArguments ApplicationArguments) {ConfiguRableApplicationContext บริบท; // สร้างและกำหนดค่าสภาพแวดล้อมที่กำหนดค่าสภาพแวดล้อมสภาพแวดล้อม = getorCreateEnVironment (); กำหนดค่าสภาพแวดล้อม (สภาพแวดล้อม, ApplicationArguments.getSourceArgs ()); Listeners.environmentPrepared (สภาพแวดล้อม); if (iswebenvironment (สภาพแวดล้อม) &&! this.webenvironment) {environment = convertTostandardenvironment (สภาพแวดล้อม); } if (this.bannermode! = banner.mode.off) {printbanner (สภาพแวดล้อม); } // สร้าง, โหลด, รีเฟรชและเรียกใช้ applicationContext context = createApplicationContext (); Context.setEnvironment (สภาพแวดล้อม); PostprocessApplicationContext (บริบท); ApplyInitializers (บริบท); Listeners.ContextPrepared (บริบท); if (this.logStartupInfo) {logStartUpInfo (context.get.getParent () == null); LogStartupprofileInfo (บริบท); } // เพิ่ม boot singleton beans context.getBeanFactory (). RegisterSingleton ("SpringApplicationArguments", ApplicationArguments); // โหลดแหล่งที่มาที่ตั้งค่า <jobch> แหล่งที่มา = getSources (); assert.NotEmpty (แหล่งที่มา "แหล่งที่มาจะต้องไม่ว่าง"); โหลด (บริบท, แหล่งที่มา (วัตถุใหม่ [sources.size ()])); Listeners.ContextLoaded (บริบท); // รีเฟรชบริบทรีเฟรช (บริบท); if (this.registershutdownhook) {ลอง {context.registershutdownhook (); } catch (accessControlexception ex) {// ไม่ได้รับอนุญาตในบางสภาพแวดล้อม }} กลับบริบท; } // สร้างและกำหนดค่าสภาพแวดล้อมที่กำหนดค่าสภาพแวดล้อมสภาพแวดล้อม = getorCreateEnVironment (); กำหนดค่าสภาพแวดล้อม (สภาพแวดล้อม, ApplicationArguments.getSourceArgs ());ขั้นตอนนี้ดำเนินการกำหนดค่าและการโหลดสภาพแวดล้อม
if (this.bannermode! = banner.mode.off) {printbanner (สภาพแวดล้อม); -ขั้นตอนนี้พิมพ์โลโก้สปริงบูต หากคุณต้องการเปลี่ยนให้เพิ่ม banner.txt และ banner.txt ไปยังไฟล์ทรัพยากรเพื่อเปลี่ยนเป็นรูปแบบที่คุณต้องการ
// สร้าง, โหลด, รีเฟรชและเรียกใช้ applicationContext context = createApplicationContext (); return (configurableapplicationContext) beanutils.instantiate (contextclass)
บริบทการสร้างนั้นรวมถึงสิ่งที่คอนเทนเนอร์ถูกสร้างขึ้นและสร้างคลาสการตอบกลับรวมถึงการสร้าง EmbeddedServletContainerFactory ไม่ว่าจะเลือกท่าเทียบเรือหรือ Tomcat มีเนื้อหามากมายดังนั้นฉันจะพูดถึงมันในครั้งต่อไป
if (this.registershutdownhook) {ลอง {context.registershutdownhook (); } catch (accessControlexception ex) {// ไม่ได้รับอนุญาตในบางสภาพแวดล้อม -ขั้นตอนนี้คือการลงทะเบียนบริบทปัจจุบันและทำลายคอนเทนเนอร์เมื่อได้รับคำสั่งฆ่า
โดยพื้นฐานแล้วการวิเคราะห์เริ่มต้นจบลง แต่ยังมีรายละเอียดบางอย่างที่ต้องใช้เวลานานมากที่จะบอก สิ่งนี้จะถูกกล่าวถึงในโพสต์บล็อกที่ตามมาและนั่นคือทั้งหมดสำหรับวันนี้
โดยสรุปกระบวนการเริ่มต้นของ Spring Boot Jar นั้นเป็นขั้นตอนต่อไปนี้:
1. เมื่อเราแพ็คเกจ Maven ตามปกติปลั๊กอินสปริงจะขยายวงจรชีวิต Maven และมอบแพ็คเกจที่เกี่ยวข้องกับการบูตสปริงลงในขวด JAR นี้มีไฟล์คลาสที่เกี่ยวข้องกับโปรแกรมการเริ่มต้นการบูต Spring นอกเหนือจากขวดที่ผลิตโดยแอปพลิเคชัน
2. ฉันเคยเห็นกระบวนการเริ่มต้นของ Spring Boot Jar รุ่นที่ต่ำกว่าเล็กน้อยมาก่อน ในเวลานั้นฉันจำได้ว่าเธรดปัจจุบันมีเธรดใหม่ที่จะเรียกใช้โปรแกรมหลักและตอนนี้มันถูกเปลี่ยนเป็นการใช้งานสะท้อนโดยตรงเพื่อเริ่มโปรแกรมหลัก
สรุป
ข้างต้นคือการวิเคราะห์หลักการของ Jar Boot Spring ที่แนะนำโดยบรรณาธิการ ฉันหวังว่ามันจะเป็นประโยชน์กับคุณ หากคุณมีคำถามใด ๆ โปรดฝากข้อความถึงฉันและบรรณาธิการจะตอบกลับคุณทันเวลา ขอบคุณมากสำหรับการสนับสนุนเว็บไซต์ Wulin.com!