AutowireCapableBeanFactory探密(2)——传统装配模式与现代注解驱动注入方式

回顾

上篇,介绍了AutowireCapableBeanFactory的作用:让Spring管理的Bean去装配和填充那些不被Spring托管的Bean,为第三方框架赋能。其中,介绍AutowireCapableBeanFactory接口时简单提了一下该接口定义了6个属性,其中有5个(AUTOWIRE_*)是与autowireMode相关的常量值定义,用于描述该用何种方式来进行装配Bean

正文

这里有个容易让人误解的点,就是关于autowireMode的各个常量的命名。其中有两个常量定义分别是int AUTOWIRE_BY_NAME = 1int AUTOWIRE_BY_TYPE = 2,从命名上看跟我们最常用的@Resouce、@Autowired这种注解使用的方式很像,但实际上二者八杆子打不着关系,千万不要把他们混为一谈

实际上,注解注入方式在Spring中被称为annotation-driven injection,而autowireMode这种装配在Spring里其实被称为traditional autowiring

autowireMode之所以称为traditional,是因为这种使用姿势已经足够老,老到新一代的Javaer,甚至都没有在项目中用过这种装配模式,上来就是注解也能很好地work,因此,traditional autowiring在Spring 2.5后已式微,大概只有在古董项目上才能窥之身影

先来看一下,Spring 2.5以前项目里如何使用traditional autowiring

定义案例需要用到的类FooServiceBarService,其中FooService依赖BarService

public class FooService {
    private BarService barService;

    public void setBarService(BarService barService) {
        this.barService = barService;
    }
}

public class BarService {
}

在很久很久以前,那时候Spring还没有大量使用注解,也没有提供各类注解给应用层使用,各类配置项在都是依靠XML进行配置,如下所示


    



需要用户在XML配置文件中定义Spring需要管理哪些Bean,并通过手动装配方式指定依赖关系。如果只有两个Bean以及它们之间的依赖关系,这种方式也很OK,但如果有大量的Bean呢,都要手动指定那得多麻烦枯燥

byName

懒惰是科技进步的动力,程序员天性懒惰但又很聪明,想出了自动装配的方式,如下所示:




这样,无论FooService里依赖多少其它Bean,都不再需要手动一个个装配,通过autowire="byName"的方式,Spring 就会在构建FooService之后,内过Java Bean内省机制拿到Bean里所有的propertyName(案例中的propertyName:barService),然后上Spring IoC容器中找到beanName为propertyName的bean(barService)并将之注入,完成装配。这种方式要求propertyName与beanName一致,故命名为byName

byType

如果想要想打破上面的限制,不要求propertyName与beanName一致也能完成注入,可以配置为autowire="byType",如下所示:




甚至将FooService对BarService的依赖改的"面目全非"(此处,propertyName:quz)

public class FooService {
    private BarService xxx;

    public void setQuz(BarService xxx) {
        this.xxx = xxx;
    }
}

将BarService的bean id设置为baz,尽管与FooService中的propertyName(quz)不一致,但通过指定autowire="byType",依然能够将barService注入到fooService实例中。

原理是: Spring 在构建FooService之后,内过Java Bean内省机制拿到Bean里所有的propertyName(案例中的propertyName:quz),最终拿到DependencyDescriptor(依赖描述符,它描述了一个待注入的依赖信息:要么是构造器参数,要么是方法参数,要么是字段,并且提供了非常友好的、一种统一的方式去访问)。在本案例中,DependencyDescriptor描述的是:我的名字叫setQuz,是在FooService类中被声名的(declaringClass),需要一个入参且其类型是BarService(parameterTypes、resolvableType)。Spring在IoC容器中找到type为BarService的bean返回并注入,完成装配。这种方式只要求待注入的参数,其类型能在Spring里找到,故命名为byType

小总结:无论是byName还是byType,Spring都是通过Java Bean的内省机制找到property,然后上IoC容器中找到对应的Bean来完成的注入,这也就是古董项目重充斥大量setter方法的缘由

