SpringBoot启动流程
1.springboot项目启动方式:
- 在ide中启动springboot主类(xxxapplication)中的main方法
- 使用mvn spring-boot:run命令启动
- 打成jar包之后使用java -jar xxx.jar运行
- 打成war包之后放在web容器中运行
这是一篇一年多前写的博客,使用的源码版本是1.5.x。当时发布在csdn,现在同步到其他平台,虽然springboot这个版本帝刷的很快,但是2.x版本的启动流程并没有怎么变化,一样可供参考。
2.springboot启动流程主要分为三步:
第一部分:springapplication初始化模块,配置一些基本的环境变量,资源,监听器,构造器;
第二部分:实现了应用具体的启动方案,包括流程的监听模块,加载配置环境模块以及创建上下文环境模块
第三部分:自动化配置模块,这个模块是实现springboot的自动配置
springboot程序的主入口就是标注了@springbootapplication注解的类,该类中有一个main方法,在main方法中调用springapplication的run()方法,这个run()方法来启动整个程序
@springbootapplication public class crmwebapiapplication extends springbootservletinitializer { public static void main(string[] args) { springapplication.run(crmwebapiapplication.class, args); } }
下面是@springbootapplication注解的头部源码
@target(elementtype.type) @retention(retentionpolicy.runtime) @documented @inherited @springbootconfiguration @enableautoconfiguration @componentscan(excludefilters = { @filter(type = filtertype.custom, classes = typeexcludefilter.class), @filter(type = filtertype.custom, classes = autoconfigurationexcludefilter.class) }) public @interface springbootapplication {
这是一个组合注解,其中标注的注解主要有以下作用
@enableautoconfiguration: 开启springboot自动配置,在程序启动时会自动加载springboot的默认配置,如果有对一些参数进行配置,则会在程序启动时或调用时进行追加或者覆盖
@springbootconfiguration: 这个注解和@configuration注解的作用一样,用来表示被标注的类是一个配置类,会将被标注的类中一个或多个被@bean注解修饰的方法添加到spring容器中,实例的名字默认是方法名
@componentscan: 包扫描注解,默认扫描主类包路径下的类
进入run()方法后的代码如下:
/** * static helper that can be used to run a {@link springapplication} from the * specified sources using default settings and user supplied arguments. * @param sources the sources to load * @param args the application arguments (usually passed from a java main method) * @return the running {@link applicationcontext} */ public static configurableapplicationcontext run(object[] sources, string[] args) { return new springapplication(sources).run(args); }
这里会创建一个springapplication类的实例,进入springapplication类中可以看到构造方法里调用了一个initialize(sources)方法
/** * create a new {@link springapplication} instance. the application context will load * beans from the specified sources (see {@link springapplication class-level} * documentation for details. the instance can be customized before calling * {@link #run(string...)}. * @param sources the bean sources * @see #run(object, string[]) * @see #springapplication(resourceloader, object...) */ public springapplication(object... sources) { initialize(sources); }
initialize(sources)方法源码如下:
@suppresswarnings({ "unchecked", "rawtypes" }) private void initialize(object[] sources) { if (sources != null && sources.length > 0) { //将sources设置到springapplication类的source属性中,这时的source值只有主类 this.sources.addall(arrays.aslist(sources)); } //判断是不是web程序, this.webenvironment = deducewebenvironment(); //从spring.factories文件中找出key为applicationcontextinitializer的类进行实例化,然后设置到springapplciation类的initializers属性中,这个过程也是找出所有的应用程序初始化器 setinitializers((collection) getspringfactoriesinstances( applicationcontextinitializer.class)); //从spring.factories文件中找出key为applicationlistener的类并实例化后设置到springapplication的listeners属性中。这个过程就是找出所有的应用程序事件监听器 setlisteners((collection) getspringfactoriesinstances(applicationlistener.class)); //找出main类,也就是springboot项目的主类 this.mainapplicationclass = deducemainapplicationclass(); }
2.1 run方法完整代码
执行完初始化之后回到run()方法中,完整代码如下:
/** * run the spring application, creating and refreshing a new * {@link applicationcontext}. * @param args the application arguments (usually passed from a java main method) * @return a running {@link applicationcontext} */ public configurableapplicationcontext run(string... args) { 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); //加载springboot配置环境configurableenvironment,见2.2配置configurableenvironment configurableenvironment environment = prepareenvironment(listeners,applicationarguments); //打印banner banner printedbanner = printbanner(environment); //创建应用程序上下文,见2.3 创建应用程序上下文 context = createapplicationcontext(); analyzers = new failureanalyzers(context); preparecontext(context, environment, listeners, applicationarguments,printedbanner); 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.2 配置configurableenvironment
加载springboot配置环境configurableenvironment流程如下:
private configurableenvironment prepareenvironment( springapplicationrunlisteners listeners, applicationarguments applicationarguments) { // create and configure the environment configurableenvironment environment = getorcreateenvironment(); configureenvironment(environment, applicationarguments.getsourceargs()); listeners.environmentprepared(environment); if (!this.webenvironment) { environment = new environmentconverter(getclassloader()) .converttostandardenvironmentifnecessary(environment); } return environment; }
在加载配置环境的过程中会判断是否是web容器启动,如果是容器启动会加载standardservletenvironment
private configurableenvironment getorcreateenvironment() { if (this.environment != null) { return this.environment; } if (this.webenvironment) { return new standardservletenvironment(); } return new standardenvironment(); }
standardservletenvironment类的继承关系如下,standardservletenvironment
propertyresolver接口是用于解析任何基础源的属性的接口,在加载完配置之后会将配置环境加入到监听器对象springapplicationrunlisteners中。
2.3 创建应用程序上下文
然后会创建应用上下文对象,具体代码如下:
protected configurableapplicationcontext createapplicationcontext() { class<?> contextclass = this.applicationcontextclass; if (contextclass == null) { try { 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); }
方法会先显式的获取应用上下文对象,如果对象为空,再加载默认的环境配置,通过是否是webenvironment进行判断,默认选择的是annotationconfigapplicationcontext(注解上下文,通过扫秒注解来加载bean),然后通过beanutils来实例化应用上下文对象然后返回,configurableapplicationcontext类继承关系如下:
这里推荐一下我的另一篇博客,不太懂configurableapplicationcontext的可以去看一下,https://juejin.im/post/5d72055f5188256bab4c0b6d
回到run()方法中,会调用preparecontext()方法将environment, listeners,applicationarguments, printedbanner等组件与上下文对象进行关联
private void preparecontext(configurableapplicationcontext context, configurableenvironment environment, springapplicationrunlisteners listeners, applicationarguments applicationarguments, banner printedbanner) { context.setenvironment(environment); postprocessapplicationcontext(context); applyinitializers(context); listeners.contextprepared(context); if (this.logstartupinfo) { logstartupinfo(context.getparent() == null); logstartupprofileinfo(context); } // add boot specific singleton beans context.getbeanfactory().registersingleton("springapplicationarguments", applicationarguments); if (printedbanner != null) { context.getbeanfactory().registersingleton("springbootbanner", printedbanner); } // load the sources set<object> sources = getsources(); assert.notempty(sources, "sources must not be empty"); load(context, sources.toarray(new object[sources.size()])); listeners.contextloaded(context); }
然后会调用refreshcontext()方法,实际调用org.springframework.context.support.abstractapplicationcontext.refresh()内的相关方法。这个方法里会进行redis,mybatis等的自动配置,包括spring.factories的加载,bean的实例化,benfactorypostprocessor接口的执行,beanpostprocessor接口的执行,条件注解的解析,国际化功能的初始化等。
refreshcontext()方法执行完毕之后会执行afterrefresh方法,当run()方法执行完之后spring容器也就初始化完毕了
protected void afterrefresh(configurableapplicationcontext context, applicationarguments args) { callrunners(context, args); } private void callrunners(applicationcontext context, applicationarguments args) { list<object> runners = new arraylist<object>(); runners.addall(context.getbeansoftype(applicationrunner.class).values()); runners.addall(context.getbeansoftype(commandlinerunner.class).values()); annotationawareordercomparator.sort(runners); for (object runner : new linkedhashset<object>(runners)) { if (runner instanceof applicationrunner) { callrunner((applicationrunner) runner, args); } if (runner instanceof commandlinerunner) { callrunner((commandlinerunner) runner, args); } } } private void callrunner(applicationrunner runner, applicationarguments args) { try { (runner).run(args); } catch (exception ex) { throw new illegalstateexception("failed to execute applicationrunner", ex); } } private void callrunner(commandlinerunner runner, applicationarguments args) { try { (runner).run(args.getsourceargs()); } catch (exception ex) { throw new illegalstateexception("failed to execute commandlinerunner", ex); } }
推荐阅读