前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【小家Spring】细说Spring IOC容器的自动装配(@Autowired),以及Spring4.0新特性之【泛型依赖注入】的源码级解析

【小家Spring】细说Spring IOC容器的自动装配(@Autowired),以及Spring4.0新特性之【泛型依赖注入】的源码级解析

作者头像
YourBatman
发布2019-09-03 16:30:46
3.6K0
发布2019-09-03 16:30:46
举报
文章被收录于专栏:BAT的乌托邦BAT的乌托邦

前言

前面我发布了Spring IOC容器的刷新(初始化)过程,以及Spring 容器的Bean的实例化、初始化过程。其中有一个步骤小伙伴们比较关心,也提问的比较多,那就是泛型依赖注入。鉴于之前对这一块描述得也不是很详细,鉴于此处还是比较重要的,因此本文专门用篇幅聊聊这个事

看本篇文章之前,建议至少已经了解Spring容器的一个大致过程,最好已经看过博文(或者知道):

【小家Spring】AbstractAutowireCapableBeanFactory#populateBean实现Bean的属性赋值和initializeBean对Bean的初始化

因此本文将直接从依赖注入这一步,分析Spring是怎么样实现控制反转、依赖注入(DI)的~

Demo Show(自动装配的)

在讲解之前,先构造一个例子看看效果(效果很像RedisTemplate):

代码语言:javascript
复制
// 准备一个带泛型的Bean
@Getter
@Setter
@NoArgsConstructor  
@AllArgsConstructor
@ToString
public class GenericBean<T, W> {

    private T t;
    private W w;

}

// config配置文件中注入两个泛型Bean
    @Bean
    public Parent parentOne() {
        return new Parent();
    }
    @Bean
    public Parent parentTwo() {
        return new Parent();
    }
    @Bean
    public GenericBean<String, String> stringGeneric() {
        return new GenericBean<String, String>("str1", "str2");
    }
    @Bean
    public GenericBean<Object, Object> objectGeneric() {
        return new GenericBean<Object, Object>("obj1", 2);
    }

// 使用@Autowired注入,测试一下:
    @Autowired
    private GenericBean<Object, Object> objectGenericBean; //GenericBean(t=obj1, w=2)
    @Autowired
    private GenericBean<String, String> stringGenericBean; //GenericBean(t=st   r1, w=str2)
    // 注意,容器里虽然有两个Parent,这里即使不使用@Qualifier也不会报错。
    // 但是需要注意字段名parentOne,必须是容器里存在的,否则就报错了。
    @Autowired
    private Parent parentOne; //com.fsx.bean.Parent@23c98163

    //Spring4.0后的新特性,这样会注入所有类型为(包括子类)GenericBean的Bean(但是顺序是不确定的,可通过Order接口控制顺序)
    @Autowired
    private List<GenericBean> genericBeans; //[GenericBean(t=st   r1, w=str2), GenericBean(t=obj1, w=2)]
    // 这里的key必须是String类型,把GenericBean类型的都拿出来了,beanName->Bean
    @Autowired
    private Map<String, GenericBean> genericBeanMap; //{stringGenericBean=GenericBean(t=st   r1, w=str2), objectGenericBean=GenericBean(t=obj1, w=2)}
    // 这里面,用上泛型也是好使的,就只会拿指定泛型的了
    @Autowired
    private Map<String, GenericBean<Object, Object>> genericBeanObjMap; //{objectGenericBean=GenericBean(t=obj1, w=2)}

    // 普通类型,容器里面没有的Bean类型,注入是会报错的
    //@Autowired
    //private Integer normerValue;

如果你还不知道泛型依赖注入的话,就应该有这样一个疑问(应该有哈,若没有,看来你对@Autowired还没有个概性的认识):

@Autowired明明是根据类型进行注入的,那我们往容器里放置了两个GenericBean类型的Bean,为何启动没有报错呢???

(最直观的感受:若我们的service有两个serviceImpl,直接仅使用@Autowired注入是会报错的)

那么接下来,我们通过跟踪源码的方式,一层一层剥开内部的缘由,看看葫芦里到底是什么药~

依赖注入源码分析

在上面推荐的博文里已经讲到了,Spring在populateBean这一步为属性赋值的时候,会执行InstantiationAwareBeanPostProcessor处理器的postProcessPropertyValues方法。

这里AutowiredAnnotationBeanPostProcessor该处理器的postProcessPropertyValues方法就是来处理该注解的。(因此,我们的源码解析从此处开始吧)

代码语言:javascript
复制
	@Override
	public PropertyValues postProcessPropertyValues(
			PropertyValues pvs, PropertyDescriptor[] pds, Object bean, String beanName) throws BeanCreationException {

		// 找到所有的@Autowired元数据-------当前值可以见下图
		InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
		try {
			// 所以我们看InjectionMetadata 的inject即可,见下
			metadata.inject(bean, beanName, pvs);
		}
		catch (BeanCreationException ex) {
			throw ex;
		}
		catch (Throwable ex) {
			throw new BeanCreationException(beanName, "Injection of autowired dependencies failed", ex);
		}
		return pvs;
	}

metadata值如下:

InjectionMetadata#inject

代码语言:javascript
复制
	public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
		Collection<InjectedElement> checkedElements = this.checkedElements;
		
		// 这里是待遍历、待处理的属性Element们(备注,本处的InjectedElement实现类为:AutowiredFieldElement 因为我们是Field注入嘛)
		// 所以从下可知,我们直接看AutowiredFieldElement#inject方法吧
		Collection<InjectedElement> elementsToIterate = (checkedElements != null ? checkedElements : this.injectedElements);
		if (!elementsToIterate.isEmpty()) {
			boolean debug = logger.isDebugEnabled();
			for (InjectedElement element : elementsToIterate) {
				if (debug) {
					logger.debug("Processing injected element of bean '" + beanName + "': " + element);
				}
				element.inject(target, beanName, pvs);
			}
		}
	}

AutowiredFieldElement#inject:根据字段注入

AutowiredFieldElement是AutowiredAnnotationBeanPostProcessor的一个私有普通内部类,高内聚~