简单看一下源码中,处理byNamebyType的地方,依然是在populateBean方法内部

// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean

//如果是byName或byType
if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME || mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
	MutablePropertyValues newPvs = new MutablePropertyValues(pvs);
	// Add property values based on autowire by name if applicable.
	if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_NAME) {
		autowireByName(beanName, mbd, bw, newPvs);
	}
	// Add property values based on autowire by type if applicable.
	if (mbd.getResolvedAutowireMode() == AUTOWIRE_BY_TYPE) {
		autowireByType(beanName, mbd, bw, newPvs);
	}
	pvs = newPvs;
}
// ...(省略)[省略的内容就包括处理@Resouce、@Autowired注解驱动注入的部分]

// 上面是收集属性到pvs,并不执行属性装配的动作,下边才真正执行
if (pvs != null) {
	applyPropertyValues(beanName, mbd, bw, pvs);
}

通过判断autowireMode是byName还是byType,分别进入autowireByNameautowireByType方法,将待装配的对象收集到pvs,最后再通过applyPropertyValues真正执行属性装配。此处再次提醒:该段逻辑与处理@Resouce、@Autowired的注解驱动注入无关,省略部分是上篇内容,即上篇内容提到ibp.postProcessProperties才是处理annotation-driven injection的逻辑的地方,且真正执行了注入的逻辑

pvs是MutablePropertyValues类型实例,该类主要职责是维护内部的PropertyValue集合,实现了PropertyValues接口,提供对集合的增删查操作

public class MutablePropertyValues implements PropertyValues, Serializable {

    private final List propertyValueList;
    // ...(省略)
}

public interface PropertyValues extends Iterable

PropertyValue代表的是某个Bean属性的属性名与属性值,类似于一个键值对

public class PropertyValue extends BeanMetadataAttributeAccessor implements Serializable {

	private final String name;

	@Nullable
	private final Object value;
	// ...(省略)
}

因此,pvs也即List代表了某个Bean里所有的属性名及属性值

constructor

byNamebyType都是setter方法注入,接着看看autowireMode = constructor构造器装配使用姿势

public class FooService {
    private BarService barService;

    public FooService(BarService barService) {
        this.barService = barService;
    }
}



通过在FooService中定入参为BarService的构造函数,在定义Spring bean时指定autowire="constructor",那么Spring在构造FooService实例时,就会找到该构造函数,并从IoC容器中找到类型为BarService的bean,传入构造函数中进行构造,如此,生成的FooService实例就自动注入了BarService

原理涉及的核心代码如下:

// org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBeanInstance

Constructor[] ctors = determineConstructorsFromBeanPostProcessors(beanClass, beanName);
// 如果找到合适的构造器或者autowireMode = AUTOWIRE_CONSTRUCTOR 则执行autowireConstructor
if (ctors != null || mbd.getResolvedAutowireMode() == AUTOWIRE_CONSTRUCTOR ||
		mbd.hasConstructorArgumentValues() || !ObjectUtils.isEmpty(args)) {
	return autowireConstructor(beanName, mbd, ctors, args);
}

这里有一点特殊的地方值得一提:本案例中不配置autowire="constructor",也能够通过构造器进行装配,原因在于determineConstructorsFromBeanPostProcessors的返回值如果不为空,表明找到了合适的构造器,也会进入autowireConstructor中执行构造器装配

// org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#determineCandidateConstructors

// ...(省略)
if (!candidates.isEmpty()) { 
	// 与@Autowired、@Value 注入有关,不是本节重点,暂时忽略
	// Add default constructor to list of optional constructors, as fallback.
	if (requiredConstructor == null) {
		if (defaultConstructor != null) {
			candidates.add(defaultConstructor);
		}
		else if (candidates.size() == 1 && logger.isInfoEnabled()) {
			// ...(省略,log信息)
		}
	}
	candidateConstructors = candidates.toArray(new Constructor[0]);
}
else if (rawCandidates.length == 1 && rawCandidates[0].getParameterCount() > 0) {
	candidateConstructors = new Constructor[] {rawCandidates[0]}; // 有且只有一个非空参构造函数
}
// ...(省略两个else if,它们与kotlin相关,跳过不看)
else {
	candidateConstructors = new Constructor[0];
}

