คำนำ
บทความนี้ส่วนใหญ่แนะนำเนื้อหาที่เกี่ยวข้องเกี่ยวกับกระบวนการเริ่มต้นและหลักการของ Servlet ใน Spring Boot ฉันจะไม่พูดด้านล่างมากนักลองมาดูการแนะนำรายละเอียดด้วยกัน
กระบวนการเริ่มต้นและหลักการ:
1 Spring Boot Application Startup Method
stopwatch stopwatch = new Stopwatch (); stopwatch.start (); ConfiguRableApplicationContext Context = NULL; เครื่องวิเคราะห์ Failureanalyzers = NULL; configureheadlessProperty (); SpringApplicationRunListeners Listeners = getRunListeners (args); ผู้ฟังเริ่มต้น (); ลอง {ApplicationArguments ApplicationArguments = ใหม่ defaultApplicationArguments (ARGS); สภาพแวดล้อมที่กำหนดค่าได้ แบนเนอร์ printedbanner = printbanner (สิ่งแวดล้อม); // สร้าง applicationContext container context = createApplicationContext (); เครื่องวิเคราะห์ = ใหม่ failureanalyzers (บริบท); Preparecontext (บริบท, สิ่งแวดล้อม, ผู้ฟัง, ApplicationArguments, PrintedBanner); // รีเฟรช IOC คอนเทนเนอร์รีเฟรช (บริบท); AfterRefresh (บริบท, ApplicationArguments); ผู้ฟังเสร็จสิ้น (บริบท, null); stopwatch.stop (); if (this.logstartupInfo) {ใหม่ startupInfologger (this.mainapplicationClass) .logstarted (getApplicationLog (), stopwatch); } คืนบริบท; } catch (throwable ex) {handlerunfailure (บริบท, ผู้ฟัง, เครื่องวิเคราะห์, Ex); โยน unlueLstateException ใหม่ (EX); -2 CreateApplicationContext (): สร้างคอนเทนเนอร์ IOC หากเป็นเว็บแอปพลิเคชันให้สร้างคอนเทนเนอร์ IOC ของ AnnotationConfigembedDedWebapplication หากไม่ได้สร้างคอนเทนเนอร์ IOC ของคำอธิบายประกอบ
สตริงสุดท้ายคงที่สาธารณะ default_context_class = "org.springframework.context" + "Annotation.AnnotationConfigapplicationContext"; /** * ชื่อคลาสของบริบทแอปพลิเคชันที่จะใช้โดยค่าเริ่มต้นสำหรับสภาพแวดล้อมเว็บ * */ สตริงสุดท้ายคงที่สาธารณะ default_web_context_class = "org.springframework" + "boot.context.embedded.annotationConfigembedDedWebapplicationContext"; ProfessuraDePplicationContext createApplicationContext () {class <?> contextclass = this.applicationContextClass; if (contextclass == null) {ลอง {// สร้างคอนเทนเนอร์ IOC ที่แตกต่างกันตามสภาพแวดล้อมแอปพลิเคชัน contextclass = class.forName (this.webenvironment? default_web_context_class: default_context_class); } catch (classnotfoundexception ex) {โยนใหม่ unlilmalstateException ("ไม่สามารถสร้าง ApplicationContext เริ่มต้น" + "โปรดระบุ ApplicationContextClass", Ex); }} return (configurableapplicationContext) beanutils.instantiate (contextclass); -3 RefreshContext (บริบท) สปริงบูตรีเฟรชคอนเทนเนอร์ IOC (สร้างวัตถุคอนเทนเนอร์เริ่มต้นคอนเทนเนอร์และสร้างแต่ละส่วนประกอบของคอนเทนเนอร์)
Private Void RefreshContext (ConfiguRableApplicationContext บริบท) {Refresh (บริบท); if (this.registershutdownhook) {ลอง {context.registershutdownhook (); } catch (accessControlexception ex) {// ไม่ได้รับอนุญาตในบางสภาพแวดล้อม -4 รีเฟรช (บริบท); รีเฟรชคอนเทนเนอร์ IOC ที่คุณเพิ่งสร้างขึ้น
ได้รับการป้องกันการรีเฟรชเป็นโมฆะ (ApplicationContext ApplicationContext) {assert.isinstanceof (AbstractaplicationContext.class, ApplicationContext); ((AbstractApplicationContext) ApplicationContext) .REFRESH (); -5. เรียกเมธอดการรีเฟรชคลาสแม่ ()
โมฆะสาธารณะ Refresh () พ่น beansexception, unglemalstateException {object var1 = this.startupshutdownmonitor; ซิงโครไนซ์ (this.startupshutdownmonitor) {this.preparerefresh (); configurableListableBeanFactory beanfactory = this.obtainfreshbeanfactory (); this.preparebeanfactory (beanfactory); ลอง {this.postprocessbeanfactory (beanfactory); this.invokebeanfactorypostprocessors (beanfactory); this.registerBeanPostProcessors (beanfactory); this.initmessagesource (); this.initaplicationEventMulticaster (); this.onrefresh (); this.registerListeners (); สิ่งนี้ FinishBeanFactoryInitialization (BeanFactory); this.finishrefresh (); } catch (beansexception var9) {ถ้า (this.logger.iswarnenabled ()) {this.logger.warn ("ข้อยกเว้นที่พบในระหว่างการเริ่มต้นบริบท - การยกเลิกการรีเฟรชการรีเฟรช:" + var9); } this.destroybeans (); this.cancelrefresh (var9); โยน var9; } ในที่สุด {this.resetCommonCaches (); -6 วิธี onrefresh ของ subclass embeddedwebapplicationContext ของคลาส Parent Parent AbstractapplicationContext
@Override ป้องกันโมฆะ onRefresh () {super.onrefresh (); ลอง {createembeddedServletContainer (); } catch (throwable ex) {โยน applicationcontextexception ใหม่ ("ไม่สามารถเริ่มคอนเทนเนอร์ฝังตัว", ex); -7 ใน createembeddedservletContainer โรงงานคอนเทนเนอร์ servlet ที่ฝังตัวจะได้รับและ servlet ถูกสร้างขึ้นจากโรงงานคอนเทนเนอร์
โมฆะส่วนตัว createembeddedservletContainer () {EmbeddedServletContainer localContainer = this.embeddedServletContainer; servletContext localservletContext = getServletContext (); if (localContainer == null && localservletContext == null) {// รับ servlet container enmbeddedservletContainerFactory containerFactory = getembeddedServletContainerFactory (); // รับคอนเทนเนอร์ Servlet ที่มีความสอดคล้องกันตามโรงงานคอนเทนเนอร์ this.MubdedServletContainer = containerFactory .getEmbeddedServletContainer (getSelfInitializer ()); } อื่นถ้า (localservletContext! = null) {ลอง {getSelfInitializer (). onStartup (localservletContext); } catch (servletexception ex) {โยน applicationcontextexception ใหม่ ("ไม่สามารถเริ่มต้นบริบท servlet", ex); }} InitPropertySources (); -8 รับ Servlet Container Factory จาก IOC Container
// EmbeddedWebapplicationContext#getembeddedservletContainerFactory EmbeddedServletContainerFactory getembeddedservletContainerFactory () {// ใช้ชื่อถั่วเพื่อที่เราจะไม่พิจารณาสตริงลำดับชั้น if (beannames.length == 0) {โยน applicationcontextexception ใหม่ ("ไม่สามารถเริ่ม EmbeddedWebapplicationContext เนื่องจากหายไป" + "EmbeddedServletContainerFactory Bean"); } if (beannames.length> 1) {โยน applicationcontextexception ใหม่ ("ไม่สามารถเริ่ม EmbeddedWebapplicationContext ได้เนื่องจากถั่ว" + "embeddedServletContainerFactory หลายตัว:" + stringutils.arraytocommadeLimitedString (Beannames); } return getBeanFactory (). getBean (Beannames [0], EmbeddedServletContainerFactory.class); -9 ใช้โรงงานคอนเทนเนอร์ Servlet เพื่อรับคอนเทนเนอร์ servlet แบบฝัง โรงงานคอนเทนเนอร์ที่ใช้ขึ้นอยู่กับการพึ่งพาสภาพแวดล้อมการกำหนดค่า
this.mebedservletContainer = containerFactory .getembeddedservletContainer (getSelfInitializer ());
10 กระบวนการสร้างข้างต้นก่อนเริ่มคอนเทนเนอร์ IOC ก่อนจากนั้นเริ่มคอนเทนเนอร์เซิร์ฟเล็ตแบบฝังแล้วดึงวัตถุที่เหลือซึ่งไม่ได้สร้างในคอนเทนเนอร์ IOC เช่นคอนโทรลเลอร์ที่คุณสร้างขึ้นด้วยตัวเอง
// อินสแตนซ์ Singletons ที่เหลืออยู่ทั้งหมด FinishBeanFactoryInitialization (beanfactory);
Void Void FinishBeanFactoryInitialization (ConfigurableListableBeanFactory BeanFactory) {// เริ่มต้นบริการแปลงสำหรับบริบทนี้ if (beanfactory.containsbean (conversion_service_bean_name) && beanfactory.istypematch (Conversion_service_bean_name, Conversionservice.class)) {beanfactory.SetConversionservice (beanfactory.getBean } // ลงทะเบียนตัวแก้ไขค่าฝังตัวเริ่มต้นหากไม่มีถั่วโพสต์โปรเซสเซอร์ // (เช่นถั่วโฮลเดอร์คอนเดอร์ของถั่ว) ลงทะเบียนก่อนหน้านี้: // ณ จุดนี้ส่วนใหญ่สำหรับการแก้ปัญหาในค่าแอตทริบิวต์คำอธิบายประกอบ if (! beanfactory.hasembeddedDvalueresolver ()) {beanfactory.addembeddedvalueresolver (ใหม่ StringValueresolver () {@Override สตริงสาธารณะ redentrentvalue (String strval) } // เริ่มต้นถั่ว LoadTimeWeaveraware ก่อนที่จะอนุญาตให้ลงทะเบียนหม้อแปลงก่อน String [] weaverawarenames = beanfactory.getBeanNamesFortype (loadtimeweaveraware.class, false, false); สำหรับ (String Weaverawarename: Weaverawarenames) {getBean (weaverawarename); } // หยุดใช้ classloader ชั่วคราวสำหรับการจับคู่ประเภท beanfactory.settempclassloader (null); // อนุญาตให้แคชข้อมูลเมตาคำจำกัดความของถั่วทั้งหมดไม่คาดหวังการเปลี่ยนแปลงเพิ่มเติม beanfactory.freezeconfiguration (); // อินสแตนซ์ Singletons ที่เหลืออยู่ทั้งหมด beanfactory.preinstantiatesingletons (); -ตรวจสอบวิธีการ preinstantiatesingletons
โมฆะสาธารณะ preinstantiatesingletons () พ่น beansexception {ถ้า (this.logger.isdebugenabled ()) {this.logger.debug ("singletons pre-instantiating ใน" + นี้); } รายการ <String> beanNames = new ArrayList (this.beanDefinitionNames); ตัววนซ้ำ var2 = beannames.iterator (); ในขณะที่ (จริง) {ในขณะที่ (จริง) {string beanname; RootBeanDefinition BD; ทำ {do {do {if (! var2.hasnext ()) {var2 = beannames.iterator (); ในขณะที่ (var2.hasnext ()) {beanname = (สตริง) var2.next (); Object SingletonInstance = this.getSingleton (Beanname); if (singletoninstance อินสแตนซ์ของ smartinitializingletleton) {ขั้นสุดท้าย smartinitializingsingleton smartsingleton = (smartinitializingletleton) SingletonInstance; if (system.getSecurityManager ()! = null) {accessController.doprivileged (ใหม่ PrivilegedAction <jobch> () {วัตถุสาธารณะรัน () {smartsingleton.aftersingletonsinstantiated (); return null;}} } else {smartsingleton.aftersingletonsinstantiated (); } } กลับ; } beanName = (สตริง) var2.next (); bd = this.getMergedLocalBeanDefinition (BeanName); } ในขณะที่ (bd.isabstract ()); } ในขณะที่ (! bd.issingleton ()); } ในขณะที่ (bd.islazyinit ()); if (this.isfactoryBean (Beanname)) {Final FactoryBean <?> Factory = (FactoryBean) this.getBean ("&" + Beanname); บูลีน iseagerinit; if (system.getSecurityManager ()! = null && อินสแตนซ์ของโรงงานของ SmartFactoryBean) {iseagerinit = ((บูลีน) AccessController.doprivileged (PrivilegedAction ใหม่ <boolean> () this.getAccessControlContext ())). booleanValue (); } else {iseagerinit = อินสแตนซ์จากโรงงานของ SmartFactoryBean && ((SmartFactoryBean) โรงงาน) .iseagerinit (); } if (iseagerinit) {this.getBean (Beanname); }} else {// ลงทะเบียน bean this.getBean (Beanname); -ใช้วิธีการของ GetBean เพื่อสร้างอินสแตนซ์ที่ไม่ได้สร้างทั้งหมดผ่านการสะท้อนกลับ
การใช้คอนเทนเนอร์ servlet แบบฝัง:
ข้อดี: ง่ายพกพา
ข้อเสีย: JSP ไม่สนับสนุนโดยค่าเริ่มต้นการเพิ่มประสิทธิภาพและการปรับแต่งมีความซับซ้อนมากขึ้น
ขั้นตอนในการใช้คอนเทนเนอร์ servlet ภายนอก:
1. ต้องสร้างโครงการสงครามและจำเป็นต้องมีโครงสร้างไดเรกทอรีของโครงการเว็บ Jianhao
ขอบเขตการพึ่งพา Tomcat แบบฝังตัว 2 ตัวระบุให้
3 เขียนคลาส subclass คลาส springbootservletInitializer และแทนที่วิธีการกำหนดค่า
ระดับสาธารณะ servletInitializer ขยาย SpringbootservletInitializer {@Override Protected SpringApplicationBuilder Configure (แอปพลิเคชัน SpringApplicationBuilder) {return application.Sources (SpringBoot04WebjSpapplication.Class); -4 เริ่มเซิร์ฟเวอร์
ความแตกต่างระหว่างแพ็คเกจเริ่มต้นและแพ็คเกจสงคราม
แพ็คเกจ JAR: ดำเนินการวิธีการเรียกใช้ของ SpringBootapplic
แพ็คเกจสงคราม: ก่อนอื่นเริ่มเซิร์ฟเวอร์เซิร์ฟเวอร์เซิร์ฟเวอร์เริ่มแอปพลิเคชัน Springboot (SpringbootservletInitizer) จากนั้นเริ่มคอนเทนเนอร์ IOC
กฎ Servlet 3.0+
1. การเริ่มต้นเซิร์ฟเวอร์ (การเริ่มต้นแอปพลิเคชันเว็บ) อินสแตนซ์ servletContainerLnitializer ในแพ็คเกจ JAR ทั้งหมดในเว็บแอปพลิเคชันปัจจุบันจะถูกสร้างขึ้น
2 การใช้งานของ ServletContainerInitializ
3 คุณยังสามารถใช้คำอธิบายประกอบ @HandLestypes เพื่อโหลดคลาสที่ระบุเมื่อแอปพลิเคชันเริ่มต้นขึ้น
กระบวนการและหลักการของ Tomcat ภายนอก
①เริ่ม Tomcat
②ตามกฎ servlet3.0+ ที่อธิบายไว้ข้างต้นคุณสามารถค้นหาไฟล์ชื่อ Javax.servlet.servletContainerInitializer ในสปริงเว็บโมดูลและเนื้อหาของไฟล์คือ org.springframework.web.springservletContainerInitializer
③ดูที่คำจำกัดความของ SpringservletContainerInitializer
@HandLestypes (WebApplicationInitializer.class) คลาสสาธารณะ SpringservletContainerInitializer ใช้ servletContainerInitializer { /** * มอบหมาย {@code servletContext} ให้กับ {@link webapplicationInitializer} * <p> เนื่องจากคลาสนี้ประกาศ@{@code handlestypes (webapplicationInitializer.class)}, * servlet 3.0+ คอนเทนเนอร์จะสแกน classpath โดยอัตโนมัติสำหรับการใช้งาน * ของ {@code webapplicationinitializer} ของสปริง * <p> หากไม่พบการใช้งาน {@code webapplicationInitializer} ใน classpath * วิธีนี้เป็นไปอย่างไม่มีประสิทธิภาพ ข้อความบันทึกระดับข้อมูลจะได้รับการแจ้งเตือน * ผู้ใช้ว่า {@code servletContainerInitializer} ได้รับการเรียกใช้แน่นอน แต่พบว่ามีการใช้งาน * @code webapplicationInitializer} * <p> สมมติว่ามีการตรวจพบประเภท {@code webapplicationInitializer} หนึ่งรายการ * พวกเขาจะถูกสร้างอินสแตนซ์ (และ <em> เรียงลำดับ </em> หาก@@@@link * org.springframework.core.annotation.order@order นำไปใช้งานแล้ว {@link webapplicationInitializer#onstartup (servletContext)} * จะถูกเรียกใช้ในแต่ละอินสแตนซ์โดยมอบหมายให้ {@code servletContex ส่วนประกอบเช่นตัวกรอง onstartup (set <class <? >> webappinitializerclasses, servletcontext servletcontext) พ่น servletexception {list <webapplicationInitializer> initializers = new LinkedList <WebApplicationInitializer> () ปกป้อง: คอนเทนเนอร์ servlet บางตัวให้คลาสที่ไม่ถูกต้องแก่เรา // ไม่ว่า @handlestypes จะพูดอะไร ... ถ้า (! waiclass.isinterface () &&! modifier.isabstract (waiclass.getModifiers () && /& collection itializers.add ((webapplicationInitializer) waiclass.newinstance ()); classpath "); return;} servletContext.log (initializers.size () +" Spring WebapplicationInitializers ที่ตรวจพบบน classPath "); AnnotationAwareOrderComparator.sort (Initializers); // การเรียกใช้วิธีการเริ่มต้น -ในความคิดเห็นที่ยาวนานด้านบนคุณจะเห็นว่า SpringservletContainerInitializer ผ่านคลาสทั้งหมดของประเภท WebApplicationInitializer ที่มีคำอธิบายประกอบโดย @HandLestypes (WebApplicationInitializer.class) ลงในพารามิเตอร์ที่ตั้งไว้ของวิธีการเริ่มต้น
④วิธีการในตอนท้ายการใช้งาน webapplicationInitilizer แต่ละครั้งเรียกใช้วิธีการเริ่มต้นของตัวเอง
⑤ WebApplicationInitializer มีคลาสการใช้งานนามธรรม SpringbootservletInitializer (โปรดจำไว้ว่าเราได้รับมรดกระดับนามธรรมนี้) และวิธีการเริ่มต้นของแต่ละอินสแตนซ์ WebApplicationInitializer (รวมถึง SpringBootServleTinitializer) จะถูกเรียก
บทคัดย่อระดับสาธารณะคลาส SpringbootservletInitializer ใช้ WebApplicationInitializer {// รหัสอื่น ๆ ... @Override โมฆะสาธารณะ onStartup (servletContext servletContext) โยน servletexception {// การเริ่มต้น logger ถูกเลื่อนออกไป // สร้าง IOC container webApplicationContext rootAppContext = createrootapplicationContext (servletContext); if (rootAppContext! = null) {servletContext.addListener (New ContextloaderListener (RootAppContext) {@Override Public Void Contextialized (Event ServletContextEvent) {// no-op เพราะบริบทแอปพลิเคชันเริ่มต้นแล้ว}}); } else {this.logger.debug ("ไม่มี contextloaderListener ลงทะเบียนเช่น" + "createrootapplicationContext () ไม่ได้" + "คืนบริบทแอปพลิเคชัน"); }} การป้องกัน WebApplicationContext CreaterootapplicationContext (servletContext servletContext) {// สร้างตัวสร้างแอปพลิเคชันสปริงและตั้งค่าคุณสมบัติที่เกี่ยวข้อง SpringApplicationBuilder Builder = CreateSpringApplicationBuilder (); StandardservletEnvironment Environment = มาตรฐานใหม่ ServletEnvironment (); environment.initPropertySources (servletContext, null); builder.environment (สิ่งแวดล้อม); builder.main (getclass ()); ApplicationContext parent = getExistingRootWebapplicationContext (servletContext); if (parent! = null) {this.logger.info ("บริบทรูทสร้างแล้ว (ใช้เป็นหลัก)"); servletContext.setAttribute (webApplicationContext.root_web_application_context_attribute, null); builder.initializers (ใหม่ ParentContextApplicationContextInitializer (parent)); } builder.initializers (ใหม่ servletContextApplicationContextInitializer (servletContext)); builder.contextclass (AnnotationConfigembeddedWebapplicationContext.class); // หลังจากเรียกใช้วิธีการกำหนดค่าและสร้างโครงการเว็บประเภทสงครามเนื่องจากคลาสย่อยของ SpringbootservleTinitializer แทนที่วิธีการกำหนดค่าวิธีการกำหนดค่าที่เรากำหนดแทนที่เรียกว่าที่นี่ builder = configure (builder); // แอปพลิเคชันสปริงถูกสร้างขึ้นผ่านแอปพลิเคชัน SpringApplication Builder = builder.build (); if (application.getSources (). isEmpty () && AnnotationUtils .findannotation (getClass (), configuration.class)! = null) {application.getSources (). เพิ่ม (getClass ()); } assert.state (! application.getSources (). isempty (), "ไม่มีการกำหนดแหล่งที่มาของ SpringApplication // ตรวจสอบให้แน่ใจว่าหน้าข้อผิดพลาดได้รับการลงทะเบียนถ้า (this.registererRorPageFilter) {application.getSources (). เพิ่ม (ErrorPageFilterConfiguration.class); } // เริ่มต้นใช้งานแอปพลิเคชันสปริง Run Run (แอปพลิเคชัน); } // แอปพลิเคชันสปริงเริ่มต้นสร้างและส่งคืน IOC คอนเทนเนอร์ที่ได้รับการป้องกัน WebApplicationContext Run (แอปพลิเคชัน SpringApplication) {return (WebApplicationContext) แอปพลิเคชัน Run (); -เมื่ออินสแตนซ์ SpringBootServleTinitializer ดำเนินการวิธีการเริ่มต้นวิธีการเรียกใช้จะถูกดำเนินการผ่านวิธี CreaterootapplicationContext กระบวนการต่อไปนั้นเหมือนกับกระบวนการรันของแอปพลิเคชันที่เริ่มต้นในรูปแบบของแพ็คเกจ JAR คอนเทนเนอร์ IOC จะถูกสร้างขึ้นภายในและส่งคืน อย่างไรก็ตามแอปพลิเคชันในรูปแบบของแพ็คเกจสงครามจะไม่สร้างคอนเทนเนอร์ servlet อีกต่อไปในระหว่างกระบวนการสร้างคอนเทนเนอร์ IOC
สรุป
ข้างต้นเป็นเนื้อหาทั้งหมดของบทความนี้ ฉันหวังว่าเนื้อหาของบทความนี้จะมีค่าอ้างอิงบางอย่างสำหรับการศึกษาหรือที่ทำงานของทุกคน หากคุณมีคำถามใด ๆ คุณสามารถฝากข้อความไว้เพื่อสื่อสาร ขอบคุณสำหรับการสนับสนุน Wulin.com