代码语言:javascript
复制
		// 这段代码虽然长,其实核心逻辑还并不在这里,而是在beanFactory Bean工厂的resolveDependency处理依赖实现里
		@Override
		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 {
				// 把field和required属性,包装成desc描述类
				DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
				desc.setContainingClass(bean.getClass());
				
				// 装载注入的名称,最最最后会被注册(缓存)起来
				Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
				Assert.state(beanFactory != null, "No BeanFactory available");
				TypeConverter typeConverter = beanFactory.getTypeConverter();
				try {
					// 把desc传进去,里面还有注解信息、元信息等等,这个方法是根据注解信息寻找到依赖的Bean的核心逻辑
					// 备注:此部分处理依赖逻辑交给Bean工厂,其实也没毛病。毕竟它处理的相当于是Field
					// 那么接下里,重点分析resolveDependency这个方法
					value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);
				} catch (BeansException ex) {
					throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);
				}
				synchronized (this) {
					if (!this.cached) {
						if (value != null || this.required) {
							this.cachedFieldValue = desc;
							registerDependentBeans(beanName, autowiredBeanNames);
							if (autowiredBeanNames.size() == 1) {
								String autowiredBeanName = autowiredBeanNames.iterator().next();
								if (beanFactory.containsBean(autowiredBeanName) &&
										beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {
									this.cachedFieldValue = new ShortcutDependencyDescriptor(
											desc, autowiredBeanName, field.getType());
								}
							}
						}
						else {
							this.cachedFieldValue = null;
						}
						this.cached = true;
					}
				}
			}
			if (value != null) {
				ReflectionUtils.makeAccessible(field);
				field.set(bean, value);
			}
		}
	}

DefaultListableBeanFactory#resolveDependency:解决依赖(根据依赖关系找到值)

代码语言:javascript
复制
	@Override
	@Nullable
	public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,
			@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
		
		// 把当前Bean工厂的名字发现器赋值给传进来DependencyDescriptor 类
		// 这里面注意了:有必要说说名字发现器这个东西,具体看下面吧==========还是比较重要的
		// Bean工厂的默认值为:private ParameterNameDiscoverer parameterNameDiscoverer = new DefaultParameterNameDiscoverer();
		descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());

		// 支持到Optional类型的注入,比如我们这样注入:private Optional<GenericBean<Object, Object>> objectGenericBean;
		// 也是能够注入进来的,只是类型变为,Optional[GenericBean(t=obj1, w=2)]
		// 对于Java8中Optional类的处理
		if (Optional.class == descriptor.getDependencyType()) {
			return createOptionalDependency(descriptor, requestingBeanName);
		}
		// 兼容ObjectFactory和ObjectProvider(Spring4.3提供的接口)
		// 关于ObjectFactory和ObjectProvider在依赖注入中的大作用,我觉得是非常有必要再撰文讲解的
		//对于前面讲到的提早曝光的ObjectFactory的特殊处理
		else if (ObjectFactory.class == descriptor.getDependencyType() ||
				ObjectProvider.class == descriptor.getDependencyType()) {
			return new DependencyObjectProvider(descriptor, requestingBeanName);
		}
		// 支持到了javax.inject.Provider这个类的实现
		else if (javaxInjectProviderClass == descriptor.getDependencyType()) {
			return new Jsr330ProviderFactory().createDependencyProvider(descriptor, requestingBeanName);
		}
		// 这个应该是我们觉得部分触及到的,其实不管何种方式,最终都是交给doResolveDependency方法去处理了
		else {
			//getAutowireCandidateResolver()得到ContextAnnotationAutowireCandidateResolver 根据依赖注解信息,找到对应的Bean值信息
			//getLazyResolutionProxyIfNecessary方法,它也是唯一实现。
			//如果字段上带有@Lazy注解,表示进行懒加载 Spring不会立即创建注入属性的实例,而是生成代理对象,来代替实例
			Object result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(
					descriptor, requestingBeanName);
			// 如果在@Autowired上面还有个注解@Lazy,那就是懒加载的,是另外一种处理方式(是一门学问)
			// 这里如果不是懒加载的(绝大部分情况都走这里) 就进入核心方法doResolveDependency 下面有分解
			if (result == null) {
				result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
			}
			return result;
		}
	}

DefaultListableBeanFactory#doResolveDependency 处理属性依赖关系的核心方法:通用的处理逻辑

代码语言:javascript
复制
	@Nullable
	public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
			@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
	
		// 相当于打个点,记录下当前的步骤位置  返回值为当前的InjectionPoint 
 		InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
		try {
			// 简单的说就是去Bean工厂的缓存里去看看,有没有名称为此的Bean,有就直接返回,没必要继续往下走了
			// 比如此处的beanName为:objectGenericBean等等
			Object shortcut = descriptor.resolveShortcut(this);
			if (shortcut != null) {
				return shortcut;
			}

			// 此处为:class com.fsx.bean.GenericBean
			Class<?> type = descriptor.getDependencyType();
		
			// 看看ContextAnnotationAutowireCandidateResolver的getSuggestedValue方法,具体实现在父类 QualifierAnnotationAutowireCandidateResolver中
			//处理@Value注解-------------------------------------
			//获取@Value中的value属性
			Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
			// 若存在value值,那就去解析它。使用到了AbstractBeanFactory#resolveEmbeddedValue
			// 也就是使用StringValueResolver处理器去处理一些表达式~~
			if (value != null) {
				if (value instanceof String) {
					String strVal = resolveEmbeddedValue((String) value);
					BeanDefinition bd = (beanName != null && containsBean(beanName) ? getMergedBeanDefinition(beanName) : null);
					value = evaluateBeanDefinitionString(strVal, bd);
				}
				//如果需要会进行类型转换后返回结果
				TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());
				return (descriptor.getField() != null ?
						converter.convertIfNecessary(value, type, descriptor.getField()) :
						converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));
			}
			
			//对数组、Collection、Map等类型进行处理,也是支持自动注入的。
			//因为是数组或容器,Sprng可以直接把符合类型的bean都注入到数组或容器中,处理逻辑是:
			//1.确定容器或数组的组件类型 if else 分别对待,分别处理
			//2.调用findAutowireCandidates(核心方法)方法,获取与组件类型匹配的Map(beanName -> bean实例)
			//3.将符合beanNames添加到autowiredBeanNames中
			Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
			if (multipleBeans != null) {
				return multipleBeans;
			}
			
			// 获取所有【类型】匹配的Beans,形成一个Map(此处用Map装,是因为可能不止一个符合条件)
			// 该方法就特别重要了,对泛型类型的匹配、对@Qualifierd的解析都在这里面,下面详情分解
			Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
			// 若没有符合条件的Bean。。。
			if (matchingBeans.isEmpty()) {
			    // 并且是必须的,那就抛出没有找到合适的Bean的异常吧
			    // 我们非常熟悉的异常信息:expected at least 1 bean which qualifies as autowire candidate...
				if (isRequired(descriptor)) {
					raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
				}
				return null;
			}

			String autowiredBeanName;
			Object instanceCandidate;


			//如果类型匹配的bean不止一个,Spring需要进行筛选,筛选失败的话继续抛出异常
			// 如果只找到一个该类型的,就不用进这里面来帮忙筛选了~~~~~~~~~
			if (matchingBeans.size() > 1) {
				// 该方法作用:从给定的beans里面筛选出一个符合条件的bean,此筛选步骤还是比较重要的,因此看看可以看看下文解释吧
				autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
				if (autowiredBeanName == null) {
					// 如果此Bean是要求的,或者 不是Array、Collection、Map等类型,那就抛出异常NoUniqueBeanDefinitionException
					if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
						// 抛出此异常
						return descriptor.resolveNotUnique(type, matchingBeans);
					}
					// Spring4.3之后才有:表示如果是required=false,或者就是List Map类型之类的,即使没有找到Bean,也让它不抱错,因为最多注入的是空集合嘛
					else {
						// In case of an optional Collection/Map, silently ignore a non-unique case:
						// possibly it was meant to be an empty collection of multiple regular beans
						// (before 4.3 in particular when we didn't even look for collection beans).
						return null;
					}
				}
				
				instanceCandidate = matchingBeans.get(autowiredBeanName);
			}
			else {
				// We have exactly one match.
				// 仅仅只匹配上一个,走这里 很简单  直接拿出来即可
				// 注意这里直接拿出来的技巧:不用遍历,直接用iterator.next()即可
				Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
				autowiredBeanName = entry.getKey();
				instanceCandidate = entry.getValue();
			}
			
			// 把找到的autowiredBeanName 放进去
			if (autowiredBeanNames != null) {
				autowiredBeanNames.add(autowiredBeanName);
			}
			// 底层就是调用了beanFactory.getBean(beanName);  确保该实例肯定已经被实例化了的
			if (instanceCandidate instanceof Class) {
				instanceCandidate = descriptor.resolveCandidate(autowiredBeanName, type, this);
			}
			Object result = instanceCandidate;
			if (result instanceof NullBean) {
				if (isRequired(descriptor)) {
					raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
				}
				result = null;
			}
			// 再一次校验,type和result的type类型是否吻合=====
			if (!ClassUtils.isAssignableValue(type, result)) {
				throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
			}
			return result;
		}
		// 最终把节点归还回来
		finally {
			ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
		}
	}