return (candidateConstructors.length > 0 ? candidateConstructors : null);

在目前的Spring实现中,AutowiredAnnotationBeanPostProcessor实现了determineCandidateConstructors方法,如果找到有且只有一个非空参构造器,那么就可以成功返回
,很显然,我们的FooService符合这样的条件,因此返回public FooService(BarService barService)构造器,进入autowireConstructor中执行构造器装配

假如给FooService再添加一个空参构造器,或者别的带参构造器,就不再满足上述条件,需要配置autowire="constructor"才能实现构造器注入

AUTODETECT

该装配模式是byTypeconstructor的混合体,从Spring 3.0已经标识为Deprecated,Spring认为,如果你不想关注到底是哪种autowireMode,就想闭着眼睛让Spring帮忙注入,那就用注解注入的方式吧,这样同样实现效果的同时,也能让注入语义更清晰一些

// org.springframework.beans.factory.support.AbstractBeanDefinition#getResolvedAutowireMode

public int getResolvedAutowireMode() {
	if (this.autowireMode == AUTOWIRE_AUTODETECT) {
		// Work out whether to apply setter autowiring or constructor autowiring.
		// If it has a no-arg constructor it's deemed to be setter autowiring,
		// otherwise we'll try constructor autowiring.
		Constructor[] constructors = getBeanClass().getConstructors();
		for (Constructor constructor : constructors) {
			if (constructor.getParameterCount() == 0) {
				return AUTOWIRE_BY_TYPE;
			}
		}
		return AUTOWIRE_CONSTRUCTOR;
	}
	else {
		return this.autowireMode;
	}
}

判断的逻辑很简单,如果存在空参构造函数,就认为是想用byType的装配模式,否则就是constructor的构造模式

由于Spring 已经将这种autowireMode标识为Deprecated,且除了稍显简便也没别的其他优势,我们还是能不用则不用

AUTOWIRE_NO

默认的autowireMode就是不装配,无特殊行为,不作过多介绍

到了Spring 3.0,XML配置方式已经开始展现颓势,官方推出了Annotation Config,搭配@Configuration、@Bean、@DependsOn等注解,作为XML的等价配置方式,试图取代XML的配置地位,从今日的结果看来,这一目的已经达成,新项目中基本不再出现旧的配置方式

简单介绍一下Annotation Config如何配置autowireMode,以下两种形式是等价的:




@Bean(autowire = Autowire.BY_NAME)
public FooService fooService() {
    return new FooService();
}

@Bean
public BarService BarService() {
    return new BarService();
}

@Bean注解有一个autowire属性,取值类似于autowireMode,不过在此处它的取值只有三个:NO、BY_NAME、BY_TYPE,没有CONSTRUCT、AUTODETECT,简单想一下可知道,Annotation Config的配置方式不适合CONSTRUCT,以及可能包含CONSTRUCT方式的AUTODETECT。
无论XML配置还是Annotaion Config配置autowireMode,它们的底层原理都一样,最终都是给AbstractBeanDefinitionautowireMode属性赋值,因此在真正执行装配时都是判断BeanDefinition的autowireMode属性值来决定装配模式,默认取值: AUTOWIRE_NO

此外,上篇文章提到的AutowireCapableBeanFactory有很多细粒度控制Bean生命周期的方法,如autowire、autowireBeanProperties等带有autowireMode参数,作用与上面介绍的是一样的,最终都落实到AbstractBeanDefinitionautowireMode属性上,也会将待装配对象收集到pvs,最后调用applyPropertyValues方法执行装配


以上便是传统装配模式的介绍,接下来将介绍现代注解驱动注入方式

