深入浅析SpringBoot中的自动装配
springboot的自动装配是拆箱即用的基础,也是微服务化的前提。这次主要的议题是,来看看它是怎么样实现的,我们透过源代码来把握自动装配的来龙去脉。
一、自动装配过程分析
1.1、关于@springbootapplication
我们在编写springboot项目时,@springbootapplication是最常见的注解了,我们可以看一下源代码:
/* * copyright 2012-2017 the original author or authors. * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ package org.springframework.boot.autoconfigure; import java.lang.annotation.documented; import java.lang.annotation.elementtype; import java.lang.annotation.inherited; import java.lang.annotation.retention; import java.lang.annotation.retentionpolicy; import java.lang.annotation.target; import org.springframework.boot.springbootconfiguration; import org.springframework.boot.context.typeexcludefilter; import org.springframework.context.annotation.bean; import org.springframework.context.annotation.componentscan; import org.springframework.context.annotation.componentscan.filter; import org.springframework.context.annotation.configuration; import org.springframework.context.annotation.filtertype; import org.springframework.core.annotation.aliasfor; /** * indicates a {@link configuration configuration} class that declares one or more * {@link bean @bean} methods and also triggers {@link enableautoconfiguration * auto-configuration} and {@link componentscan component scanning}. this is a convenience * annotation that is equivalent to declaring {@code @configuration}, * {@code @enableautoconfiguration} and {@code @componentscan}. * * @author phillip webb * @author stephane nicoll * @since 1.2.0 */ @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 { /** * exclude specific auto-configuration classes such that they will never be applied. * @return the classes to exclude */ @aliasfor(annotation = enableautoconfiguration.class, attribute = "exclude") class<?>[] exclude() default {}; /** * exclude specific auto-configuration class names such that they will never be * applied. * @return the class names to exclude * @since 1.3.0 */ @aliasfor(annotation = enableautoconfiguration.class, attribute = "excludename") string[] excludename() default {}; /** * base packages to scan for annotated components. use {@link #scanbasepackageclasses} * for a type-safe alternative to string-based package names. * @return base packages to scan * @since 1.3.0 */ @aliasfor(annotation = componentscan.class, attribute = "basepackages") string[] scanbasepackages() default {}; /** * type-safe alternative to {@link #scanbasepackages} for specifying the packages to * scan for annotated components. the package of each class specified will be scanned. * <p> * consider creating a special no-op marker class or interface in each package that * serves no purpose other than being referenced by this attribute. * @return base packages to scan * @since 1.3.0 */ @aliasfor(annotation = componentscan.class, attribute = "basepackageclasses") class<?>[] scanbasepackageclasses() default {}; }
这里面包含了@springbootconfiguration,@enableautoconfiguration,@componentscan,此处@componentscan由于没有指定扫描包,因此它默认扫描的是与该类同级的类或者同级包下的所有类,另外@springbootconfiguration,通过源码得知它是一个@configuration:
/* * copyright 2012-2016 the original author or authors. * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ package org.springframework.boot; import java.lang.annotation.documented; import java.lang.annotation.elementtype; import java.lang.annotation.retention; import java.lang.annotation.retentionpolicy; import java.lang.annotation.target; import org.springframework.context.annotation.configuration; /** * indicates that a class provides spring boot application * {@link configuration @configuration}. can be used as an alternative to the spring's * standard {@code @configuration} annotation so that configuration can be found * automatically (for example in tests). * <p> * application should only ever include <em>one</em> {@code @springbootconfiguration} and * most idiomatic spring boot applications will inherit it from * {@code @springbootapplication}. * * @author phillip webb * @since 1.4.0 */ @target(elementtype.type) @retention(retentionpolicy.runtime) @documented @configuration public @interface springbootconfiguration { }
由此我们可以推断出@springbootapplication等同于@configuration @componentscan @enableautoconfiguration
1.2、@enableautoconfiguration
一旦加上此注解,那么将会开启自动装配功能,简单点讲,spring会试图在你的classpath下找到所有配置的bean然后进行装配。当然装配bean时,会根据若干个(conditional)定制规则来进行初始化。我们看一下它的源码:
/* * copyright 2012-2017 the original author or authors. * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ package org.springframework.boot.autoconfigure; import java.lang.annotation.documented; import java.lang.annotation.elementtype; import java.lang.annotation.inherited; import java.lang.annotation.retention; import java.lang.annotation.retentionpolicy; import java.lang.annotation.target; import org.springframework.boot.autoconfigure.condition.conditionalonbean; import org.springframework.boot.autoconfigure.condition.conditionalonclass; import org.springframework.boot.autoconfigure.condition.conditionalonmissingbean; import org.springframework.boot.context.embedded.embeddedservletcontainerfactory; import org.springframework.boot.context.embedded.tomcat.tomcatembeddedservletcontainerfactory; import org.springframework.context.annotation.conditional; import org.springframework.context.annotation.configuration; import org.springframework.context.annotation.import; import org.springframework.core.io.support.springfactoriesloader; /** * enable auto-configuration of the spring application context, attempting to guess and * configure beans that you are likely to need. auto-configuration classes are usually * applied based on your classpath and what beans you have defined. for example, if you * have {@code tomcat-embedded.jar} on your classpath you are likely to want a * {@link tomcatembeddedservletcontainerfactory} (unless you have defined your own * {@link embeddedservletcontainerfactory} bean). * <p> * when using {@link springbootapplication}, the auto-configuration of the context is * automatically enabled and adding this annotation has therefore no additional effect. * <p> * auto-configuration tries to be as intelligent as possible and will back-away as you * define more of your own configuration. you can always manually {@link #exclude()} any * configuration that you never want to apply (use {@link #excludename()} if you don't * have access to them). you can also exclude them via the * {@code spring.autoconfigure.exclude} property. auto-configuration is always applied * after user-defined beans have been registered. * <p> * the package of the class that is annotated with {@code @enableautoconfiguration}, * usually via {@code @springbootapplication}, has specific significance and is often used * as a 'default'. for example, it will be used when scanning for {@code @entity} classes. * it is generally recommended that you place {@code @enableautoconfiguration} (if you're * not using {@code @springbootapplication}) in a root package so that all sub-packages * and classes can be searched. * <p> * auto-configuration classes are regular spring {@link configuration} beans. they are * located using the {@link springfactoriesloader} mechanism (keyed against this class). * generally auto-configuration beans are {@link conditional @conditional} beans (most * often using {@link conditionalonclass @conditionalonclass} and * {@link conditionalonmissingbean @conditionalonmissingbean} annotations). * * @author phillip webb * @author stephane nicoll * @see conditionalonbean * @see conditionalonmissingbean * @see conditionalonclass * @see autoconfigureafter * @see springbootapplication */ @suppresswarnings("deprecation") @target(elementtype.type) @retention(retentionpolicy.runtime) @documented @inherited @autoconfigurationpackage @import(enableautoconfigurationimportselector.class) public @interface enableautoconfiguration { string enabled_override_property = "spring.boot.enableautoconfiguration"; /** * exclude specific auto-configuration classes such that they will never be applied. * @return the classes to exclude */ class<?>[] exclude() default {}; /** * exclude specific auto-configuration class names such that they will never be * applied. * @return the class names to exclude * @since 1.3.0 */ string[] excludename() default {}; }
虽然根据文档注释的说明它指点我们去看enableautoconfigurationimportselector。但是该类在springboot1.5.x版本已经过时了,因此我们看一下它的父类autoconfigurationimportselector:
/* * copyright 2012-2017 the original author or authors. * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ package org.springframework.boot.autoconfigure; import java.io.ioexception; import java.util.arraylist; import java.util.arrays; import java.util.collections; import java.util.hashset; import java.util.linkedhashset; import java.util.list; import java.util.map; import java.util.set; import java.util.concurrent.timeunit; import org.apache.commons.logging.log; import org.apache.commons.logging.logfactory; import org.springframework.beans.beansexception; import org.springframework.beans.factory.aware; import org.springframework.beans.factory.beanclassloaderaware; import org.springframework.beans.factory.beanfactory; import org.springframework.beans.factory.beanfactoryaware; import org.springframework.beans.factory.nosuchbeandefinitionexception; import org.springframework.beans.factory.config.configurablelistablebeanfactory; import org.springframework.boot.bind.relaxedpropertyresolver; import org.springframework.context.environmentaware; import org.springframework.context.resourceloaderaware; import org.springframework.context.annotation.deferredimportselector; import org.springframework.core.ordered; import org.springframework.core.annotation.annotationattributes; import org.springframework.core.env.configurableenvironment; import org.springframework.core.env.environment; import org.springframework.core.io.resourceloader; import org.springframework.core.io.support.springfactoriesloader; import org.springframework.core.type.annotationmetadata; import org.springframework.core.type.classreading.cachingmetadatareaderfactory; import org.springframework.core.type.classreading.metadatareaderfactory; import org.springframework.util.assert; import org.springframework.util.classutils; import org.springframework.util.stringutils; /** * {@link deferredimportselector} to handle {@link enableautoconfiguration * auto-configuration}. this class can also be subclassed if a custom variant of * {@link enableautoconfiguration @enableautoconfiguration}. is needed. * * @author phillip webb * @author andy wilkinson * @author stephane nicoll * @author madhura bhave * @since 1.3.0 * @see enableautoconfiguration */ public class autoconfigurationimportselector implements deferredimportselector, beanclassloaderaware, resourceloaderaware, beanfactoryaware, environmentaware, ordered { private static final string[] no_imports = {}; private static final log logger = logfactory .getlog(autoconfigurationimportselector.class); private configurablelistablebeanfactory beanfactory; private environment environment; private classloader beanclassloader; private resourceloader resourceloader; @override public string[] selectimports(annotationmetadata annotationmetadata) { if (!isenabled(annotationmetadata)) { return no_imports; } try { autoconfigurationmetadata autoconfigurationmetadata = autoconfigurationmetadataloader .loadmetadata(this.beanclassloader); annotationattributes attributes = getattributes(annotationmetadata); list<string> configurations = getcandidateconfigurations(annotationmetadata, attributes); configurations = removeduplicates(configurations); configurations = sort(configurations, autoconfigurationmetadata); set<string> exclusions = getexclusions(annotationmetadata, attributes); checkexcludedclasses(configurations, exclusions); configurations.removeall(exclusions); configurations = filter(configurations, autoconfigurationmetadata); fireautoconfigurationimportevents(configurations, exclusions); return configurations.toarray(new string[configurations.size()]); } catch (ioexception ex) { throw new illegalstateexception(ex); } } protected boolean isenabled(annotationmetadata metadata) { return true; } /** * return the appropriate {@link annotationattributes} from the * {@link annotationmetadata}. by default this method will return attributes for * {@link #getannotationclass()}. * @param metadata the annotation metadata * @return annotation attributes */ protected annotationattributes getattributes(annotationmetadata metadata) { string name = getannotationclass().getname(); annotationattributes attributes = annotationattributes .frommap(metadata.getannotationattributes(name, true)); assert.notnull(attributes, "no auto-configuration attributes found. is " + metadata.getclassname() + " annotated with " + classutils.getshortname(name) + "?"); return attributes; } /** * return the source annotation class used by the selector. * @return the annotation class */ protected class<?> getannotationclass() { return enableautoconfiguration.class; } /** * return the auto-configuration class names that should be considered. by default * this method will load candidates using {@link springfactoriesloader} with * {@link #getspringfactoriesloaderfactoryclass()}. * @param metadata the source metadata * @param attributes the {@link #getattributes(annotationmetadata) annotation * attributes} * @return a list of candidate configurations */ protected list<string> getcandidateconfigurations(annotationmetadata metadata, annotationattributes attributes) { list<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; } /** * return the class used by {@link springfactoriesloader} to load configuration * candidates. * @return the factory class */ protected class<?> getspringfactoriesloaderfactoryclass() { return enableautoconfiguration.class; } private void checkexcludedclasses(list<string> configurations, set<string> exclusions) { list<string> invalidexcludes = new arraylist<string>(exclusions.size()); for (string exclusion : exclusions) { if (classutils.ispresent(exclusion, getclass().getclassloader()) && !configurations.contains(exclusion)) { invalidexcludes.add(exclusion); } } if (!invalidexcludes.isempty()) { handleinvalidexcludes(invalidexcludes); } } /** * handle any invalid excludes that have been specified. * @param invalidexcludes the list of invalid excludes (will always have at least one * element) */ protected void handleinvalidexcludes(list<string> invalidexcludes) { stringbuilder message = new stringbuilder(); for (string exclude : invalidexcludes) { message.append("\t- ").append(exclude).append(string.format("%n")); } throw new illegalstateexception(string .format("the following classes could not be excluded because they are" + " not auto-configuration classes:%n%s", message)); } /** * return any exclusions that limit the candidate configurations. * @param metadata the source metadata * @param attributes the {@link #getattributes(annotationmetadata) annotation * attributes} * @return exclusions or an empty set */ protected set<string> getexclusions(annotationmetadata metadata, annotationattributes attributes) { set<string> excluded = new linkedhashset<string>(); excluded.addall(aslist(attributes, "exclude")); excluded.addall(arrays.aslist(attributes.getstringarray("excludename"))); excluded.addall(getexcludeautoconfigurationsproperty()); return excluded; } private list<string> getexcludeautoconfigurationsproperty() { if (getenvironment() instanceof configurableenvironment) { relaxedpropertyresolver resolver = new relaxedpropertyresolver( this.environment, "spring.autoconfigure."); map<string, object> properties = resolver.getsubproperties("exclude"); if (properties.isempty()) { return collections.emptylist(); } list<string> excludes = new arraylist<string>(); for (map.entry<string, object> entry : properties.entryset()) { string name = entry.getkey(); object value = entry.getvalue(); if (name.isempty() || name.startswith("[") && value != null) { excludes.addall(new hashset<string>(arrays.aslist(stringutils .tokenizetostringarray(string.valueof(value), ",")))); } } return excludes; } relaxedpropertyresolver resolver = new relaxedpropertyresolver(getenvironment(), "spring.autoconfigure."); string[] exclude = resolver.getproperty("exclude", string[].class); return (arrays.aslist(exclude == null ? new string[0] : exclude)); } private list<string> sort(list<string> configurations, autoconfigurationmetadata autoconfigurationmetadata) throws ioexception { configurations = new autoconfigurationsorter(getmetadatareaderfactory(), autoconfigurationmetadata).getinpriorityorder(configurations); return configurations; } private list<string> filter(list<string> configurations, autoconfigurationmetadata autoconfigurationmetadata) { long starttime = system.nanotime(); string[] candidates = configurations.toarray(new string[configurations.size()]); boolean[] skip = new boolean[candidates.length]; boolean skipped = false; for (autoconfigurationimportfilter filter : getautoconfigurationimportfilters()) { invokeawaremethods(filter); boolean[] match = filter.match(candidates, autoconfigurationmetadata); for (int i = 0; i < match.length; i++) { if (!match[i]) { skip[i] = true; skipped = true; } } } if (!skipped) { return configurations; } list<string> result = new arraylist<string>(candidates.length); for (int i = 0; i < candidates.length; i++) { if (!skip[i]) { result.add(candidates[i]); } } if (logger.istraceenabled()) { int numberfiltered = configurations.size() - result.size(); logger.trace("filtered " + numberfiltered + " auto configuration class in " + timeunit.nanoseconds.tomillis(system.nanotime() - starttime) + " ms"); } return new arraylist<string>(result); } protected list<autoconfigurationimportfilter> getautoconfigurationimportfilters() { return springfactoriesloader.loadfactories(autoconfigurationimportfilter.class, this.beanclassloader); } private metadatareaderfactory getmetadatareaderfactory() { try { return getbeanfactory().getbean( sharedmetadatareaderfactorycontextinitializer.bean_name, metadatareaderfactory.class); } catch (nosuchbeandefinitionexception ex) { return new cachingmetadatareaderfactory(this.resourceloader); } } protected final <t> list<t> removeduplicates(list<t> list) { return new arraylist<t>(new linkedhashset<t>(list)); } protected final list<string> aslist(annotationattributes attributes, string name) { string[] value = attributes.getstringarray(name); return arrays.aslist(value == null ? new string[0] : value); } private void fireautoconfigurationimportevents(list<string> configurations, set<string> exclusions) { list<autoconfigurationimportlistener> listeners = getautoconfigurationimportlisteners(); if (!listeners.isempty()) { autoconfigurationimportevent event = new autoconfigurationimportevent(this, configurations, exclusions); for (autoconfigurationimportlistener listener : listeners) { invokeawaremethods(listener); listener.onautoconfigurationimportevent(event); } } } protected list<autoconfigurationimportlistener> getautoconfigurationimportlisteners() { return springfactoriesloader.loadfactories(autoconfigurationimportlistener.class, this.beanclassloader); } private void invokeawaremethods(object instance) { if (instance instanceof aware) { if (instance instanceof beanclassloaderaware) { ((beanclassloaderaware) instance) .setbeanclassloader(this.beanclassloader); } if (instance instanceof beanfactoryaware) { ((beanfactoryaware) instance).setbeanfactory(this.beanfactory); } if (instance instanceof environmentaware) { ((environmentaware) instance).setenvironment(this.environment); } if (instance instanceof resourceloaderaware) { ((resourceloaderaware) instance).setresourceloader(this.resourceloader); } } } @override public void setbeanfactory(beanfactory beanfactory) throws beansexception { assert.isinstanceof(configurablelistablebeanfactory.class, beanfactory); this.beanfactory = (configurablelistablebeanfactory) beanfactory; } protected final configurablelistablebeanfactory getbeanfactory() { return this.beanfactory; } @override public void setbeanclassloader(classloader classloader) { this.beanclassloader = classloader; } protected classloader getbeanclassloader() { return this.beanclassloader; } @override public void setenvironment(environment environment) { this.environment = environment; } protected final environment getenvironment() { return this.environment; } @override public void setresourceloader(resourceloader resourceloader) { this.resourceloader = resourceloader; } protected final resourceloader getresourceloader() { return this.resourceloader; } @override public int getorder() { return ordered.lowest_precedence - 1; } }
首先该类实现了deferredimportselector接口,这个接口继承了importselector:
/* * copyright 2002-2013 the original author or authors. * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ package org.springframework.context.annotation; import org.springframework.core.type.annotationmetadata; /** * interface to be implemented by types that determine which @{@link configuration} * class(es) should be imported based on a given selection criteria, usually one or more * annotation attributes. * * <p>an {@link importselector} may implement any of the following * {@link org.springframework.beans.factory.aware aware} interfaces, and their respective * methods will be called prior to {@link #selectimports}: * <ul> * <li>{@link org.springframework.context.environmentaware environmentaware}</li> * <li>{@link org.springframework.beans.factory.beanfactoryaware beanfactoryaware}</li> * <li>{@link org.springframework.beans.factory.beanclassloaderaware beanclassloaderaware}</li> * <li>{@link org.springframework.context.resourceloaderaware resourceloaderaware}</li> * </ul> * * <p>importselectors are usually processed in the same way as regular {@code @import} * annotations, however, it is also possible to defer selection of imports until all * {@code @configuration} classes have been processed (see {@link deferredimportselector} * for details). * * @author chris beams * @since 3.1 * @see deferredimportselector * @see import * @see importbeandefinitionregistrar * @see configuration */ public interface importselector { /** * select and return the names of which class(es) should be imported based on * the {@link annotationmetadata} of the importing @{@link configuration} class. */ string[] selectimports(annotationmetadata importingclassmetadata); }
该接口主要是为了导入@configuration的配置项,而deferredimportselector是延期导入,当所有的@configuration都处理过后才会执行。
回过头来我们看一下autoconfigurationimportselector的selectimport方法:
@override public string[] selectimports(annotationmetadata annotationmetadata) { if (!isenabled(annotationmetadata)) { return no_imports; } try { autoconfigurationmetadata autoconfigurationmetadata = autoconfigurationmetadataloader .loadmetadata(this.beanclassloader); annotationattributes attributes = getattributes(annotationmetadata); list<string> configurations = getcandidateconfigurations(annotationmetadata, attributes); configurations = removeduplicates(configurations); configurations = sort(configurations, autoconfigurationmetadata); set<string> exclusions = getexclusions(annotationmetadata, attributes); checkexcludedclasses(configurations, exclusions); configurations.removeall(exclusions); configurations = filter(configurations, autoconfigurationmetadata); fireautoconfigurationimportevents(configurations, exclusions); return configurations.toarray(new string[configurations.size()]); } catch (ioexception ex) { throw new illegalstateexception(ex); } }
该方法刚开始会先判断是否进行自动装配,而后会从meta-inf/spring-autoconfigure-metadata.properties读取元数据与元数据的相关属性,紧接着会调用getcandidateconfigurations方法:
/** * return the auto-configuration class names that should be considered. by default * this method will load candidates using {@link springfactoriesloader} with * {@link #getspringfactoriesloaderfactoryclass()}. * @param metadata the source metadata * @param attributes the {@link #getattributes(annotationmetadata) annotation * attributes} * @return a list of candidate configurations */ protected list<string> getcandidateconfigurations(annotationmetadata metadata, annotationattributes attributes) { list<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; } /** * return the class used by {@link springfactoriesloader} to load configuration * candidates. * @return the factory class */ protected class<?> getspringfactoriesloaderfactoryclass() { return enableautoconfiguration.class; }
在这里又遇到我们的老熟人了--springfactoryiesloader, 它会读取meta-inf/spring.factories下的enableautoconfiguration的配置,紧接着在进行排除与过滤,进而得到需要装配的类。最后让所有配置在meta-inf/spring.factories下的autoconfigurationimportlistener执行autoconfigurationimportevent事件,代码如下:
private void fireautoconfigurationimportevents(list<string> configurations, set<string> exclusions) { list<autoconfigurationimportlistener> listeners = getautoconfigurationimportlisteners(); if (!listeners.isempty()) { autoconfigurationimportevent event = new autoconfigurationimportevent(this, configurations, exclusions); for (autoconfigurationimportlistener listener : listeners) { invokeawaremethods(listener); listener.onautoconfigurationimportevent(event); } } } protected list<autoconfigurationimportlistener> getautoconfigurationimportlisteners() { return springfactoriesloader.loadfactories(autoconfigurationimportlistener.class, this.beanclassloader); }
二、何时进行自动装配
在前面的环节里只是最终要确定哪些类需要被装配,在springboot时何时处理这些自动装配的类呢?下面我们简要的分析一下:
2.1、abstractapplicationcontext的refresh方法:
这个方法老生常谈了其中请大家关注一下这个方法:
// invoke factory processors registered as beans in the context. invokebeanfactorypostprocessors(beanfactory);
在这里是处理beanfactorypostprocessor的,那么我们在来看一下这个接口beandefinitionregistrypostprocessor:
/* * copyright 2002-2010 the original author or authors. * * licensed under the apache license, version 2.0 (the "license"); * you may not use this file except in compliance with the license. * you may obtain a copy of the license at * * http://www.apache.org/licenses/license-2.0 * * unless required by applicable law or agreed to in writing, software * distributed under the license is distributed on an "as is" basis, * without warranties or conditions of any kind, either express or implied. * see the license for the specific language governing permissions and * limitations under the license. */ package org.springframework.beans.factory.support; import org.springframework.beans.beansexception; import org.springframework.beans.factory.config.beanfactorypostprocessor; /** * extension to the standard {@link beanfactorypostprocessor} spi, allowing for * the registration of further bean definitions <i>before</i> regular * beanfactorypostprocessor detection kicks in. in particular, * beandefinitionregistrypostprocessor may register further bean definitions * which in turn define beanfactorypostprocessor instances. * * @author juergen hoeller * @since 3.0.1 * @see org.springframework.context.annotation.configurationclasspostprocessor */ public interface beandefinitionregistrypostprocessor extends beanfactorypostprocessor { /** * modify the application context's internal bean definition registry after its * standard initialization. all regular bean definitions will have been loaded, * but no beans will have been instantiated yet. this allows for adding further * bean definitions before the next post-processing phase kicks in. * @param registry the bean definition registry used by the application context * @throws org.springframework.beans.beansexception in case of errors */ void postprocessbeandefinitionregistry(beandefinitionregistry registry) throws beansexception; }
该接口继承了beanfactorypostprocessor。
2.2、configurationclasspostprocessor 类
该类主要处理@configuration注解的,它实现了beandefinitionregistrypostprocessor, 那么也间接实现了beanfactorypostprocessor,关键代码如下:
@override public void postprocessbeanfactory(configurablelistablebeanfactory beanfactory) { int factoryid = system.identityhashcode(beanfactory); if (this.factoriespostprocessed.contains(factoryid)) { throw new illegalstateexception( "postprocessbeanfactory already called on this post-processor against " + beanfactory); } this.factoriespostprocessed.add(factoryid); if (!this.registriespostprocessed.contains(factoryid)) { // beandefinitionregistrypostprocessor hook apparently not supported... // simply call processconfigurationclasses lazily at this point then. processconfigbeandefinitions((beandefinitionregistry) beanfactory); } enhanceconfigurationclasses(beanfactory); beanfactory.addbeanpostprocessor(new importawarebeanpostprocessor(beanfactory)); } /** * build and validate a configuration model based on the registry of * {@link configuration} classes. */ public void processconfigbeandefinitions(beandefinitionregistry registry) { //.....省略部分代码 // parse each @configuration class configurationclassparser parser = new configurationclassparser( this.metadatareaderfactory, this.problemreporter, this.environment, this.resourceloader, this.componentscanbeannamegenerator, registry); set<beandefinitionholder> candidates = new linkedhashset<beandefinitionholder>(configcandidates); set<configurationclass> alreadyparsed = new hashset<configurationclass>(configcandidates.size()); do { parser.parse(candidates); parser.validate(); set<configurationclass> configclasses = new linkedhashset<configurationclass>(parser.getconfigurationclasses()); configclasses.removeall(alreadyparsed); // read the model and create bean definitions based on its content if (this.reader == null) { this.reader = new configurationclassbeandefinitionreader( registry, this.sourceextractor, this.resourceloader, this.environment, this.importbeannamegenerator, parser.getimportregistry()); } this.reader.loadbeandefinitions(configclasses); alreadyparsed.addall(configclasses); candidates.clear(); if (registry.getbeandefinitioncount() > candidatenames.length) { string[] newcandidatenames = registry.getbeandefinitionnames(); set<string> oldcandidatenames = new hashset<string>(arrays.aslist(candidatenames)); set<string> alreadyparsedclasses = new hashset<string>(); for (configurationclass configurationclass : alreadyparsed) { alreadyparsedclasses.add(configurationclass.getmetadata().getclassname()); } for (string candidatename : newcandidatenames) { if (!oldcandidatenames.contains(candidatename)) { beandefinition bd = registry.getbeandefinition(candidatename); if (configurationclassutils.checkconfigurationclasscandidate(bd, this.metadatareaderfactory) && !alreadyparsedclasses.contains(bd.getbeanclassname())) { candidates.add(new beandefinitionholder(bd, candidatename)); } } } candidatenames = newcandidatenames; } } while (!candidates.isempty()); // ....省略部分代码 }
其实这里注释已经很清楚了,我们可以清楚的看到解析每一个@configurationclass的关键类是:configurationclassparser,那么我们继续看一看这个类的parse方法:
public void parse(set<beandefinitionholder> configcandidates) { this.deferredimportselectors = new linkedlist<deferredimportselectorholder>(); for (beandefinitionholder holder : configcandidates) { beandefinition bd = holder.getbeandefinition(); try { if (bd instanceof annotatedbeandefinition) { parse(((annotatedbeandefinition) bd).getmetadata(), holder.getbeanname()); } else if (bd instanceof abstractbeandefinition && ((abstractbeandefinition) bd).hasbeanclass()) { parse(((abstractbeandefinition) bd).getbeanclass(), holder.getbeanname()); } else { parse(bd.getbeanclassname(), holder.getbeanname()); } } catch (beandefinitionstoreexception ex) { throw ex; } catch (throwable ex) { throw new beandefinitionstoreexception( "failed to parse configuration class [" + bd.getbeanclassname() + "]", ex); } } processdeferredimportselectors(); }
在这里大家留意一下最后一句processdeferredimportselectors方法,在这里将会对deferredimportselector进行处理,这样我们就和autoconfigurationselectimporter结合到一起了:
private void processdeferredimportselectors() { list<deferredimportselectorholder> deferredimports = this.deferredimportselectors; this.deferredimportselectors = null; collections.sort(deferredimports, deferred_import_comparator); for (deferredimportselectorholder deferredimport : deferredimports) { configurationclass configclass = deferredimport.getconfigurationclass(); try { string[] imports = deferredimport.getimportselector().selectimports(configclass.getmetadata()); processimports(configclass, assourceclass(configclass), assourceclasses(imports), false); } catch (beandefinitionstoreexception ex) { throw ex; } catch (throwable ex) { throw new beandefinitionstoreexception( "failed to process import candidates for configuration class [" + configclass.getmetadata().getclassname() + "]", ex); } } }
请大家关注这句代码:string[] imports = deferredimport.getimportselector().selectimports(configclass.getmetadata());在这里deferredimport的类型为deferredimportselectorholder:
private static class deferredimportselectorholder { private final configurationclass configurationclass; private final deferredimportselector importselector; public deferredimportselectorholder(configurationclass configclass, deferredimportselector selector) { this.configurationclass = configclass; this.importselector = selector; } public configurationclass getconfigurationclass() { return this.configurationclass; } public deferredimportselector getimportselector() { return this.importselector; } }
在这个内部类里持有了一个deferredimportselector的引用,至此将会执行自动装配的所有操作
三、总结
1)自动装配还是利用了springfactoriesloader来加载meta-inf/spring.factoires文件里所有配置的enableautoconfgruation,它会经过exclude和filter等操作,最终确定要装配的类
2) 处理@configuration的核心还是configurationclasspostprocessor,这个类实现了beanfactorypostprocessor, 因此当abstractapplicationcontext执行refresh方法里的invokebeanfactorypostprocessors(beanfactory)方法时会执行自动装配
以上所述是小编给大家介绍的springboot中的自动装配,希望对大家有所帮助