若出现多个Bean,将由下面方法去匹配和决定~

代码语言:javascript
复制
//determineAutowireCandidate 从多个Bean中,筛选出一个符合条件的Bean
	@Nullable
	protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {
		Class<?> requiredType = descriptor.getDependencyType();
		// 看看传入的Bean中有没有标注了@Primary注解的
		String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
		// 如果找到了 就直接返回
		// 由此可见,@Primary的优先级还是非常的高的
		if (primaryCandidate != null) {
			return primaryCandidate;
		}
		//找到一个标注了javax.annotation.Priority注解的。(备注:优先级的值不能有相同的,否则报错)
		String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
		if (priorityCandidate != null) { 
			return priorityCandidate;
		}
		// Fallback
		// 这里是最终的处理(相信绝大部分情况下,都会走这里~~~~~~~~~~~~~~~~~~~~)
		// 此处就能看出resolvableDependencies它的效能了,他会把解析过的依赖们缓存起来,不用再重复解析了
		for (Map.Entry<String, Object> entry : candidates.entrySet()) {
			String candidateName = entry.getKey();
			Object beanInstance = entry.getValue();
			
			// 到这一步就比较简单了,matchesBeanName匹配上Map的key就行。
			// 需要注意的是,bean可能存在很多别名,所以只要有一个别名相同,就认为是能够匹配上的  具体参考AbstractBeanFactory#getAliases方法
			//descriptor.getDependencyName() 这个特别需要注意的是:如果是字段,这里调用的this.field.getName() 直接用的是字段的名称
			// 因此此处我们看到的情况是,我们采用@Autowired虽然匹配到两个类型的Bean了,即使我们没有使用@Qualifier注解,也会根据字段名找到一个合适的(若没找到,就抱错了)
			if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) ||
					matchesBeanName(candidateName, descriptor.getDependencyName())) {
				return candidateName;
			}
		}
		return null;
	}

//determinePrimaryCandidate:顾名思义。它是从给定的Bean中看有木有标注了@Primary注解的Bean,有限选择它
	@Nullable
	protected String determinePrimaryCandidate(Map<String, Object> candidates, Class<?> requiredType) {
		String primaryBeanName = null;
		for (Map.Entry<String, Object> entry : candidates.entrySet()) {
			String candidateBeanName = entry.getKey();
			Object beanInstance = entry.getValue();
			// isPrimary就是去看看容器里(包含父容器)对应的Bean定义信息是否有@Primary标注
			if (isPrimary(candidateBeanName, beanInstance)) {
				if (primaryBeanName != null) {
					boolean candidateLocal = containsBeanDefinition(candidateBeanName);
					boolean primaryLocal = containsBeanDefinition(primaryBeanName);

					// 这个相当于如果已经找到了一个@Primary的,然后又找到了一个 那就抛出异常
					// @Primary只能标注到一个同类型的Bean上
					if (candidateLocal && primaryLocal) {
						throw new NoUniqueBeanDefinitionException(requiredType, candidates.size(),
								"more than one 'primary' bean found among candidates: " + candidates.keySet());
					}
					else if (candidateLocal) {
						primaryBeanName = candidateBeanName;
					}
				}
				// 把找出来的标注了@Primary的Bean的名称返回出去
				else {
					primaryBeanName = candidateBeanName;
				}
			}
		}
		return primaryBeanName;
	} 

