【走近Spring】Spring IOC容器的自动装配(@Autowired)以及Spring4.0新特性之【泛型依赖注入】
在介绍Spring IOC容器的刷新(初始化)过程,以及Spring 容器的Bean的实例化、初始化过程。我们提到了泛型依赖注入,本文就来聊聊Spring是如何做到的。
先来看一个例子:
// 准备一个带泛型的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明明是根据类型进行注入的,那我们往容器里放置了两个GenericBean类型的Bean,为何启动没有报错呢?(最直观的感受:若我们的service有两个serviceImpl,直接仅使用@Autowired注入是会报错的)
依赖注入源码分析
Spring在populateBean这一步为属性赋值的时候,会执行InstantiationAwareBeanPostProcessor处理器的postProcessPropertyValues方法。
这里AutowiredAnnotationBeanPostProcessor该处理器的postProcessPropertyValues方法就是来处理该注解的。
@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:
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的一个私有普通内部类,高内聚~
// 这段代码虽然长,其实核心逻辑还并不在这里,而是在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:解决依赖(根据依赖关系找到值)
@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 处理属性依赖关系的核心方法:通用的处理逻辑
@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,将由下面方法去匹配和决定~
//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注入
上面代码的处理过程总结如下:
- Spring注入依赖后会保存依赖的beanName,作为下次注入相同属性的捷径。如果存在捷径的话,直接通过保存的beanName获取bean实例
- 对@Value注解的处理。如果存在,会获取并解析value值
- 对数组或容器类型的处理。如果是数组或容器类型的话,Spring可以将所有与目标类型匹配的bean实例都注入进去,不需要判断
- 获取数组或容器单个组件的类型
- 调用findAutowireCandidates方法,获取与组件类型匹配的Map(beanName -> bean实例)
- 保存类型匹配的beanNames
- 非数组、容器类型的处理
- 调用findAutowireCandidates方法,获取与组件类型匹配的Map(beanName -> bean实例)
- 如果类型匹配的结果为多个,需要进行筛选(@Primary、优先级、字段名)
- 如果筛选结果不为空,或者只有一个bean类型匹配,就直接使用该bean
DefaultListableBeanFactory#findAutowireCandidates:搜索类型匹配的beand的Map
根据注解进行依赖注入的主要工作,就是根据标注的字段的类型来搜索符合的bean,并将类型匹配的bean注入到字段中。而搜索bean的工作在这个方法中实现:
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;
}
步骤总结:
- 将获取类型匹配的Bean工作交给BeanFactoryUtils.beanNamesForTypeIncludingAncestors。该方法除了当前beanFactory还会递归对父parentFactory进行查找
- 如果注入类型是特殊类型或其子类,会将特殊类型的实例添加到结果
- 对结果进行筛选
- BeanDefinition的autowireCandidate属性,表示是否允许该bena注入到其他bean中,默认为true
- 泛型类型的匹配,如果存在的话
- Qualifier注解。如果存在Qualifier注解的话,会直接比对Qualifier注解中指定的beanName。需要注意的是,Spring处理自己定义的Qualifier注解,还支持javax.inject.Qualifier注解
- 如果筛选后,结果为空,Spring会放宽筛选条件,再筛选一次
DefaultListableBeanFactory#isAutowireCandidate 判断指定的descriptor是否能够被注入
确定指定的bean定义是否有资格作为autowire候选项注入到声明匹配类型依赖项的其他bean中。
@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注解)
@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的别名)
/**
* 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参考如下:我们发现它的类型都是带有泛型的
泛型依赖注入
有了上面的源码解析,详细下面的案例结果,我们是能够猜到的:
// 向容器内注入Bean(此处忽略)
@Autowired
private GenericBean<String, Object> objectGenericBean; // 这样注入报错:说找不到Bean
@Autowired
private GenericBean objectGenericBean; // 依然报错,但是和上面报错不同,这里报错是找到了2个,匹配不到
// 因此这种情况要特别的注意:如果字段名不是objectGenericBean,而是objectGeneric,就不会报错了
这里面为了协助理解,附图解释:
从上图可以看出,如果我们注入的时候不指定的泛型,它就是两个 ?,属于通配符。所以能够匹配容器里的同类型的所有的Bean,所以如果筛选不出来only one,那就报错了。(因此,如果容器只有这一个类型的Bean,那就木有问题,就是它了)
接下来再看看这个case:
// 我向容器里只注入一个该类型的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,默认都是单例的
@Autowired和@Resource的区别
直观的错误理解:
- @Autowired根据类型进行注入,若没有找到该类型Bean会报错
- @Autowired根据类型进行注入, 若出现多个类型的Bean,会报错
- @Resource根据名称进入注入
解答这些误解(给出正确答案):
- @Autowired根据类型进行注入这话没毛病,但是若没有找到该类型的Bean,若设置了属性required=false也是不会报错的
- @Autowired注入若找到多个类型的Bean,也不会报错,比如下面三种情况,都不会报错~
// 向容器中注入两个相同类型的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.buqiong.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的版本问题?
需要说明的是,它和@Qualifier的区别:它们的生效阶段不一样。
@Qualifier:它在寻找同类型的Bean的时候就生效了,在方法findAutowireCandidates这里去寻找候选的Bean的时候就生效了,只会找出一个(或者0个出来)
@Autowired自带的根据字段名匹配:发生在若找出多个同类型Bean的情况下,会根据此字段名称determine一个匹配上的出来
@Resource装配顺序解释:
- 如果既没有指定name,又没有指定type,则自动先按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配
- 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常
- 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常
- 如果指定了type,则从上下文中找到类似匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常
在Spring4.2之前,@Resource并不支持@Primary,但是在4.2之后,@Resource已经全面支持了@Primary以及提供了对@Lazy的支持。
注意:它对@Lazy支持并不是ContextAnnotationAutowireCandidateResolver来处理的,全部由CommonAnnotationBeanPostProcessor这个类里面的方法处理
它的核心处理办法是ResourceElement.inject,最终调用ResourceElement.getResourceToInject方法:
private class ResourceElement extends LookupElement {
@Override
protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) : getResource(this, requestingBeanName));
}
}
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、@Qualifier一模一样,他们可以混用(唯一区别是:只有Spring的亲儿子@Autowired只是required = false)
@Autowired等三个注解都可议注入List、Map等等。但是要求必须至少有一个,否则请写@Autowired(required = false),显然此时如果你用的@Resource或者@Inject就只能眼睁睁看着它报错了~所以推荐大家使用Spring的亲儿子:@Autowired吧~~~ 并且大都情况下推荐构造函数注入
泛型依赖注入的另一优点实例(Base基类设计)
泛型依赖注入在书写Base基类的时候,有非常大的用处,可以省略不少的代码,更好的规划和设计代码。
// 我定义的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注解也是能够实现依赖注入的:
@Configuration
public class RootConfig {
@Bean
public Person person() {
return new Person();
}
// 这样就能够实现依赖注入了~~~
@Value("#{person}")
private Person person;
}
注意细节:
- 只能是#{person}而不能是${person}
- person表示beanName,因此请保证此Bean必须存在。比如若写成这样@Value("#{person2}")就报错:
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表达式也有这样的能力(原理是StandardBeanExpressionResolver和StandardEvaluationContext),只是一般我们不这么干。
@Value(#{})与@Value(${})的区别
- @Value(#{}): SpEL表达式。@Value("#{}") 表示SpEl表达式通常用来获取bean的属性,或者调用bean的某个方法。当然还有可以表示常量
- @Value(${}):获取配置文件中的属性值
它俩可以结合使用:比如:@Value("#{'${spring.redis.cluster.nodes}'.split(',')}")是一个结合使用的案例~ 这样就可以把如下配置解析成List了
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
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· SQL Server 2025 AI相关能力初探
· Linux系列:如何用 C#调用 C方法造成内存泄露
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 阿里最新开源QwQ-32B,效果媲美deepseek-r1满血版,部署成本又又又降低了!
· 单线程的Redis速度为什么快?
· SQL Server 2025 AI相关能力初探
· AI编程工具终极对决:字节Trae VS Cursor,谁才是开发者新宠?
· 展开说说关于C#中ORM框架的用法!