前言
本文主要介紹了關于spring boot中servlet啟動過程與原理的相關內容,下面話不多說了,來一起看看詳細的介紹吧
啟動過程與原理:
1 spring boot 應用啟動運行run方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
|
stopwatch stopwatch = new stopwatch(); stopwatch.start(); configurableapplicationcontext context = null ; failureanalyzers analyzers = null ; configureheadlessproperty(); springapplicationrunlisteners listeners = getrunlisteners(args); listeners.starting(); try { applicationarguments applicationarguments = new defaultapplicationarguments( args); configurableenvironment environment = prepareenvironment(listeners, applicationarguments); banner printedbanner = printbanner(environment); //創建一個applicationcontext容器 context = createapplicationcontext(); analyzers = new failureanalyzers(context); preparecontext(context, environment, listeners, applicationarguments, printedbanner); //刷新ioc容器 refreshcontext(context); afterrefresh(context, applicationarguments); listeners.finished(context, null ); stopwatch.stop(); if ( this .logstartupinfo) { new startupinfologger( this .mainapplicationclass) .logstarted(getapplicationlog(), stopwatch); } return context; } catch (throwable ex) { handlerunfailure(context, listeners, analyzers, ex); throw new illegalstateexception(ex); } |
2 createapplicationcontext():創建ioc容器,如果是web應用則創建annotationconfigembeddedwebapplacation的ioc容器,如果不是,則創建annotationconfigapplication的ioc容器
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
|
public static final string default_context_class = "org.springframework.context." + "annotation.annotationconfigapplicationcontext" ; /** * the class name of application context that will be used by default for web * environments. */ public static final string default_web_context_class = "org.springframework." + "boot.context.embedded.annotationconfigembeddedwebapplicationcontext" ; protected configurableapplicationcontext createapplicationcontext() { class <?> contextclass = this .applicationcontextclass; if (contextclass == null ) { try { //根據應用環境,創建不同的ioc容器 contextclass = class .forname( this .webenvironment ? default_web_context_class : default_context_class); } catch (classnotfoundexception ex) { throw new illegalstateexception( "unable create a default applicationcontext, " + "please specify an applicationcontextclass" , ex); } } return (configurableapplicationcontext) beanutils.instantiate(contextclass); } |
3 refreshcontext(context) spring boot刷新ioc容器(創建容器對象,并初始化容器,創建容器每一個組件)
1
2
3
4
5
6
7
8
9
10
11
|
private void refreshcontext(configurableapplicationcontext context) { refresh(context); if ( this .registershutdownhook) { try { context.registershutdownhook(); } catch (accesscontrolexception ex) { // not allowed in some environments. } } } |
4 refresh(context);刷新剛才創建的ioc容器
1
2
3
4
|
protected void refresh(applicationcontext applicationcontext) { assert .isinstanceof(abstractapplicationcontext. class , applicationcontext); ((abstractapplicationcontext) applicationcontext).refresh(); } |
5 調用父類的refresh()的方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
|
public void refresh() throws beansexception, illegalstateexception { object var1 = this .startupshutdownmonitor; synchronized ( this .startupshutdownmonitor) { this .preparerefresh(); configurablelistablebeanfactory beanfactory = this .obtainfreshbeanfactory(); this .preparebeanfactory(beanfactory); try { this .postprocessbeanfactory(beanfactory); this .invokebeanfactorypostprocessors(beanfactory); this .registerbeanpostprocessors(beanfactory); this .initmessagesource(); this .initapplicationeventmulticaster(); this .onrefresh(); this .registerlisteners(); this .finishbeanfactoryinitialization(beanfactory); this .finishrefresh(); } catch (beansexception var9) { if ( this .logger.iswarnenabled()) { this .logger.warn( "exception encountered during context initialization - cancelling refresh attempt: " + var9); } this .destroybeans(); this .cancelrefresh(var9); throw var9; } finally { this .resetcommoncaches(); } } } |
6 抽象父類abstractapplicationcontext類的子類embeddedwebapplicationcontext的onrefresh方法
1
2
3
4
5
6
7
8
9
10
11
|
@override protected void onrefresh() { super .onrefresh(); try { createembeddedservletcontainer(); } catch (throwable ex) { throw new applicationcontextexception( "unable to start embedded container" , ex); } } |
7 在createembeddedservletcontainer放啊發中會獲取嵌入式servlet容器工廠,由容器工廠創建servlet
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
|
private void createembeddedservletcontainer() { embeddedservletcontainer localcontainer = this .embeddedservletcontainer; servletcontext localservletcontext = getservletcontext(); if (localcontainer == null && localservletcontext == null ) { //獲取嵌入式servlet容器工廠 embeddedservletcontainerfactory containerfactory = getembeddedservletcontainerfactory(); //根據容器工廠獲取對應嵌入式servlet容器 this .embeddedservletcontainer = containerfactory .getembeddedservletcontainer(getselfinitializer()); } else if (localservletcontext != null ) { try { getselfinitializer().onstartup(localservletcontext); } catch (servletexception ex) { throw new applicationcontextexception( "cannot initialize servlet context" , ex); } } initpropertysources(); } |
8 從ioc容器中獲取servlet容器工廠
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
|
//embeddedwebapplicationcontext#getembeddedservletcontainerfactory protected embeddedservletcontainerfactory getembeddedservletcontainerfactory() { // use bean names so that we don't consider the hierarchy string[] beannames = getbeanfactory() .getbeannamesfortype(embeddedservletcontainerfactory. class ); if (beannames.length == 0 ) { throw new applicationcontextexception( "unable to start embeddedwebapplicationcontext due to missing " + "embeddedservletcontainerfactory bean." ); } if (beannames.length > 1 ) { throw new applicationcontextexception( "unable to start embeddedwebapplicationcontext due to multiple " + "embeddedservletcontainerfactory beans : " + stringutils.arraytocommadelimitedstring(beannames)); } return getbeanfactory().getbean(beannames[ 0 ], embeddedservletcontainerfactory. class ); } |
9 使用servlet容器工廠獲取嵌入式servlet容器,具體使用哪一個容器工廠看配置環境依賴
1
2
|
this .embeddedservletcontainer = containerfactory .getembeddedservletcontainer(getselfinitializer()); |
10 上述創建過程 首先啟動ioc容器,接著啟動嵌入式servlet容器,接著將ioc容器中剩下沒有創建的對象獲取出來,比如自己創建的controller
1
2
|
// instantiate all remaining (non-lazy-init) singletons. finishbeanfactoryinitialization(beanfactory); |
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
|
protected void finishbeanfactoryinitialization(configurablelistablebeanfactory beanfactory) { // initialize conversion service for this context. if (beanfactory.containsbean(conversion_service_bean_name) && beanfactory.istypematch(conversion_service_bean_name, conversionservice. class )) { beanfactory.setconversionservice( beanfactory.getbean(conversion_service_bean_name, conversionservice. class )); } // register a default embedded value resolver if no bean post-processor // (such as a propertyplaceholderconfigurer bean) registered any before: // at this point, primarily for resolution in annotation attribute values. if (!beanfactory.hasembeddedvalueresolver()) { beanfactory.addembeddedvalueresolver( new stringvalueresolver() { @override public string resolvestringvalue(string strval) { return getenvironment().resolveplaceholders(strval); } }); } // initialize loadtimeweaveraware beans early to allow for registering their transformers early. string[] weaverawarenames = beanfactory.getbeannamesfortype(loadtimeweaveraware. class , false , false ); for (string weaverawarename : weaverawarenames) { getbean(weaverawarename); } // stop using the temporary classloader for type matching. beanfactory.settempclassloader( null ); // allow for caching all bean definition metadata, not expecting further changes. beanfactory.freezeconfiguration(); // instantiate all remaining (non-lazy-init) singletons. beanfactory.preinstantiatesingletons(); } |
看看 preinstantiatesingletons方法
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
|
public void preinstantiatesingletons() throws beansexception { if ( this .logger.isdebugenabled()) { this .logger.debug( "pre-instantiating singletons in " + this ); } list<string> beannames = new arraylist( this .beandefinitionnames); iterator var2 = beannames.iterator(); while ( true ) { while ( true ) { string beanname; rootbeandefinition bd; do { do { do { if (!var2.hasnext()) { var2 = beannames.iterator(); while (var2.hasnext()) { beanname = (string)var2.next(); object singletoninstance = this .getsingleton(beanname); if (singletoninstance instanceof smartinitializingsingleton) { final smartinitializingsingleton smartsingleton = (smartinitializingsingleton)singletoninstance; if (system.getsecuritymanager() != null ) { accesscontroller.doprivileged( new privilegedaction<object>() { public object run() { smartsingleton.aftersingletonsinstantiated(); return null ; } }, this .getaccesscontrolcontext()); } else { smartsingleton.aftersingletonsinstantiated(); } } } return ; } beanname = (string)var2.next(); bd = this .getmergedlocalbeandefinition(beanname); } while (bd.isabstract()); } while (!bd.issingleton()); } while (bd.islazyinit()); if ( this .isfactorybean(beanname)) { final factorybean<?> factory = (factorybean) this .getbean( "&" + beanname); boolean iseagerinit; if (system.getsecuritymanager() != null && factory instanceof smartfactorybean) { iseagerinit = (( boolean )accesscontroller.doprivileged( new privilegedaction< boolean >() { public boolean run() { return ((smartfactorybean)factory).iseagerinit(); } }, this .getaccesscontrolcontext())).booleanvalue(); } else { iseagerinit = factory instanceof smartfactorybean && ((smartfactorybean)factory).iseagerinit(); } if (iseagerinit) { this .getbean(beanname); } } else { //注冊bean this .getbean(beanname); } } } } |
是使用getbean方法來通過反射將所有未創建的實例創建出來
使用嵌入式servlet容器:
優點: 簡單,便攜
缺點: 默認不支持jsp,優化定制比較復雜
使用外置servlet容器的步驟:
1 必須創建war項目,需要劍豪web項目的目錄結構
2 嵌入式tomcat依賴scope指定provided
3 編寫springbootservletinitializer類子類,并重寫configure方法
1
2
3
4
5
6
7
|
public class servletinitializer extends springbootservletinitializer { @override protected springapplicationbuilder configure(springapplicationbuilder application) { return application.sources(springboot04webjspapplication. class ); } } |
4 啟動服務器
jar包和war包啟動區別
jar包:執行springbootapplication的run方法,啟動ioc容器,然后創建嵌入式servlet容器
war包: 先是啟動servlet服務器,服務器啟動springboot應用(springbootservletinitizer),然后啟動ioc容器
servlet 3.0+規則
1 服務器啟動(web應用啟動),會創建當前web應用里面所有jar包里面的servletcontainerlnitializer實例
2 servletcontainerinitializer的實現放在jar包的meta-inf/services文件夾下
3 還可以使用@handlestypes注解,在應用啟動的時候加載指定的類。
外部tomcat流程以及原理
① 啟動tomcat
② 根據上述描述的servlet3.0+規則,可以在spring的web模塊里面找到有個文件名為javax.servlet.servletcontainerinitializer的文件,而文件的內容為org.springframework.web.springservletcontainerinitializer,用于加載springservletcontainerinitializer類
③看看springservletcontainerinitializer定義
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
@handlestypes (webapplicationinitializer. class ) public class springservletcontainerinitializer implements servletcontainerinitializer { /** * delegate the {@code servletcontext} to any {@link webapplicationinitializer} * implementations present on the application classpath. * <p>because this class declares @{@code handlestypes(webapplicationinitializer.class)}, * servlet 3.0+ containers will automatically scan the classpath for implementations * of spring's {@code webapplicationinitializer} interface and provide the set of all * such types to the {@code webappinitializerclasses} parameter of this method. * <p>if no {@code webapplicationinitializer} implementations are found on the classpath, * this method is effectively a no-op. an info-level log message will be issued notifying * the user that the {@code servletcontainerinitializer} has indeed been invoked but that * no {@code webapplicationinitializer} implementations were found. * <p>assuming that one or more {@code webapplicationinitializer} types are detected, * they will be instantiated (and <em>sorted</em> if the @{@link * org.springframework.core.annotation.order @order} annotation is present or * the {@link org.springframework.core.ordered ordered} interface has been * implemented). then the {@link webapplicationinitializer#onstartup(servletcontext)} * method will be invoked on each instance, delegating the {@code servletcontext} such * that each instance may register and configure servlets such as spring's * {@code dispatcherservlet}, listeners such as spring's {@code contextloaderlistener}, * or any other servlet api componentry such as filters. * @param webappinitializerclasses all implementations of * {@link webapplicationinitializer} found on the application classpath * @param servletcontext the servlet context to be initialized * @see webapplicationinitializer#onstartup(servletcontext) * @see annotationawareordercomparator */ @override public void onstartup(set< class <?>> webappinitializerclasses, servletcontext servletcontext) throws servletexception { list<webapplicationinitializer> initializers = new linkedlist<webapplicationinitializer>(); if (webappinitializerclasses != null ) { for ( class <?> waiclass : webappinitializerclasses) { // be defensive: some servlet containers provide us with invalid classes, // no matter what @handlestypes says... if (!waiclass.isinterface() && !modifier.isabstract(waiclass.getmodifiers()) && webapplicationinitializer. class .isassignablefrom(waiclass)) { try { //為所有的webapplicationinitializer類型創建實例,并加入集合中 initializers.add((webapplicationinitializer) waiclass.newinstance()); } catch (throwable ex) { throw new servletexception( "failed to instantiate webapplicationinitializer class" , ex); } } } } if (initializers.isempty()) { servletcontext.log( "no spring webapplicationinitializer types detected on classpath" ); return ; } servletcontext.log(initializers.size() + " spring webapplicationinitializers detected on classpath" ); annotationawareordercomparator.sort(initializers); //調用每一個webapplicationinitializer實例的onstartup方法 for (webapplicationinitializer initializer : initializers) { initializer.onstartup(servletcontext); } } } |
在上面一段長長的注釋中可以看到,springservletcontainerinitializer將@handlestypes(webapplicationinitializer.class)標注的所有webapplicationinitializer這個類型的類都傳入到onstartup方法的set參數中,并通過反射為這些webapplicationinitializer類型的類創建實例;
④ 方法最后,每一個webapplicationinitilizer實現調用自己onstartup方法
⑤ 而webapplicationinitializer有個抽象實現類springbootservletinitializer(記住我們繼承了該抽象類),則會調用每一個webapplicationinitializer實例(包括springbootservletinitializer)的onstartup方法:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
|
public abstract class springbootservletinitializer implements webapplicationinitializer { //other code... @override public void onstartup(servletcontext servletcontext) throws servletexception { // logger initialization is deferred in case a ordered // logservletcontextinitializer is being used this .logger = logfactory.getlog(getclass()); //創建ioc容器 webapplicationcontext rootappcontext = createrootapplicationcontext( servletcontext); if (rootappcontext != null ) { servletcontext.addlistener( new contextloaderlistener(rootappcontext) { @override public void contextinitialized(servletcontextevent event) { // no-op because the application context is already initialized } }); } else { this .logger.debug( "no contextloaderlistener registered, as " + "createrootapplicationcontext() did not " + "return an application context" ); } } protected webapplicationcontext createrootapplicationcontext( servletcontext servletcontext) { //創建spring應用構建器,并進行相關屬性設置 springapplicationbuilder builder = createspringapplicationbuilder(); standardservletenvironment environment = new standardservletenvironment(); environment.initpropertysources(servletcontext, null ); builder.environment(environment); builder.main(getclass()); applicationcontext parent = getexistingrootwebapplicationcontext(servletcontext); if (parent != null ) { this .logger.info( "root context already created (using as parent)." ); servletcontext.setattribute( webapplicationcontext.root_web_application_context_attribute, null ); builder.initializers( new parentcontextapplicationcontextinitializer(parent)); } builder.initializers( new servletcontextapplicationcontextinitializer(servletcontext)); builder.contextclass(annotationconfigembeddedwebapplicationcontext. class ); //調用configure方法,創建war類型的web項目后,由于編寫springbootservletinitializer的子類重寫configure方法,所以此處調用的是我們定義的子類重寫的configure方法 builder = configure(builder); //通過構建器構建了一個spring應用 springapplication application = builder.build(); if (application.getsources().isempty() && annotationutils .findannotation(getclass(), configuration. class ) != null ) { application.getsources().add(getclass()); } assert .state(!application.getsources().isempty(), "no springapplication sources have been defined. either override the " + "configure method or add an @configuration annotation" ); // ensure error pages are registered if ( this .registererrorpagefilter) { application.getsources().add(errorpagefilterconfiguration. class ); } //啟動spring應用 return run(application); } //spring應用啟動,創建并返回ioc容器 protected webapplicationcontext run(springapplication application) { return (webapplicationcontext) application.run(); } } |
springbootservletinitializer實例執行onstartup方法的時候會通過createrootapplicationcontext方法來執行run方法,接下來的過程就同以jar包形式啟動的應用的run過程一樣了,在內部會創建ioc容器并返回,只是以war包形式的應用在創建ioc容器過程中,不再創建servlet容器了。
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對服務器之家的支持。
原文鏈接:https://www.cnblogs.com/developerxiaofeng/p/9081689.html