深入讲解spring boot中servlet的启动过程与原理
前言
本文主要介绍了关于spring boot中servlet启动过程与原理的相关内容,下面话不多说了,来一起看看详细的介绍吧
启动过程与原理:
1 spring boot 应用启动运行run方法
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容器
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容器(创建容器对象,并初始化容器,创建容器每一个组件)
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容器
protected void refresh(applicationcontext applicationcontext) { assert.isinstanceof(abstractapplicationcontext.class, applicationcontext); ((abstractapplicationcontext) applicationcontext).refresh(); }
5 调用父类的refresh()的方法
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方法
@override protected void onrefresh() { super.onrefresh(); try { createembeddedservletcontainer(); } catch (throwable ex) { throw new applicationcontextexception("unable to start embedded container", ex); } }
7 在createembeddedservletcontainer放啊发中会获取嵌入式servlet容器工厂,由容器工厂创建servlet
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容器工厂
//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容器,具体使用哪一个容器工厂看配置环境依赖
this.embeddedservletcontainer = containerfactory .getembeddedservletcontainer(getselfinitializer());
10 上述创建过程 首先启动ioc容器,接着启动嵌入式servlet容器,接着将ioc容器中剩下没有创建的对象获取出来,比如自己创建的controller
// instantiate all remaining (non-lazy-init) singletons. finishbeanfactoryinitialization(beanfactory);
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方法
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方法
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定义
@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方法:
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容器了。
总结
以上就是这篇文章的全部内容了,希望本文的内容对大家的学习或者工作具有一定的参考学习价值,如果有疑问大家可以留言交流,谢谢大家对的支持。