// determineHighestPriorityCandidate:从给定的Bean里面筛选出一个优先级最高的
// 什么叫优先级最高呢?主要为了兼容JDK6提供的注解javax.annotation.Priority
	@Nullable
	protected String determineHighestPriorityCandidate(Map<String, Object> candidates, Class<?> requiredType) {
		String highestPriorityBeanName = null;
		Integer highestPriority = null;
		for (Map.Entry<String, Object> entry : candida         tes.entrySet()) {
			String candidateBeanName = entry.getKey();
			Object beanInstance = entry.getValue();
			if (beanInstance != null) {
				//AnnotationAwareOrderComparator#getPriority
				// 这里就是为了兼容JDK6提供的javax.annotation.Priority这个注解,然后做一个优先级排序
				// 注意注意注意:这里并不是@Order,和它木有任何关系~~~
				// 它有的作用像Spring提供的@Primary注解
				Integer candidatePriority = getPriority(beanInstance);
				// 大部分情况下,我们这里都是null,但是需要注意的是,@Primary只能标注一个,这个虽然可以标注多个,但是里面的优先级值,不能出现相同的(强烈建议不要使用~~~~而使用@Primary)
				if (candidatePriority != null) {
					if (highestPriorityBeanName != null) {
					
						// 如果优先级的值相等,是不允许的,这里需要引起注意,个人建议一般还是使用@Primary吧
						if (candidatePriority.equals(highestPriority)) {
							throw new NoUniqueBeanDefinitionException(requiredType, candidates.size(),
									"Multiple beans found with the same priority ('" + highestPriority +
									"') among candidates: " + candidates.keySet());
						}
						else if (candidatePriority < highestPriority) {
							highestPriorityBeanName = candidateBeanName;
							highestPriority = candidatePriority;
						}
					}
					else {
						highestPriorityBeanName = candidateBeanName;
						highestPriority = candidatePriority;
					}
				}
			}
		}
		return highestPriorityBeanName;
	}

当前已经解析过的依赖截图如下:这些特殊类型,可以直接@Autowired注入

上面代码的处理过程总结如下:

  1. Spring注入依赖后会保存依赖的beanName,作为下次注入相同属性的捷径。如果存在捷径的话,直接通过保存的beanName获取bean实例
  2. 对@Value注解的处理。如果存在,会获取并解析value值
  3. 对数组或容器类型的处理。如果是数组或容器类型的话,Spring可以将所有与目标类型匹配的bean实例都注入进去,不需要判断
    1. 获取数组或容器单个组件的类型
    2. 调用findAutowireCandidates方法,获取与组件类型匹配的Map(beanName -> bean实例)
    3. 保存类型匹配的beanNames
  4. 非数组、容器类型的处理
    1. 调用findAutowireCandidates方法,获取与组件类型匹配的Map(beanName -> bean实例)
    2. 如果类型匹配的结果为多个,需要进行筛选(@Primary、优先级、字段名)
    3. 如果筛选结果不为空,或者只有一个bean类型匹配,就直接使用该bean
DefaultListableBeanFactory#findAutowireCandidates:搜索类型匹配的beand的Map

根据注解进行依赖注入的主要工作,就是根据标注的字段的类型来搜索符合的bean,并将类型匹配的bean注入到字段中。而搜索bean的工作在这个方法中实现:

代码语言:javascript
复制
	protected Map<String, Object> findAutowireCandidates(
			@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {

		// 获取类型匹配的bean的beanName列表(包括父容器,但是此时还没有进行泛型的精确匹配)
		String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(
				this, requiredType, true, descriptor.isEager());
		//存放结果的Map(beanName -> bena实例)  最终会return的
		Map<String, Object> result = new LinkedHashMap<>(candidateNames.length);
		
		//如果注入类型是特殊类型或其子类,会将特殊类型的实例添加到结果
		// 哪些特殊类型呢?上面截图有,比如你要注入ApplicationContext、BeanFactory等等
		for (Class<?> autowiringType : this.resolvableDependencies.keySet()) {
			if (autowiringType.isAssignableFrom(requiredType)) {
				Object autowiringValue = this.resolvableDependencies.get(autowiringType);
				autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);
				if (requiredType.isInstance(autowiringValue)) {
					result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);
					break;
				}
			}
		}
			
		// candidateNames可能会有多个,这里就要开始过滤了,比如@Qualifier、泛型等等
		for (String candidate : candidateNames) {
			//不是自引用 && 符合注入条件
			// 自引用的判断:找到的候选的Bean的名称和当前Bean名称相等 或者 当前bean名称等于工厂bean的名称~~~~~~~
			// isAutowireCandidate:这个方法非常的关键,判断该bean是否允许注入进来。泛型的匹配就发生在这个方法里,下面会详解
			if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {
				addCandidateEntry(result, candidate, descriptor, requiredType);
			}
		}
			
		////结果集为空 && 注入属性是非数组、容器类型  那么Spring就会放宽注入条件,然后继续寻找
		// 什么叫放宽:比如泛型不要求精确匹配了、比如自引用的注入等等
		if (result.isEmpty() && !indicatesMultipleBeans(requiredType)) {
			// Consider fallback matches if the first pass failed to find anything...
			//// FallbackMatch:放宽对泛型类型的验证  所以从这里用了一个新的fallbackDescriptor 对象   相当于放宽了泛型的匹配
			DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();
			for (String candidate : candidateNames) {
				if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor)) {
					addCandidateEntry(result, candidate, descriptor, requiredType);
				}
			}
			if (result.isEmpty()) {
				// Consider self references as a final pass...
				// but in the case of a dependency collection, not the very same bean itself.
				//// 如果结果还是为空,Spring会将自引用添加到结果中  自引用是放在最后一步添加进去的
				for (String candidate : candidateNames) {
					if (isSelfReference(beanName, candidate) &&
							(!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) &&
							isAutowireCandidate(candidate, fallbackDescriptor)) {
						addCandidateEntry(result, candidate, descriptor, requiredType);
					}
				}
			}
		}
		return result;
	}

步骤总结:

  1. 将获取类型匹配的Bean工作交给BeanFactoryUtils.beanNamesForTypeIncludingAncestors。该方法除了当前beanFactory还会递归对父parentFactory进行查找
  2. 如果注入类型是特殊类型或其子类,会将特殊类型的实例添加到结果
  3. 对结果进行筛选
    1. BeanDefinition的autowireCandidate属性,表示是否允许该bena注入到其他bean中,默认为true
    2. 泛型类型的匹配,如果存在的话
    3. Qualifier注解。如果存在Qualifier注解的话,会直接比对Qualifier注解中指定的beanName。需要注意的是,Spring处理自己定义的Qualifier注解,还支持javax.inject.Qualifier注解
  4. 如果筛选后,结果为空,Spring会放宽筛选条件,再筛选一次
DefaultListableBeanFactory#isAutowireCandidate 判断指定的descriptor是否能够被注入

Determine whether the specified bean definition qualifies as an autowire candidate to be injected into other beans which declare a dependency of matching type.

