随着互联网的快速发展,各种组件层出不穷,需要框架集成的组件越来越多。每一种组件与spring容器整合需要实现相关代码。springmvc框架配置由于太过于繁琐和依赖xml文件;为了方便快速集成第三方组件和减少对配置文件的依赖,springboot应运而生,其中采用了约定大于配置的理论让开发者不需要过多配置即可进行开发。springboot底层使用的spring ,默认集成了n多组件的自动装配。使用springboot很简单,在主类中添加一个@springbootapplication,以及调用springapplication.run()并传入主类。代码如下
@springbootapplicationpublic class startapp { public static void main(string[] args) { springapplication.run(startapp.class); }}
由上面的源码可知,springapplication.run()是springboot的程序入口。本文会从springapplication.run()和@springbootapplication注解两方面来分析。
一、springboot启动代码主线分析springapplication.run(startapp.class)的中关键代码,先创建一个springapplication类,再执行run方法。代码如下,
public static configurableapplicationcontext run(class<?>[] primarysources, string[] args) { return new springapplication(primarysources).run(args);}
1.springapplication的构造方法代码如下
public springapplication(resourceloader resourceloader, class<?>... primarysources) { // 设置资源加载器 this.resourceloader = resourceloader; assert.notnull(primarysources, "primarysources must not be null"); // 设置应用主配置类 this.primarysources = new linkedhashset<>(arrays.aslist(primarysources)); // 获取web服务器类型 this.webapplicationtype = webapplicationtype.deducefromclasspath(); // 从spring.factories 文件中获取 applicationcontextinitializer 的实现类 setinitializers((collection) getspringfactoriesinstances(applicationcontextinitializer.class)); // 从spring.factories 文件中获取 applicationlistener 监听器的实现类 setlisteners((collection) getspringfactoriesinstances(applicationlistener.class)); // 设置main启动类 this.mainapplicationclass = deducemainapplicationclass();}
构造方法中主要逻辑:
1.设置应用主配置类,后面的run方法中会用它封装成 beandefinitionholder 并加载到 context 的 registry 中。
2.获取web服务器类型,后面的run方法中会用它来创建具体的web服务类型。
3.从spring.factories 文件中获取 applicationcontextinitializer 的实现类,并设置给springapplication实例
4.从spring.factories 文件中获取 applicationlistener 监听器的实现类,并设置给springapplication实例
5.设置main启动类
其中getspringfactoriesinstances方法主要逻辑是:从meta-inf/spring.factories文件中根据接口获取具体实现类字符串,并把字符串成实例化为对象。代码如下,
// 获取类加载器classloader classloader = getclassloader();// use names and ensure unique to protect against duplicates// 根据type 从meta-inf/spring.factories获取 具体的实现类字符串列表set<string> names = new linkedhashset<>(springfactoriesloader.loadfactorynames(type, classloader));// 实例化具体的实现类list<t> instances = createspringfactoriesinstances(type, parametertypes, classloader, args, names);// 排序annotationawareordercomparator.sort(instances);return instances;
在meta-inf/spring.factories文件中applicationcontextinitializer.class 对应的实现类字符串为,
org.springframework.boot.context.configurationwarningsapplicationcontextinitializer,\org.springframework.boot.context.contextidapplicationcontextinitializer,\org.springframework.boot.context.config.delegatingapplicationcontextinitializer,\org.springframework.boot.rsocket.context.rsocketportinfoapplicationcontextinitializer,\org.springframework.boot.web.context.serverportinfoapplicationcontextinitializer
在meta-inf/spring.factories文件中applicationlistener.class 对应的实现类字符串为,
org.springframework.boot.clearcachesapplicationlistener,\org.springframework.boot.builder.parentcontextcloserapplicationlistener,\org.springframework.boot.cloud.cloudfoundryvcapenvironmentpostprocessor,\org.springframework.boot.context.fileencodingapplicationlistener,\org.springframework.boot.context.config.ansioutputapplicationlistener,\org.springframework.boot.context.config.configfileapplicationlistener,\org.springframework.boot.context.config.delegatingapplicationlistener,\org.springframework.boot.context.logging.classpathloggingapplicationlistener,\org.springframework.boot.context.logging.loggingapplicationlistener,\org.springframework.boot.liquibase.liquibaseservicelocatorapplicationlistener
2.run方法的代码如下,
stopwatch stopwatch = new stopwatch();stopwatch.start();configurableapplicationcontext context = null;collection<springbootexceptionreporter> exceptionreporters = new arraylist<>();// 设置了一个名为 java.awt.headless 的系统属性// 其实是想设计应用程序,即使没有检测到显示器,也允许其启动// 对于服务器来说,是不需要显示器的 ,所以要这样设置configureheadlessproperty();// 获取 springapplicationrunlistener 加载的是 eventpublishingrunlistener// 获取启动时的监听器springapplicationrunlisteners listeners = getrunlisteners(args);// 触发启动事件listeners.starting();try { // 构造一个应用程序的参数持有类 applicationarguments applicationarguments = new defaultapplicationarguments(args); // 创建并配置环境 configurableenvironment environment = prepareenvironment(listeners, applicationarguments); // 配置需要忽略的beaninfo信息 configureignorebeaninfo(environment); banner printedbanner = printbanner(environment); // 创建上下文对象 context = createapplicationcontext(); // 加载配置的启动异常处理器 exceptionreporters = getspringfactoriesinstances(springbootexceptionreporter.class, new class[] { configurableapplicationcontext.class }, context); // 刷新前操作 preparecontext(context, environment, listeners, applicationarguments, printedbanner); // 刷新应用上下文 完成 spring 容器的初始化 refreshcontext(context); // 刷新后操作 afterrefresh(context, applicationarguments); stopwatch.stop(); if (this.logstartupinfo) { new startupinfologger(this.mainapplicationclass).logstarted(getapplicationlog(), stopwatch); } // 启动完成事件 listeners.started(context); // 执行 applicationrunner 和 commandlinerunner 实现类 callrunners(context, applicationarguments);}catch (throwable ex) { // 事件广播启动出错了 handlerunfailure(context, ex, exceptionreporters, listeners); throw new illegalstateexception(ex);}try { // 运行事件 listeners.running(context);}catch (throwable ex) { handlerunfailure(context, ex, exceptionreporters, null); throw new illegalstateexception(ex);}return context;
run方法中主要逻辑:
1. 从spring.factories 文件中获取 springapplicationrunlistener 的实现类(监听事件发布器),并在context生命周期中执行相关的事件 ,比如触发启动事件、启动完成事件等。
2.创建web应用上下文对象,根据webapplicationtype来创建具体的web服务类型。
3.刷新前操作,把主配置类资源封装成 beandefinitionholder 加载到 context 的 registry 中。
4.刷新应用上下文 完成 spring 容器的初始化。
5.执行 实现了 applicationrunner 和 commandlinerunner 接口的类。
二、springboot自动装配原理分析1.自动装配的前置知识@import@springbootapplication注解其中主要是利用@import 注解,@import源码如下:
@target(elementtype.type)@retention(retentionpolicy.runtime)@documentedpublic @interface import { /** * {@link configuration @configuration}, {@link importselector}, * {@link importbeandefinitionregistrar}, or regular component classes to import. */ class<?>[] value();}
@import在注解一般和@configuration一起用,spring容器初始化的过程中会进行解析@configuration注解类(源码在org.springframework.context.annotation.configurationclasspostprocessor#processconfigbeandefinitions中),其过程会解析注解类的@import注解的元数据,并根据类是否实现相关接口进行处理。源码位置:org.springframework.context.annotation.configurationclassparser#processimports;关键代码如下,
try { for (sourceclass candidate : importcandidates) { if (candidate.isassignable(importselector.class)) { // candidate class is an importselector -> delegate to it to determine imports class<?> candidateclass = candidate.loadclass(); importselector selector = parserstrategyutils.instantiateclass(candidateclass, importselector.class, this.environment, this.resourceloader, this.registry); predicate<string> selectorfilter = selector.getexclusionfilter(); if (selectorfilter != null) { exclusionfilter = exclusionfilter.or(selectorfilter); } if (selector instanceof deferredimportselector) { this.deferredimportselectorhandler.handle(configclass, (deferredimportselector) selector); } else { string[] importclassnames = selector.selectimports(currentsourceclass.getmetadata()); collection<sourceclass> importsourceclasses = assourceclasses(importclassnames, exclusionfilter); processimports(configclass, currentsourceclass, importsourceclasses, exclusionfilter, false); } } else if (candidate.isassignable(importbeandefinitionregistrar.class)) { // candidate class is an importbeandefinitionregistrar -> // delegate to it to register additional bean definitions class<?> candidateclass = candidate.loadclass(); importbeandefinitionregistrar registrar = parserstrategyutils.instantiateclass(candidateclass, importbeandefinitionregistrar.class, this.environment, this.resourceloader, this.registry); configclass.addimportbeandefinitionregistrar(registrar, currentsourceclass.getmetadata()); } else { // candidate class not an importselector or importbeandefinitionregistrar -> // process it as an @configuration class this.importstack.registerimport( currentsourceclass.getmetadata(), candidate.getmetadata().getclassname()); processconfigurationclass(candidate.asconfigclass(configclass), exclusionfilter); } }}
从上面代码可知@import的value类使用有三种场景:
1.实现了 importselector.class接口的场景;直接调用实例selector 的selectimports方法返回要实例化的bean对象的全类名列表,并根据全类名字符串列表创建实例对象,然后递归调用当前的processimports 方法,最终会添加到configurationclasses的集合中,configurationclasses集合中的对象会被注册到beandefinitionregistry类型的 registry 对象中。实现接口importselector这种情况下又扩展了 deferredimportselector 接口的情况,该接口用来实现beandefinition的延迟注入功能更。deferredimportselector接口扩展了importselector接口,并且其中有个内部接口 group,如果某个@import注解的value类实现了deferredimportselector接口并且也实现了该接口的内部类group接口,则表面此实现类需要延迟处理。如果是需要延迟处理,则会把importselector 实例selector 组装成 deferredimportselectorholder 对象添加到 deferredimportselectors集合中,处理逻辑源码位置: org.springframework.context.annotation.configurationclassparser.deferredimportselectorhandler#handle;关键代码如下,
public void handle(configurationclass configclass, deferredimportselector importselector) { deferredimportselectorholder holder = new deferredimportselectorholder(configclass, importselector); if (this.deferredimportselectors == null) { deferredimportselectorgroupinghandler handler = new deferredimportselectorgroupinghandler(); handler.register(holder); handler.processgroupimports(); } else { this.deferredimportselectors.add(holder); }}
deferredimportselector接口的实现逻辑会在org.springframework.context.annotation.configurationclassparser#parse方法中调用,具体代码在this.deferredimportselectorhandler.process()中,关键代码如下,
public void process() { list<deferredimportselectorholder> deferredimports = this.deferredimportselectors; this.deferredimportselectors = null; try { if (deferredimports != null) { deferredimportselectorgroupinghandler handler = new deferredimportselectorgroupinghandler(); deferredimports.sort(deferred_import_comparator); deferredimports.foreach(handler::register); // 具体的执行逻辑 handler.processgroupimports(); } } finally { this.deferredimportselectors = new arraylist<>(); }}
在processgroupimports()方法中,先通过grouping.getimports()拿到需要自动装配的group.entry(封装了全类名)对象集合,然后通过processimports()方法根据entry类名字符串进行创建sourceclass类(该类可以通过asconfigclass()方法转成configurationclass对象),最终添加到configurationclasses集合中。代码如下,
public void processgroupimports() { for (deferredimportselectorgrouping grouping : this.groupings.values()) { predicate<string> exclusionfilter = grouping.getcandidatefilter(); grouping.getimports().foreach(entry -> { configurationclass configurationclass = this.configurationclasses.get(entry.getmetadata()); try { processimports(configurationclass, assourceclass(configurationclass, exclusionfilter), collections.singleton(assourceclass(entry.getimportclassname(), exclusionfilter)), exclusionfilter, false); } catch (beandefinitionstoreexception ex) { throw ex; } catch (throwable ex) { throw new beandefinitionstoreexception( "failed to process import candidates for configuration class [" + configurationclass.getmetadata().getclassname() + "]", ex); } }); }}
grouping.getimports()方法中主要执行具体的实现类的process方法和selectimports()方法(如果是autoconfigurationimportselector类,则调用org.springframework.boot.autoconfigure.autoconfigurationimportselector.autoconfigurationgroup#process和org.springframework.boot.autoconfigure.autoconfigurationimportselector.autoconfigurationgroup#selectimports,两个方法的具体类容请看2.2.2章节的说明),selectimports返回需要自动装配的group.entry对象集合,entry对象中保存了全类名。代码如下:
public iterable<group.entry> getimports() { for (deferredimportselectorholder deferredimport : this.deferredimports) { this.group.process(deferredimport.getconfigurationclass().getmetadata(), deferredimport.getimportselector()); } return this.group.selectimports();}
importselector接口代码代码如下:
public interface importselector { string[] selectimports(annotationmetadata importingclassmetadata); @nullable default predicate<string> getexclusionfilter() { return null; }}
deferredimportselector接口的代码如下:
public interface deferredimportselector extends importselector { @nullable default class<? extends group> getimportgroup() { return null; } interface group { void process(annotationmetadata metadata, deferredimportselector selector); iterable<entry> selectimports(); class entry { // 省略 } }}
2.实现了 importbeandefinitionregistrar.class接口的场景;会先创建importbeandefinitionregistrar 实例类 registrar,再把 registrar 添加到 configclass 的 importbeandefinitionregistrars中,接口的registerbeandefinitions方法的调用是在 org.springframework.context.annotation.configurationclasspostprocessor#processconfigbeandefinitions方法里的this.reader.loadbeandefinitions(configclasses)代码中。具体执行语句loadbeandefinitionsfromregistrars(configclass.getimportbeandefinitionregistrars());关键代码如下,
private void loadbeandefinitionsfromregistrars(map<importbeandefinitionregistrar, annotationmetadata> registrars) { registrars.foreach((registrar, metadata) -> registrar.registerbeandefinitions(metadata, this.registry, this.importbeannamegenerator));}
importbeandefinitionregistrar接口代码如下:
public interface importbeandefinitionregistrar { default void registerbeandefinitions(annotationmetadata importingclassmetadata, beandefinitionregistry registry, beannamegenerator importbeannamegenerator) { registerbeandefinitions(importingclassmetadata, registry); } default void registerbeandefinitions(annotationmetadata importingclassmetadata, beandefinitionregistry registry) { }}
3.没有实现以上两接口的普通类,会直接调用org.springframework.context.annotation.configurationclassparser#processimports里面的processconfigurationclass方法,把当前configclass添加至 configurationclasses 集合中。configurationclasses集合中的对象最终会被注册到beandefinitionregistry类型的 registry 对象中。
2.@springapplication注解分析@springapplication注解主要包括了@springbootconfiguration、@enableautoconfiguration、@componentscan。代码如下,
@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) })
2.1@springbootconfiguration配置注解,包含了@configuration注解,表明是配置类。
2.2@enableautoconfiguration自动装配注解,主要逻辑是:根据 enableautoconfiguration 类型从meta-inf/spring.factories 文件加载需要自动装配的类,并注入到spring容器中。它包括了@autoconfigurationpackage注解和一个@import(autoconfigurationimportselector.class)注解。代码如下,
@target(elementtype.type)@retention(retentionpolicy.runtime)@documented@inherited@autoconfigurationpackage@import(autoconfigurationimportselector.class)
2.2.1@autoconfigurationpackage注册名为 org.springframework.boot.autoconfigure.autoconfigurationpackages ,beanclass为basepackages.class 的genericbeandefinition 到 beandefinitionregistry 中,通过@import 注解实现注入功能,代码如下,
@target(elementtype.type)@retention(retentionpolicy.runtime)@documented@inherited@import(autoconfigurationpackages.registrar.class)public @interface autoconfigurationpackage {}
autoconfigurationpackages.registrar.class实现了importbeandefinitionregistrar接口 ,所以在spring容器初始化的过程中会调用它的registerbeandefinitions方法把packageimport类注入到spring容器中去。代码如下,
static class registrar implements importbeandefinitionregistrar, determinableimports { @override public void registerbeandefinitions(annotationmetadata metadata, beandefinitionregistry registry) { register(registry, new packageimport(metadata).getpackagename()); } @override public set<object> determineimports(annotationmetadata metadata) { return collections.singleton(new packageimport(metadata)); }}
2.2.2@import(autoconfigurationimportselector.class)自动装配关键逻辑,先从meta-inf/spring.factories 文件加载类型值为 enableautoconfiguration的字符串集合,再通过过滤,生成需要自动装配的类,最后注入到spring容器中。autoconfigurationimportselector实现了deferredimportselector接口并且内部也实现了deferredimportselector.group接口,所以在spring容器初始化的过程中会调用
org.springframework.boot.autoconfigure.autoconfigurationimportselector.autoconfigurationgroup#process方法和#selectimports方法,
process()用来生成需要自动装配的类型,方法的代码如下,
assert.state(deferredimportselector instanceof autoconfigurationimportselector, () -> string.format("only %s implementations are supported, got %s", autoconfigurationimportselector.class.getsimplename(), deferredimportselector.getclass().getname()));// 1. getautoconfigurationmetadata()// 从meta-inf/spring-autoconfigure-metadata.properties文件中获取自动装配的元数据,// 里面保存了加载类是否自动装配的条件 ,// org.springframework.boot.autoconfigure.jms.jmsautoconfiguration.conditionalonbean// =javax.jms.connectionfactory// 2. getautoconfigurationentry()// 从 meta-inf/spring.factories 文件中获取key为 enableautoconfiguration 的配置类字符串类表 并封装成 自动装配类对象autoconfigurationentry autoconfigurationentry = ((autoconfigurationimportselector) deferredimportselector) .getautoconfigurationentry(getautoconfigurationmetadata(), annotationmetadata);this.autoconfigurationentries.add(autoconfigurationentry);// 循环遍历 自动装配类对象 的自动装配类字符串 ,添加到 this.entriesfor (string importclassname : autoconfigurationentry.getconfigurations()) { this.entries.putifabsent(importclassname, annotationmetadata);}
getautoconfigurationmetadata() 方法主要逻辑是:从meta-inf/spring-autoconfigure-metadata.properties文件中获取自动装配的元数据,里面保存了自动加载类是否符合自动装配的前置条件,比较熟悉的有conditionalonclass和conditionalonbean,文件相关内容如下:
org.springframework.boot.autoconfigure.data.jpa.jparepositoriesautoconfiguration=org.springframework.boot.autoconfigure.web.client.resttemplateautoconfiguration.autoconfigureafter=org.springframework.boot.autoconfigure.http.httpmessageconvertersautoconfigurationorg.springframework.boot.autoconfigure.data.cassandra.cassandrareactivedataautoconfiguration.conditionalonclass=com.datastax.driver.core.cluster,reactor.core.publisher.flux,org.springframework.data.cassandra.core.reactivecassandratemplateorg.springframework.boot.autoconfigure.data.solr.solrrepositoriesautoconfiguration.conditionalonclass=org.apache.solr.client.solrj.solrclient,org.springframework.data.solr.repository.solrrepositoryorg.springframework.boot.autoconfigure.security.oauth3.client.servlet.oauth3clientautoconfiguration.conditionalonwebapplication=servletorg.springframework.boot.autoconfigure.web.servlet.error.errormvcautoconfiguration=org.springframework.boot.autoconfigure.jersey.jerseyautoconfiguration.autoconfigurebefore=org.springframework.boot.autoconfigure.web.servlet.dispatcherservletautoconfigurationorg.springframework.boot.autoconfigure.jms.artemis.artemisxaconnectionfactoryconfiguration=org.springframework.boot.autoconfigure.web.reactive.httphandlerautoconfiguration.conditionalonwebapplication=reactiveorg.springframework.boot.autoconfigure.web.reactive.reactivewebserverfactoryautoconfiguration.conditionalonwebapplication=reactiveorg.springframework.boot.autoconfigure.data.elasticsearch.elasticsearchrepositoriesautoconfiguration=org.springframework.boot.autoconfigure.security.oauth3.resource.servlet.oauth3resourceserverautoconfiguration.conditionalonwebapplication=servletorg.springframework.boot.autoconfigure.web.servlet.multipartautoconfiguration.conditionalonwebapplication=servlet//省略
getautoconfigurationentry()方法 主要逻辑是:从spring.factories 文件中获取key为 enableautoconfiguration 的配置类字符串列表并封装成自动装配类autoconfigurationentry对象,代码如下,
protected autoconfigurationentry getautoconfigurationentry(autoconfigurationmetadata autoconfigurationmetadata, annotationmetadata annotationmetadata) { if (!isenabled(annotationmetadata)) { return empty_entry; } // 获取注解元数据的属性 annotationattributes attributes = getattributes(annotationmetadata); // 从spring.factories 文件中获取key为 enableautoconfiguration 的配置类字符串列表 list<string> configurations = getcandidateconfigurations(annotationmetadata, attributes); // 去掉重复的 自动装配类字符串 configurations = removeduplicates(configurations); // 根据注解元数据获取 需要排除的类名 set<string> exclusions = getexclusions(annotationmetadata, attributes); // 检查排除的类名 checkexcludedclasses(configurations, exclusions); // 根据排除的类名进行排除 configurations.removeall(exclusions); // 从spring.factories 文件中获取key为 autoconfigurationimportfilter 的配置对象进行过滤 // 过滤规则从 getautoconfigurationmetadata() 返回类的数据中获取 configurations = filter(configurations, autoconfigurationmetadata); // 执行导入配置类的监听事件 fireautoconfigurationimportevents(configurations, exclusions); // 返回 autoconfigurationentry 对象 return new autoconfigurationentry(configurations, exclusions);}
getcandidateconfigurations()方法从spring.factories 文件中获取类型为 enableautoconfiguration 的配置类字符串列表,代码如下,
// getspringfactoriesloaderfactoryclass()方法返回 enableautoconfigurationlist<string> configurations = springfactoriesloader.loadfactorynames(getspringfactoriesloaderfactoryclass(), getbeanclassloader()); assert.notempty(configurations, "no auto configuration classes found in meta-inf/spring.factories. if you " + "are using a custom packaging, make sure that file is correct."); return configurations;
在meta-inf/spring.factories文件中enableautoconfiguration .class 对应的实现类字符串为
# auto configureorg.springframework.boot.autoconfigure.enableautoconfiguration=\org.springframework.boot.autoconfigure.admin.springapplicationadminjmxautoconfiguration,\org.springframework.boot.autoconfigure.aop.aopautoconfiguration,\org.springframework.boot.autoconfigure.amqp.rabbitautoconfiguration,\org.springframework.boot.autoconfigure.batch.batchautoconfiguration,\org.springframework.boot.autoconfigure.cache.cacheautoconfiguration,\// 省略
2.selectimports()方法返回排序后的 entry(需要自动装配的包装实体类) 对象集合,代码如下,
public iterable<entry> selectimports() { if (this.autoconfigurationentries.isempty()) { return collections.emptylist(); } set<string> allexclusions = this.autoconfigurationentries.stream() .map(autoconfigurationentry::getexclusions).flatmap(collection::stream).collect(collectors.toset()); set<string> processedconfigurations = this.autoconfigurationentries.stream() .map(autoconfigurationentry::getconfigurations).flatmap(collection::stream) .collect(collectors.tocollection(linkedhashset::new)); processedconfigurations.removeall(allexclusions); // 返回排序后的 entry 集合 return sortautoconfigurations(processedconfigurations, getautoconfigurationmetadata()).stream() .map((importclassname) -> new entry(this.entries.get(importclassname), importclassname)) .collect(collectors.tolist());}
注意:@enableautoconfiguration 注解的分析过程需要结合@import注解的过程来看。
2.2.3@componentscan组件扫描注解,用来配置自动扫描包路径。如果没有配置路径,则扫描主配置类命名空间下的所有包和类。
以上就是springboot启动代码和自动装配源码是什么的详细内容。