Annotation-driven injection的方式,在目前的应用开发中被广泛的运用,打开任意一个三层模型(Controller-Service-Dao)的Java Web项目,在各层总是能看到@Resouce、@Autowired、@Value、@Inject(javax.inject.Inject),甚至是自定义注入注解(AutowiredAnnotationBeanPostProcessor#setAutowiredAnnotationType提供这个能力,不需要太多工作量,很迅速地完成自定义),Spring赋予了这些注解DI的能力 ,总体上可作用于Field,Setter Method,Construct,Parameter,AnnotationType(不同注解作用的范围并不一致),再加上注解自身能够配置的属性、与其它注解的配合(如@Qualifier)使用,能玩出的花样真是不胜枚举。他们之间的功能有重叠有交叉,不必要每一个都掌握,徒增精力,只掌握最小子集的注解又可涵盖注解功能的总和,满足任何场景的开发即可。故此,本节探讨一下大概率使用最多的@Resouce、@Autowired在字段注入的场景

@Resouce

案例代码如下:

@Service
public class FooService {
    @Resource
    private BarService barService;
}

@Service
public class BarService {}

Spring 处理@Resouce体现在Spring Bean Lifecycle的两个阶段

  1. 构建Bean实例之后,填充属性之前,收集Bean里面所有@Resouce相关的信息并缓存起来,注意,此阶段是作信息收集之用,不作具体处理(CommonAnnotationBeanPostProcessor#postProcessMergedBeanDefinition)
  2. 在填充属性的过程中,将待装配的对象收集到pvs后,执行applyPropertyValues之前(执行传统的属性装配之前),将第1阶段收集到的信息利用起来,执行Annotation-driven injection(CommonAnnotationBeanPostProcessor#postProcessProperties)

先看第一阶段,收集待注入的元信息

// org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#postProcessMergedBeanDefinition

@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class beanType, String beanName) {
	super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
	// 收集待注入的元信息(@Resouce相关信息)
	InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);
	metadata.checkConfigMembers(beanDefinition);
}

private InjectionMetadata findResourceMetadata(String beanName, final Class clazz, @Nullable PropertyValues pvs) {
	// ...(省略)
	// 构建待注入元信息,并放入缓存
	metadata = buildResourceMetadata(clazz);
	this.injectionMetadataCache.put(cacheKey, metadata);

	return metadata;
}

// 构建待注入元信息
private InjectionMetadata buildResourceMetadata(final Class clazz) {
	List elements = new ArrayList<>();
	Class targetClass = clazz;

	do {
		final List currElements = new ArrayList<>();

		// 通过反射查看class所有field上是否含有@Resouce注解,找到后就封装成ResourceElement
		ReflectionUtils.doWithLocalFields(targetClass, field -> {
			// ...(省略)
			else if (field.isAnnotationPresent(Resource.class)) {
				if (Modifier.isStatic(field.getModifiers())) {
					throw new IllegalStateException("@Resource annotation is not supported on static fields");
				}
				if (!this.ignoredResourceTypes.contains(field.getType().getName())) {
					currElements.add(new ResourceElement(field, field, null));
				}
			}
		});
		// ...(下边是收集方法注入方式的元信息,省略)

		elements.addAll(0, currElements);
		targetClass = targetClass.getSuperclass();
	}
	while (targetClass != null && targetClass != Object.class);

	return new InjectionMetadata(clazz, elements);
}

通过源码可以看到,最后待注入的元信息其实是ResourceElement,看着看一下ResourceElement的构造函数做了些什么事情

// 从上面的buildResourceMetadata可以看出,member、ae都是field
public ResourceElement(Member member, AnnotatedElement ae, @Nullable PropertyDescriptor pd) {
	super(member, pd);
	Resource resource = ae.getAnnotation(Resource.class);
	String resourceName = resource.name();
	Class resourceType = resource.type();
	this.isDefaultName = !StringUtils.hasLength(resourceName);
	
	// 处理resourceName
	if (this.isDefaultName) {
	    // 如果使用@Resource时未指定name,默认取member的name做为resouceName
	    // 对于member为field的case,取的field name
		resourceName = this.member.getName();
		
		//如果member是个方法,代表setter注入,那就把setXxx前的set去掉后将首字段小写作为resouceName,即xxx
		if (this.member instanceof Method && resourceName.startsWith("set") && resourceName.length() > 3) {
			resourceName = Introspector.decapitalize(resourceName.substring(3));
		}
	}
	else if (embeddedValueResolver != null) {
		// 如果手动指定resoureName了,就使用embeddedValueResolver.resolveStringValue去解析
		resourceName = embeddedValueResolver.resolveStringValue(resourceName);
	}
	
	// 处理resouceType
	if (Object.class != resourceType) {
	    // 如果手动指了定resouceType,需要检查一下是不是瞎指定,比如field明明是个A,却通过resouceType指定为B type,那明显会有问题
		checkResourceType(resourceType);
	}
	else {
		// No resource type specified... check field/method.
		// 如果未手动指定resourceType,则取member或者pd的类型
		resourceType = getResourceType();
	}
	this.name = (resourceName != null ? resourceName : "");
	this.lookupType = resourceType;
	String lookupValue = resource.lookup();
	this.mappedName = (StringUtils.hasLength(lookupValue) ? lookupValue : resource.mappedName());
	Lazy lazy = ae.getAnnotation(Lazy.class);
	this.lazyLookup = (lazy != null && lazy.value());
}

构造函数主要就是给name、lookupType、lookupValue等属性赋值,其中name为resourceName,lookupType为resourceType,而且一般都不会为空,即便我们没有通过注解手动指定它们的值,也有默认的方式获取resourceName跟resourceType,这也就是我们在多数场景直接打上注解就完事,而不指定具体参数的原因

这种设计原则值得借鉴与学习:如果我们需要给他人提供某项能力,出于优雅设计的原则,首先调研、考虑大多数场景怎样使用,我们将这类应用的场景以代码的形式固化下来,当发生API调用时不需要提供过多参数也可以正常运行,针对另外一小部分需要自定义的场景,通过提供一种optional的能力,允许用户选填,选填项优先级高于固化代码的方式,就能覆盖100%的场景。这也是一种"约定优于配置(convention over configuration)"的思想实践

另外,如果我们通过注解的属性手动指定了name,会走resourceName = embeddedValueResolver.resolveStringValue(resourceName);这段逻辑,embeddedValueResolver是EmbeddedValueResolver类的实例,该类特别强大,不但能够解析"占位符",还能够解析"Spring EL"表达式,简言之,@Value里能配置啥值,在这就能配啥值,而且解析的效果是一样的。因此,如下的方式也是与上边的方式等下的

// application.properties
quz = barService

// ----------
public class FooService {
    @Resource(name = "${quz}")
    private BarService barService;
}

Spring 的设计真是惊为天人,考虑到各种我们开发过程中可能使用到的姿势并且提供了能力,真是让人佩服五体投地

再看第二阶段,执行真正的注入逻辑

// org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#postProcessProperties

public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
	// 该方法上面出现过,第一次出现的时候是构建并且放入缓存,这是第二次出现 ,直接从缓存中获取
	// ResourceElement是InjectionMetadata的子类,因此metadata其实是ResourceElement的实例
	InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
	try {
		// 执行注入
		metadata.inject(bean, beanName, pvs);
	}
	catch (Throwable ex) {
		throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);
	}
	return pvs;
}

// org.springframework.beans.factory.annotation.InjectionMetadata#inject

public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
	Collection checkedElements = this.checkedElements;
	Collection elementsToIterate =
			(checkedElements != null ? checkedElements : this.injectedElements);
	if (!elementsToIterate.isEmpty()) {
	    // 循环每一个待注入的元素,执行注入
		for (InjectedElement element : elementsToIterate) {
			if (logger.isTraceEnabled()) {
				logger.trace("Processing injected element of bean '" + beanName + "': " + element);
			}
			// 注入
			element.inject(target, beanName, pvs);
		}
	}
}

通过反射执行注入逻辑field.set(target, getResourceToInject(target, requestingBeanName));


protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs)
		throws Throwable {

	if (this.isField) {
		Field field = (Field) this.member;
		ReflectionUtils.makeAccessible(field);
		field.set(target, getResourceToInject(target, requestingBeanName));
	}
	// ...(方法注入,省略)
}

接着看getResourceToInject(target, requestingBeanName),获取待注入对象实例

// org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.ResourceElement#getResourceToInject

protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
    // 我们并没有配置@Lazy注解,因此走`getResource(this, requestingBeanName)`逻辑
	return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) :
			getResource(this, requestingBeanName));
}

protected Object getResource(LookupElement element, @Nullable String requestingBeanName)
		throws NoSuchBeanDefinitionException {
	// ...(省略)
	// resourceFactory是BeanFactory的实例,在构建CommonAnnotationBeanPostProcessor实例的时候通过BeanFactoryAware#setBeanFactory回调接口注入进来
	return autowireResource(this.resourceFactory, element, requestingBeanName);
}
// org.springframework.context.annotation.CommonAnnotationBeanPostProcessor#autowireResource

protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName)
			throws NoSuchBeanDefinitionException {

	Object resource;
	Set autowiredBeanNames;
	String name = element.name;

	// factory是DefaultListableBeanFactory的实例对象,因此也就是AutowireCapableBeanFactory的实例对象
	if (factory instanceof AutowireCapableBeanFactory) { // (if 1)
		AutowireCapableBeanFactory beanFactory = (AutowireCapableBeanFactory) factory;
		DependencyDescriptor descriptor = element.getDependencyDescriptor();
		// fallbackToDefaultTypeMatch默认值为true
		if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) { // (if 2)
			autowiredBeanNames = new LinkedHashSet<>();
			// resouce为解析出来的待注入实例对象
			resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);
			if (resource == null) {
				throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");
			}
		}
		else { // (else 1)
			// resouce为解析出来的待注入实例对象
			resource = beanFactory.resolveBeanByName(name, descriptor);
			autowiredBeanNames = Collections.singleton(name);
		}
	}
	else { // (else 2)
		// 基本上不会考到这
		resource = factory.getBean(name, element.lookupType);
		autowiredBeanNames = Collections.singleton(name);
	}

	// factory是DefaultListableBeanFactory的实例对象,因此也就是ConfigurableBeanFactory的实例对象
	if (factory instanceof ConfigurableBeanFactory) { // (if 3)
		ConfigurableBeanFactory beanFactory = (ConfigurableBeanFactory) factory;
		for (String autowiredBeanName : autowiredBeanNames) {
			if (requestingBeanName != null && beanFactory.containsBean(autowiredBeanName)) {
				// 将依赖关系注册到Spring中,是个双向的描述
				// 即dependentBeanMap、dependenciesForBeanMap
				beanFactory.registerDependentBean(autowiredBeanName, requestingBeanName);
			}
		}
	}

	return resource;
}

factory是DefaultListableBeanFactory的实例对象,因此也就是AutowireCapableBeanFactory、ConfigurableBeanFactory的实例对象,所以if 1、if 3都会进去,if 1逻辑用于解析待注入的bean,if 3逻辑是维护依赖Bean与被依赖Bean之间的关系

if 2代码块中,有个名为fallbackToDefaultTypeMatch的属性,它表示的含义是:当未通过注解手动指定name(意味着使用defaultName,即字段名或属性名),Spring根据defaultName上Ioc容器找不到Bean时,是否需要fallback,根据type到Ioc容器中找,默认值为true

因此进入if 2块需同时满足以下三个条件:

  1. fallbackToDefaultTypeMatch = true(默认值是true)
  2. element.isDefaultName = true(不能通过注解指定name)
  3. factory.containsBean(name) = false(根据name在Ioc容器中找不到Bean)

否则,直接进入else 1的逻辑: resource = beanFactory.resolveBeanByName(name, descriptor);

fallbackToDefaultTypeMatch默认true,一般也不会改为false,忽略这种情况不考虑,那进入else 1的情况有2个:

  1. 通过注解手动指定name,如@Resource(name = “quz”)
  2. IoC中包含beanName = name的Bean或者BeanDefinition

在本案例中,由于factory.containsBean(“barService”) = true,因此进入的是else 1的逻辑

网上有说法:

@Resouce默认根据name进行注入,如果找不到就根据type进行注入

其实这种说法不够严谨,也不太准确,能成立的前提条件是:name是fieldName或者propertyName,即未通过注解进行明确指定。如若通过注解手动指定name,就会走else 1逻辑,压根不会根据type进行注入

由于name、type都可以手动指定或不指定,因此出现4种排列组合的情况:

  1. 不指定name与type: 通过fileName或propertyName判断Ioc中是否存在,存在进入else 1 ,不存在就通过type去找,找不到或找到多个都抛异常(if 2)
  2. 仅指定name: 通过name找唯一的Bean,找不到抛异常(else 1)
  3. 仅指定type: 通过type找唯一的Bean,找不到或找到多个都抛异常(if 2)
  4. 指定name、指定type: 通过name找唯一的Bean,找不到抛异常(else 1)

注: 不建议把这4个排列组合的结论记下来,而是记住原理,遇到问题能够根据原理分析出来原因并加以解决

if 2、else 1都是将待注入的Bean解析出来,所谓解析就是在Spring容器中完成Bean的构造、属性填充、初始化等,然后返回一个能用的Bean。由于解析Bean是一个比较复杂的过程,涉及的知识点很多,不在本篇讨论的重点,暂且按下不表。只要知道它能返回一个Bean即可,接着,拿着这个返回结果(待注入对象)就可以完成注入

@Autowired

Spring 处理@Autowired同样体现在Spring Bean Lifecycle的两个阶段,与@Resouce处理方式如出一辙,甚至它们之间的代码有大部分都是相似的,因此熟悉@Resouce的处理流程之后,再来看@Autowired的处理代码,简直不能再轻松

第一阶段,收集待注入的元信息(InjectionMetadata)

AutowiredAnnotationBeanPostProcessor#postProcessMergedBeanDefinition -> findAutowiringMetadata -> buildAutowiringMetadata

第二阶段,执行真正的注入逻辑

AutowiredAnnotationBeanPostProcessor#postProcessProperties -> findAutowiringMetadata -> InjectionMetadata#inject -> AutowiredFieldElement#inject
// org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.AutowiredFieldElement#inject

protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
	Field field = (Field) this.member;
	Object value;
	if (this.cached) {
		value = resolvedCachedArgument(beanName, this.cachedFieldValue);
	}
	else {
		DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
		desc.setContainingClass(bean.getClass());
		Set autowiredBeanNames = new LinkedHashSet<>(1);
		Assert.state(beanFactory != null, "No BeanFactory available");
		TypeConverter typeConverter = beanFactory.getTypeConverter();
		try {
			// value为解析出来的待注入实例对象
			value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
		}
		catch (BeansException ex) {
			throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
		}
		// ...(省略)
	}
	if (value != null) {
		ReflectionUtils.makeAccessible(field);
		field.set(bean, value);
	}
}

还是同样的配方,还是熟悉的味道,不过过程更简单了,因为没有name、type的属性,而是将field包装成DependencyDescriptor,通过beanFactory.resolveDependency根据类型解析出来待注入的实例对象,然后通过反射直接注入

思考