代码语言:javascript
复制
	@Override
	public boolean isAutowireCandidate(String beanName, DependencyDescriptor descriptor)
			throws NoSuchBeanDefinitionException {
		//getAutowireCandidateResolver()为ContextAnnotationAutowireCandidateResolver
		return isAutowireCandidate(beanName, descriptor, getAutowireCandidateResolver());
	}



	protected boolean isAutowireCandidate(String beanName, DependencyDescriptor descriptor, AutowireCandidateResolver resolver)
			throws NoSuchBeanDefinitionException {

		String beanDefinitionName = BeanFactoryUtils.transformedBeanName(beanName);
		// 若存在Bean定义,就走这里(因为有的Bean可能是直接registerSingleton进来的,是不存在Bean定义的)  
		// 我们的注入,绝大部分情况都走这里
		if (containsBeanDefinition(beanDefinitionName)) {
			//getMergedLocalBeanDefinition方法的作用就是获取缓存的BeanDefinition对象并合并其父类和本身的属性
			return isAutowireCandidate(beanName, getMergedLocalBeanDefinition(beanDefinitionName), descriptor, resolver);
		}
		// 若已经存在实例了,就走这里
		else if (containsSingleton(beanName)) {
			return isAutowireCandidate(beanName, new RootBeanDefinition(getType(beanName)), descriptor, resolver);
		}

		// 父容器  有可能为null,为null就肯定走else默认值了 true 可以注入
		BeanFactory parent = getParentBeanFactory();
		if (parent instanceof DefaultListableBeanFactory) {
			// No bean definition found in this factory -> delegate to parent.
			return ((DefaultListableBeanFactory) parent).isAutowireCandidate(beanName, descriptor, resolver);
		}
		else if (parent instanceof ConfigurableListableBeanFactory) {
			// If no DefaultListableBeanFactory, can't pass the resolver along.
			return ((ConfigurableListableBeanFactory) parent).isAutowireCandidate(beanName, descriptor);
		}
		// 默认值是true
		else {
			return true;
		}
	}


	protected boolean isAutowireCandidate(String beanName, RootBeanDefinition mbd,
			DependencyDescriptor descriptor, AutowireCandidateResolver resolver) {

		String beanDefinitionName = BeanFactoryUtils.transformedBeanName(beanName);
			
		//resolveBeanClass 这个方法之前提到过,主要是保证此Class已经被加载进来了
		resolveBeanClass(mbd, beanDefinitionName);
		//是否已经指定引用非重载方法的工厂方法名。  默认值是true
		if (mbd.isFactoryMethodUnique) {
			boolean resolve;
			synchronized (mbd.constructorArgumentLock) {
				resolve = (mbd.resolvedConstructorOrFactoryMethod == null);
			}
			// 此处主要处理工厂方法的方式,此处先略过~
			if (resolve) {
				new ConstructorResolver(this).resolveFactoryMethodIfPossible(mbd);
			}
		}

		// 核心来了。ContextAnnotationAutowireCandidateResolver#isAutowireCandidate方法
		// 真正的实现在父类:QualifierAnnotationAutowireCandidateResolver它身上
		return resolver.isAutowireCandidate(
				new BeanDefinitionHolder(mbd, beanName, getAliases(beanDefinitionName)), descriptor);
	}

QualifierAnnotationAutowireCandidateResolver#isAutowireCandidate:判断该Bean是否能注入(会解析@Qualifier注解)

代码语言:javascript
复制
	@Override
	public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
		boolean match = super.isAutowireCandidate(bdHolder, descriptor);
		
		// 到了这,如果是false,说明泛型没有匹配上(那就不用继续往下走了)
		// 如果是true,那就继续,解析@Qualifier注解啦  所以若你标记了@Qualifier注解 也是需要对应上
		if (match) {
			// 这个逻辑比较简单,看看有没有标注@Qualifier注解(没有标注也是返回true~~)
			// 需要注意的是,Spring这里支持自己的@Qualifier,也支持javax.inject.Qualifier
			// checkQualifiers() 这个方法有一些有意思的处理,因此还是决定讲解一下,请参见下面的解析~~~~~
			match = checkQualifiers(bdHolder, descriptor.getAnnotations());
			if (match) {
				// 兼容到方法级别的注入~~~~~~~~~~~~~
				MethodParameter methodParam = descriptor.getMethodParameter();
				if (methodParam != null) {
					Method method = methodParam.getMethod();
					if (method == null || void.class == method.getReturnType()) {
						match = checkQualifiers(bdHolder, methodParam.getMethodAnnotations());
					}
				}
			}
		}
		return match;
	}

//boolean match = super.isAutowireCandidate(bdHolder, descriptor);(GenericTypeAwareAutowireCandidateResolver中)
	@Override
	public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
		if (!super.isAutowireCandidate(bdHolder, descriptor)) {
			// If explicitly false, do not proceed with any other checks...
			return false;
		}
		
		// 这里,这里,这里  看方法名就能看出来。检测看看泛型是否匹配。
		// 若泛型都不匹配,就直接返回false了,基本步骤为:
		//1、从descriptor里拿倒泛型类型
		//2、First, check factory method return type, if applicable
		//3、return dependencyType.isAssignableFrom(targetType);
		// 这个方法官方doc为:Full check for complex generic type match... 带泛型的全检查,而不是简单Class类型的判断
		return checkGenericTypeMatch(bdHolder, descriptor);
	}

// if (!super.isAutowireCandidate(bdHolder, descriptor)) {  (SimpleAutowireCandidateResolver中)
	
	@Override
	public boolean isAutowireCandidate(BeanDefinitionHolder bdHolder, DependencyDescriptor descriptor) {
		// Bean定义信息的默认值,都会返回true
		return bdHolder.getBeanDefinition().isAutowireCandidate();
	}

QualifierAnnotationAutowireCandidateResolver#checkQualifiers:检查@Qualifier注解是否符合条件

上面知道了,若类型啥的都匹配上了,接下来还得解析@Qualifier是否匹配,它有一个很有意思的点:@Qualifier可以标注在类上面,也可以达到匹配的效果。(但它不是Bean名称,也不是bean的别名)

代码语言:javascript
复制
	/**
	 * Match the given qualifier annotations against the candidate bean definition.
	 * 将给定的@Qualifier注解与候选bean定义匹配~~~(简单的书就是看看类型已匹配上的,@Qualifier是否还能匹配上)
	 */
	protected boolean checkQualifiers(BeanDefinitionHolder bdHolder, Annotation[] annotationsToSearch) {
		// 这里一般会有两个注解  一个@Autowired 一个@Qualifier  
		// 或者还有其余的组合注解~~~
		if (ObjectUtils.isEmpty(annotationsToSearch)) {
			return true;
		}
		SimpleTypeConverter typeConverter = new SimpleTypeConverter();
		for (Annotation annotation : annotationsToSearch) {
			Class<? extends Annotation> type = annotation.annotationType();
			boolean checkMeta = true;
			boolean fallbackToMeta = false;
			
			//isQualifier:判断是不是@Qualifier注解以及 JSR330的`javax.inject.Qualifier`注解也是支持的
			if (isQualifier(type)) {
				// checkQualifier 最重要的方法就是这个了,它是个重载方法。。。它的内容非常长,大致我在这里解析步骤如下:
				//1、bd.getQualifier 看看Bean定义里是否已经定义过tQualifier们(但是经过我的跟踪,Bean定义得这个字段:private final Map<String, AutowireCandidateQualifier> qualifiers;永远不会被赋值 如有人知道,请告知我 了能事Spring预留得吧)
				//2、该Bean定义得AnnotatedElement qualifiedElement的这个属性上是否有指定的注解,有就拿出这个Annotation,否则继续下一步
				//3、resolvedFactoryMethod工厂方法上是否有这个注解,否则进行下一步(下一步事关键。。。)
				//4、Look for matching annotation on the target class  JavaDoc得意思备注也很清晰,就是去具体得类上面,看有没有有对应的注解,有就拿出来。
				//(有个细节):即使这个类被代理了,也是能拿到标注在它上面的注解的  因为: AnnotationUtils.getAnnotation(ClassUtils.getUserClass(bd.getBeanClass()), type)
				//5、到这里,如国获得了对应的@Qualifier注解,那就会比较。如果value值也相同,那就return true,否则继续往下走
				//6、接下来拿到这个注解的attributes,然后判断若@Qualifier没有value值或者是空串,就只return false了  否则继续看
				//7、最终会和Bean上面那个注解(一般都是@Component等注解)的value值和@Qualifier得value值进行比较,若相等  就最终返回true勒(请注意:此处Bean得alias别名若相等也是会返回true)
				//8、======就这样,我们就完成了Bean定义和@Qualifier得一个匹配过程======
				if (!checkQualifier(bdHolder, annotation, typeConverter)) {
					fallbackToMeta = true;
				}
				else {
					checkMeta = false;
				}
			}

			// 这一步非常有效:相当于支持到了组合注解的情况。 它连注解的注解都会解析
			// 比如我们@MyAnno上面还有@Qualifier注解,仍然会被这里解析到的  内部有一个递归
			if (checkMeta) {
				boolean foundMeta = false;
				for (Annotation metaAnn : type.getAnnotations()) {
					Class<? extends Annotation> metaType = metaAnn.annotationType();
					if (isQualifier(metaType)) {
						foundMeta = true;
						// Only accept fallback match if @Qualifier annotation has a value...
						// Otherwise it is just a marker for a custom qualifier annotation.
						if ((fallbackToMeta && StringUtils.isEmpty(AnnotationUtils.getValue(metaAnn))) ||
								!checkQualifier(bdHolder, metaAnn, typeConverter)) {
							return false;
						}
					}
				}
				if (fallbackToMeta && !foundMeta) {
					return false;
				}
			}
		}
		return true;
	}

当我们注入GenericBean<Object, Object>DependencyDescriptor descriptor参考如下:

我们发现它的类型都是带有泛型的

泛型依赖注入

有了上面的源码解析,详细下面的案例结果,我们是能够猜到的:

代码语言:javascript
复制
// 向容器内注入Bean(此处忽略)


    @Autowired
    private GenericBean<String, Object> objectGenericBean; 	// 这样注入报错:说找不到Bean

//////////////////////////////////////////
    @Autowired
    private GenericBean objectGenericBean; // 依然报错,但是和上面报错不同,这里报错是找到了2个,匹配不到
// 因此这种情况要特别特别特别的注意:如果字段名不是objectGenericBean,而是objectGeneric,就不会报错了,具体原因参考上文。。。

这里面为了协助理解,附图解释:

从上图可以看出,如果我们注入的时候不指定的泛型,它就是两个 ,属于通配符。所以能够匹配容器里的同类型的所有的Bean,所以如果筛选不出来only one,那就报错了。(因此,如果容器了只有这一个类型的Bean,那就木有问题,就是它了

接下来再看看这个case:

代码语言:javascript
复制
// 我向容器里只注入一个该类型的Bean,
    @Bean
    public GenericBean objectGeneric() {
        return new GenericBean<Object, Object>("obj1", 2);
    }

// 注入方式一:
    @Autowired
    private GenericBean objectGenericBean; //GenericBean(t=obj1, w=2)  显然,这是最正常不过的注入了

// 注入方式二:
    @Autowired
    private GenericBean<Integer, Integer> integerGenericBean; // GenericBean(t=obj1, w=2) 也没有任何问题,可以正常注入,但需要下面这个情况:

	// 我们可以注入任意泛型标注的(以及不用泛型标注的bean,但是使用时候需要注意)
    @Override
    public Object hello() {
        System.out.println(integerGenericBean);
        //java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Integer
        // 这里不能用Integer直接接收,只能使用Object接受,否则一定报错~
        Integer t = integerGenericBean.getT();
        Integer w = integerGenericBean.getW();
        return "service hello";
    }

// 注入方式三: 看看注入多次,是否是同一个Bean(泛型不同的情况下)
    @Autowired
    private GenericBean<Integer, Integer> integerGenericBean;
    @Autowired
    private GenericBean<String, String> stringGenericBean;

    @Override
    public Object hello() {
        // 这样直接比较会编译报错哦
        //System.out.println(integerGenericBean == stringGenericBean);

        //但是我们这样来看看到底是不是同一个对象
        System.out.println(System.identityHashCode(integerGenericBean)); //72085469
        System.out.println(System.identityHashCode(stringGenericBean)); //72085469
        return "service hello";
    }
因此我们可以大胆的说:注入的就是同一个Bean。哪怕泛型不同,也是同一个对象
毕竟Spring管理的Bean,默认都是单例的
Spring Boot中RedisTemplate<Object, Object>和StringRedisTemplate的注入问题

最近有个小伙伴问我问题,说项目中他们注入RedisTemplate的时候,好像可以随便注入,有的同时注入StringRedisTemplate,有的注入RedisTemplate,有的注入RedisTemplate<String,Object>。。。

相信如果没见到这篇文章之前,很多小伙伴也不能理解,但是有了上面的解释,相信一看就知道啥原因了。看了看他们的配置文件,有自己配置注入的RedisTemplate,且没有给泛型。所以按照上面的例子的说明,我们是可以注入任意泛型的RedisTemplate的,但是使用的时候需要注意~~~

下面我们来看看,Spring Boot自动为我们注入的情况:

代码语言:javascript
复制
	// 容器中不存在name为redisTemplate的Bean,这个会注入
	@Bean
	@ConditionalOnMissingBean(name = "redisTemplate")
	public RedisTemplate<Object, Object> redisTemplate(
			RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
		RedisTemplate<Object, Object> template = new RedisTemplate<>();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

	// 当前容易没有StringRedisTemplate 类型的(注意不是RedisTemplate类型)Bean,它就会注入
	// 说明一点:StringRedisTemplate 是RedisTemplate的子类
	@Bean
	@ConditionalOnMissingBean 
	public StringRedisTemplate stringRedisTemplate(
			RedisConnectionFactory redisConnectionFactory) throws UnknownHostException {
		StringRedisTemplate template = new StringRedisTemplate();
		template.setConnectionFactory(redisConnectionFactory);
		return template;
	}

若我们自己什么都不配置,那么我们只能这么注入,且只能这两种情况。

代码语言:javascript
复制
    // 这样注入是失败的,因为容器中并没有该类型的(泛型类型)的Bean
    //@Autowired
    //private RedisTemplate<Integer, Integer> integerRedisTemplate;
    
    @Autowired
    private RedisTemplate<Object, Object> objectRedisTemplate;
    @Autowired
    private RedisTemplate<String, String> stringRedisTemplate;
    @Autowired
    private StringRedisTemplate stringRedisTemplate2;

    @Test
    public void fun1() {
        System.out.println(objectRedisTemplate); //org.springframework.data.redis.core.RedisTemplate@3b27b497
        
        // 下面两个Bean,显然其实是同一个Bean,都是SpringBoot为我们配置的StringRedisTemplate
        System.out.println(stringRedisTemplate); //org.springframework.data.redis.core.StringRedisTemplate@b1534d3
        System.out.println(stringRedisTemplate2); //org.springframework.data.redis.core.StringRedisTemplate@b1534d3
    }
//从这里面我们可以看出,Spring IOC容器里是只有两个RedisTemplate的=====

如果我们自己手动注入一个不带泛型的Bean呢?

代码语言:javascript
复制
// 我们自己注入一个Bean  RedisTemplate
    //备注:这里bean名称不要叫为redisTemplate,让SpringBoot也能注入(为了测试)
    //生产环境:建议名称就叫redisTemplate,因为我们既然自定义了,就没必要再多余注入一个了
    @Bean
    public RedisTemplate myRedisTemplate(RedisConnectionFactory redisConnectionFactory) {
        RedisTemplate redisTemplate = new RedisTemplate();
        redisTemplate.setConnectionFactory(redisConnectionFactory);
        return redisTemplate;
    }

// 单元测试一番:
    @Autowired
    private ApplicationContext applicationContext;

    @Test
    public void fun1() {
        applicationContext.getBeansOfType(RedisTemplate.class)
                .forEach((k, v) -> System.out.println(k + "-->" + v));
        //备注:若我们注册Bean名称为redisTemplate,那就只会有两个的
        //myRedisTemplate-->org.springframework.data.redis.core.RedisTemplate@6a818392
        //redisTemplate-->org.springframework.data.redis.core.RedisTemplate@489091bd
        //stringRedisTemplate-->org.springframework.data.redis.core.StringRedisTemplate@512d6e60   
    }
@Autowired和@Resource的区别

直观的错误理解:

  1. @Autowired根据类型进行注入,若没有找到该类型Bean会报错
  2. @Autowired根据类型进行注入, 若出现多个类型的Bean,会报错
  3. @Resource根据名称进入注入

解答这些误解(给出正确答案):

  1. @Autowired根据类型进行注入这话没毛病,但是若没有找到该类型的Bean,若设置了属性required=false也是不会报错的
  2. @Autowired注入若找到多个类型的Bean,也不会报错,比如下面三种情况,都不会报错~
代码语言:javascript
复制
// 向容器中注入两个相同类型的Bean,并且都不使用@Primary标注
@Configuration
public class RootConfig {
    @Bean
    public Parent parentOne() {
        return new Parent();
    }

    @Bean
    public Parent parentTwo() {
        return new Parent();
    }
}

注入方式如下:
    @Autowired
    private Parent parent;

// 若什么都不处理,会异常:NoUniqueBeanDefinitionException: No qualifying bean of type 'com.fsx.bean.Parent' available: expected single matching bean but found 2: parentOne,parentTwo

// 方案一:向容器注入Bean的时候加上@Primary注解(略)
// 方案二:使用@Qualifier(略)
// 方案三:使得字段名,和容器里的Bean名称一致,比如改成下面字段名,就不会报错了
    @Autowired
    private Parent parentOne;
    @Autowired
    private Parent parentTwo;

关于方案三:因为大多数小伙伴都认为@Autowired注解找到两个Bean就直接报错了,是不被允许的。没想到最后它还会根据字段名进行一次过滤,完全找不到再报错。 因为我使用的Spring版本为:5.0.6.RELEASE 因此有可能是版本原因Spring做了这步处理,这里我也不再去测试从哪个版本开始支持的了,若有小伙伴清楚了,欢迎留言告知,万分感激~

需要说明的是,它和**@Qualifier**的区别:他们的生效阶段不一样。

@Qualifier**:它在寻早同类型的Bean的时候就生效了,在方法**findAutowireCandidates**这里去寻找候选的Bean的时候就生效了,只会找出一个(或者0个出来)**

@Autowired**自带的根据字段名匹配:发生在若找出多个同类型Bean的情况下,会根据此字段名称determine一个匹配上的出来**

@Resource·装配顺序解释:

  • 如果既没有指定name,又没有指定type,则自动先按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配
  • 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
  • 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常
  • 如果指定了type,则从上下文中找到类似匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常
  • 需要注意的是@Resource并不支持@Primary

@Resource并不支持@Primary这句话此处做出说明:在Spring4.2之前它是不支持的,但是在4.2之后,@Resource已经全面支持了@Primary以及提供了对@Lazy的支持。

注意:它对@Lazy支持并不是ContextAnnotationAutowireCandidateResolver来处理的,全部由CommonAnnotationBeanPostProcessor这个类里面的方法处理

它的核心处理办法是ResourceElement.inject,最终调用ResourceElement.getResourceToInject方法:

代码语言:javascript
复制
	private class ResourceElement extends LookupElement {
		@Override
		protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
			return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) : getResource(this, requestingBeanName));
		}
	}
