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

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

前言

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

看本篇文章之前,建议至少已经了解Spring容器的一个大致过程,最好已经看过博文(或者知道): 【小家Spring】AbstractAutowireCapableBeanFactory#populateBean实现Bean的属性赋值和initializeBean对Bean的初始化

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

Demo Show(自动装配的)

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

// 准备一个带泛型的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方法就是来处理该注解的。(因此,我们的源码解析从此处开始吧~~~~)

	@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注入

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

  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的工作在这个方法中实现:

	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.

	@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,默认都是单例的

Spring Boot中RedisTemplate<Object, Object>和StringRedisTemplate的注入问题

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

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

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

	// 容器中不存在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;
	}

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

    // 这样注入是失败的,因为容器中并没有该类型的(泛型类型)的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呢?

// 我们自己注入一个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,也不会报错,比如下面三种情况,都不会报错~
// 向容器中注入两个相同类型的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方法:

	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@Qualifire一模一样,他们可以混用(唯一区别是:只有Spring的亲儿子@Autowired只是required = false) @Autowired等三个注解都可议注入List、Map等等。但是要求必须至少有一个,否则请写@Autowired(required = false),显然此时如果你用的@Resource或者@Inject就只能眼睁睁看着它报错了~所以推荐大家使用Spring的亲儿子@Autowired~~~ 并且大都情况下推荐构造函数注入

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

泛型依赖注入在书写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;
}

细节: 1、只能是#{person}而不能是${person} 2、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表达式也有这样的能力(原理是StandardBeanExpressionResolverStandardEvaluationContext),只是一般我们不这么干。

@Value(#{})与@Value(${})的区别
  1. @Value(#{}): SpEL表达式 @Value("#{}") 表示SpEl表达式通常用来获取bean的属性,或者调用bean的某个方法。当然还有可以表示常量
  2. @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

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

我来说两句

0 条评论
登录 后参与评论

相关文章

  • 【小家java】java8新特性之---反射获取方法参数名

    方法的参数名,在很多时候我们是需要反射得到的。但是在java8之前,代码编译为class文件后,方法参数的类型是固定的,但参数名称却丢失了,这和动态语言严...

    BAT的乌托邦
  • 【小家Spring】高性能关键技术之---体验Spring MVC的异步模式(ResponseBodyEmitter、SseEmitter、StreamingResponseBody) 高级使用篇

    上篇博文:【小家Spring】高性能关键技术之—体验Spring MVC的异步模式(Callable、WebAsyncTask、DeferredResult) ...

    BAT的乌托邦
  • [享学Netflix] 三十四、Hystrix目标方法执行逻辑源码解读:executeCommandAndObserve

    前面用了几篇文章内容分析了Hystrix执行fallback的逻辑以及导致降级的各种情况,但是作为正常执行的逻辑均还没涉及。比如需要知道:在线程池隔离下如何执行...

    BAT的乌托邦
  • freemarker导出复杂样式的Excel

    https://gitee.com/suveng/demo/tree/master/chapter.002

    suveng
  • 啥?前后端数据到现在还是明文的?DES与3DES 加解密了解一下

    我们在线上经常使用DES加密用户id,以下简称(encodeId),后端传给前端,前端会使用localStorage保存encodeId,然后调用接口时将enc...

    周三不加班
  • [Protocol Buffer]Java使用Protocol Buffer

    下面一个例子是一个简单的通讯录,可以读写人物信息到文件。每个人都有姓名,ID,email和电话号码。

    wOw
  • [NewLife.XCode]数据模型文件

    NewLife.XCode是一个有10多年历史的开源数据中间件,由新生命团队(2002~2019)开发完成并维护至今,以下简称XCode。

    大石头
  • [NewLife.XCode]角色权限

    NewLife.XCode是一个有10多年历史的开源数据中间件,支持nfx/netcore,由新生命团队(2002~2019)开发完成并维护至今,以下简称XCo...

    大石头
  • Tomcat就是这么简单

    什么是Tomcat Tomcat简单的说就是一个运行JAVA的网络服务器,底层是Socket的一个程序,它也是JSP和Serlvet的一个容器。 ---- 为什...

    Java3y
  • 物竞天择,适者生存,架构进化之路

    作者介绍 沈剑,58到家技术总监,技术委员会负责人。曾任百度高级工程师,58高级架构师,C2C技术部负责人,58技术委员会主席! 核心内容:58同城流量从小到大...

    架构师小秘圈

扫码关注云+社区

领取腾讯云代金券