了解到传统装配模式现代注解驱动注入方式的区别之后,我们在具体开发过程中,该如何抉择呢?很显然,Spring的方式是在引导我们向Annotation-driven Injection靠拢,也就是在正常的业务代码中应该使用现代注解驱动注入的方式,这种方式已经是大家约定俗成的习惯用法,是行业共识,没有太多的勾通成本。实际上,在Annotation Config中使用autowireMode也是不被建议的,从Spring 5.1开始,@Bean注解的autowire属性已经被标识为Deprecated,文档上提到这种方式已经被@Autowired注解所取代

那么是否意味着traditional autowiring就应该被打入冷宫,不应该使用了呢?其实不是的,至少从Spring 5.1看来,AbstractBeanDefinition的autowireMode还没有被标识为过期,AutowireCapableBeanFactory的细粒度生命周期方法带有autowireMode参数也没有被标识为过期,它们还在一些场合——为第三方框架赋能的场景发挥着光和热

试想一下,如果我是一个第三方框架的开发者,开发的框架想与Spring结合,想借助它的一些能力,但是又不想与Spring结合得太过紧密,而是仅仅做为DI的可选项之一,那么设计上就要考虑松耦合,不能嵌入太多Spring相关的注解或者代码,而是独立一个-spring的模块,在该模块中引入AutowireCapableBeanFactory,根据场景配置不同的autowireMode,使之与框架自身需要DI的场景、对象进行结合,而框架的实例对象看起来与Spring毫不相关,只有这样,才能与Spring进行解耦合

举个Dubbo的例子,在Dubbo DI过程,调用的是Object object = objectFactory.getExtension(pt, property);。objectFactory是一个AdaptiveExtensionFactory(自适应的DI工厂,是个composite模式),里边维护了一个ExtensionFactory(真正的工厂类)集合,该集合包含两个工厂,一个依靠Dubbo自身的SPI机制加载元素(SpiExtensionFactory),另一个直接从Spring获取元素(SpringExtensionFactory)

public class AdaptiveExtensionFactory implements ExtensionFactory {
	private final List factories;
	// ...(省略)

    @Override
    public  T getExtension(Class type, String name) {

        for (ExtensionFactory factory : factories) {
            T extension = factory.getExtension(type, name);
            // 任意一个工厂获取到元素,就立即返回
            if (extension != null) {
                return extension;
            }
        }
        return null;
    }

}
public class SpiExtensionFactory implements ExtensionFactory {
    @Override
    public  T getExtension(Class type, String name) {
        if (type.isInterface() && type.isAnnotationPresent(SPI.class)) {
            ExtensionLoader loader = ExtensionLoader.getExtensionLoader(type);
            if (!loader.getSupportedExtensions().isEmpty()) {
                return loader.getAdaptiveExtension();
            }
        }
        return null;
    }
}

public class SpringExtensionFactory implements ExtensionFactory {
	// ...(省略)
    
    @Override
    @SuppressWarnings("unchecked")
    public  T getExtension(Class type, String name) {
        for (ApplicationContext context : contexts) {
        // 从Spring的ApplicationContext里获取Bean
            if (context.containsBean(name)) {
                Object bean = context.getBean(name);
                if (type.isInstance(bean)) {
                    return (T) bean;
                }
            }
        }
        return null;
    }
}

借助Spring能力,从ApplicationContext里获取到需要的Bean之后,装配到Dubbo框架的实例对象中,而完全看不出Spring的痕迹,很好地与Spring解耦

总结

本文介绍了传统装配模式与现代注解驱动注入方式之间的区别,先是明白了传统装配模式中的byNamebyType@Resouce@Autowired等注解之间并无任何的联系,在底层处理上它们是两套逻辑,不可"见名知义"而混问一谈。接着介绍了Annotation-driven injection的方式,从中挑选了field injection进行了详细过程的分析,并澄清了@Resouce一个网上不严谨的说法。最后,对于这两种方式的使用进行了一个思考,对在什么场合使用什么方式能更了然于胸


导读: AutowireCapableBeanFactory探密(1)——为第三方框架赋能

你可能感兴趣的:(java,spring,java)