代码语言:javascript
复制
public class CommonAnnotationBeanPostProcessor extends InitDestroyAnnotationBeanPostProcessor implements InstantiationAwareBeanPostProcessor, BeanFactoryAware, Serializable {
	...
	protected Object buildLazyResourceProxy(final LookupElement element, final @Nullable String requestingBeanName) {
		TargetSource ts = new TargetSource() {
			@Override
			public Class<?> getTargetClass() {
				return element.lookupType;
			}
			@Override
			public boolean isStatic() {
				return false;
			}
			@Override
			public Object getTarget() {
				return getResource(element, requestingBeanName);
			}
			@Override
			public void releaseTarget(Object target) {
			}
		};
		ProxyFactory pf = new ProxyFactory();
		pf.setTargetSource(ts);
		if (element.lookupType.isInterface()) {
			pf.addInterface(element.lookupType);
		}
		ClassLoader classLoader = (this.beanFactory instanceof ConfigurableBeanFactory ?
				((ConfigurableBeanFactory) this.beanFactory).getBeanClassLoader() : null);
		return pf.getProxy(classLoader);
	}
	protected Object getResource(LookupElement element, @Nullable String requestingBeanName) throws NoSuchBeanDefinitionException {
		...
		return autowireResource(this.resourceFactory, element, requestingBeanName);
	}
	protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName) throws NoSuchBeanDefinitionException {
			...
			resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);
			...
	}
	...
}

它最终也是依赖于beanFactory.resolveDependency()去处理,所以对@Primary也提供了支持~~~


在Spring生态下开发,强烈建议全部使用Spring技术栈的注解,而不推荐使用JSR规范或者JavaEE提供的注解(毕竟Spring对自己亲儿子的支持是最佳的) JSR330提供的注解@Inject@Named@Autowired@Qualifire一模一样,他们可以混用(唯一区别是:只有Spring的亲儿子@Autowired只是required = false) @Autowired等三个注解都可议注入List、Map等等。但是要求必须至少有一个,否则请写@Autowired(required = false),显然此时如果你用的@Resource或者@Inject就只能眼睁睁看着它报错了~所以推荐大家使用Spring的亲儿子@Autowired~~~ 并且大都情况下推荐构造函数注入

泛型依赖注入的另一优点实例(Base基类设计)

泛型依赖注入在书写Base基类的时候,有非常大的用处,可以省略不少的代码,更好的规划和设计代码。

我在书写公司Base类的时候,很好的使用到了这一优点。

下面简单贴出代码如下:(不做过多解释了)

代码语言:javascript
复制
// 我定义的BaseDao接口如下:
public interface IBaseDBDao<T extends BaseDBEntity<T, PK>, PK extends Number>{ ... }

// 定义的BaseService如下:
public interface IBaseDBService<T extends BaseDBEntity<T, PK>, PK extends Number> { ... }
// 因为service层,所以我肯定更希望的是提供一些基础的、共用的实现,否则抽取的意义就不大了,因此此处就用到了泛型依赖注入:

//BaseServiceImpl基础共用实现如下:
public abstract class BaseDBServiceImpl<T extends BaseDBEntity<T, PK>, PK extends Number> implements IBaseDBService<T, PK> {

	// 这里就是泛型依赖注入的核心,子类无需再关心dao层的注入,由基类完成dao的注入即可,非常的自动化,切方便管理
	// 这里子类继承,对对应的注入到具体类型的Dao接口代理类,而不用子类关心
	// 如果这是在Spring4之前,我之前做就得提供一个abstract方法给子类,让子类帮我注入给我,我才能书写公用逻辑。
	//然后这一把泛型依赖注入,大大方便了继承者的使用
	// 可以说完全做到了无侵入、赋能的方式加强子类
    @Autowired
    private IBaseDBDao<T, PK> baseDao;
	
}
冷知识:使用@Value进行依赖注入

其实上面已经提到了,AutowiredAnnotationBeanPostProcessor不仅处理@Autowired也处理@Value,所以向这么写,使用@Value注解也是能够实现依赖注入的:

代码语言:javascript
复制
@Configuration
public class RootConfig {
    @Bean
    public Person person() {
        return new Person();
    }
	
	// 这样就能够实现依赖注入了~~~
    @Value("#{person}")
    private Person person;
}

细节:

1、只能是#{person}而不能是${person}

2、person表示beanName,因此请保证此Bean必须存在。比如若写成这样@Value("#{person2}")就报错:

代码语言:javascript
复制
Caused by: org.springframework.expression.spel.SpelEvaluationException: EL1008E: Property or field 'person2' cannot be found on object of type 'org.springframework.beans.factory.config.BeanExpressionContext' - maybe not public or not valid?
	at org.springframework.expression.spel.ast.PropertyOrFieldReference.readProperty(PropertyOrFieldReference.java:217)
	at org.springframework.expression.spel.ast.PropertyOrFieldReference.getValueInternal(PropertyOrFieldReference.java:104)

可以看出,报的是Spel的解析时候找不到Bean的错~

@Value结合el表达式也有这样的能力(原理是StandardBeanExpressionResolverStandardEvaluationContext),只是一般我们不这么干。

@Value(#{})与@Value(${})的区别
  1. @Value(#{}): SpEL表达式 @Value("#{}") 表示SpEl表达式通常用来获取bean的属性,或者调用bean的某个方法。当然还有可以表示常量
  2. @Value(${}):获取配置文件中的属性值

它俩可以结合使用:比如:@Value("#{'${spring.redis.cluster.nodes}'.split(',')}")是一个结合使用的案例~ 这样就可以把如下配置解析成List了

代码语言:javascript
复制
spring.redis.cluster.nodes=10.102.144.94:7535,10.102.144.94:7536,10.102.144.95:7535,10.102.144.95:7536,10.102.148.153:7535,10.102.148.153:7536
本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2019年03月13日,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 前言
  • Demo Show(自动装配的)
  • 依赖注入源码分析
  • DefaultListableBeanFactory#findAutowireCandidates:搜索类型匹配的beand的Map
  • DefaultListableBeanFactory#isAutowireCandidate 判断指定的descriptor是否能够被注入
  • Spring Boot中RedisTemplate<Object, Object>和StringRedisTemplate的注入问题
  • @Autowired和@Resource的区别
  • 泛型依赖注入的另一优点实例(Base基类设计)
    • 冷知识:使用@Value进行依赖注入
    相关产品与服务
    云数据库 Redis
    腾讯云数据库 Redis(TencentDB for Redis)是腾讯云打造的兼容 Redis 协议的缓存和存储服务。丰富的数据结构能帮助您完成不同类型的业务场景开发。支持主从热备,提供自动容灾切换、数据备份、故障迁移、实例监控、在线扩容、数据回档等全套的数据库服务。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档