欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

深入浅析SpringBoot中的自动装配

程序员文章站 2023-12-12 14:07:52
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中的自动装配,希望对大家有所帮助

上一篇:

下一篇: