话不多说直接上代码:

声明一个接口userDao:

package ioc.hello;

public interface UserDao {
public void test();
}

2个实现类:

package ioc.hello;

import org.springframework.context.annotation.Primary;
import org.springframework.stereotype.Component;
@Component("userdao")
public class UserDaoImpl implements UserDao {
public void test() {
System.out.println("userDaoImpl...");
}
}
package ioc.hello;

import org.springframework.stereotype.Component;

@Component("userdao2")
public class UserDaoImpl2 implements UserDao {
public void test() {
System.out.println("userDaoImpl2...");
}
}

UserService类注入接口:

package ioc.hello;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; @Component("userservice")
public class UserService {
@Autowired
private UserDao dao;
public void test() {
dao.test();
}
}

测试类:

package ioc.hello;
import ioc.AppConfig;
import org.springframework.context.annotation.AnnotationConfigApplicationContext; public class TestIOC {
public static void main(String[] args) {
AnnotationConfigApplicationContext annotationConfigApplicationContext = new AnnotationConfigApplicationContext();
annotationConfigApplicationContext.register(AppConfig.class);
annotationConfigApplicationContext.refresh();
UserService service = annotationConfigApplicationContext.getBean(UserService.class);
service.test();
}
}

我们直接运行以上代码的话讲道理是会报错的,为什么呢?因为我们的UserDao接口有2个实现类,但是我们在注入的时候注入的是接口类型,而且注入的名称是dao;
看效果吧!

Caused by: org.springframework.beans.factory.NoUniqueBeanDefinitionException: No qualifying bean of type 'ioc.hello.UserDao' available: expected single matching bean but found 2: userdao,userdao2
at org.springframework.beans.factory.config.DependencyDescriptor.resolveNotUnique(DependencyDescriptor.java:215)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1139)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1088)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:581)
... 13 more

上面的报错相信大家都很熟悉吧。

如果我们把注入的接口的名称修改为userdao或者是userdao2的话就会不报错了,当然我们也可以注入dao,但是在注入的时候我们需要做一下处理,可以在要注入的实现类加一个@Primary注解,也可以在注入的时候加一个@Qualifier(或者是Resource)注解,指定注入的对象,这个知识点此处不做介绍;

package ioc.hello;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component; @Component("userservice")
public class UserService {
@Autowired
private UserDao userdao;
public void test() {
userdao.test();
}
}

运行结果:

userDaoImpl...

下面我们就此看以下Spring中的源码吧!

首先不得不说,我们的@Autowired注入是在对象创建的时候一起注入的,换句话说就是比如我们上面的案例中,在UserService中注入接口,是在UserService对象创建的时候注入的,看下面的代码,对象创建之前初始化环境的代码太多,这里就不分析了:

org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#createBean
调用了doCreateBean方法;在此处打上条件断点,当beanName.equals("userservice"),然后调用了populateBean方法;
// 调用doCreateBean 创建bean
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
//设置属性,非常重要
populateBean(beanName, mbd, instanceWrapper);
org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean
getBeanPostProcessors()方法会得到所有的后置处理器,遍历以后,当后置处理器为AutowriedAnnotationPoetProcessor的时候完成了@Autowried注入
if (!mbd.isSynthetic() && hasInstantiationAwareBeanPostProcessors()) {
for (BeanPostProcessor bp : getBeanPostProcessors()) {
if (bp instanceof InstantiationAwareBeanPostProcessor) {
InstantiationAwareBeanPostProcessor ibp = (InstantiationAwareBeanPostProcessor) bp;
if (!ibp.postProcessAfterInstantiation(bw.getWrappedInstance(), beanName)) {
continueWithPropertyPopulation = false;
break;
}
}
}
}
org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor#postProcessPropertyValues
//findAutowiringMetadata方法会找到注入的所有类的属性
InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);
try {
metadata.inject(bean, beanName, pvs);
}
private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {
// Fall back to class name as cache key, for backwards compatibility with custom callers.
String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());
//injectionMetadataCache可以理解为,是所有的bean,从所有的bean中得到service
InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
synchronized (this.injectionMetadataCache) {
metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
if (metadata != null) {
metadata.clear(pvs);
}
metadata = buildAutowiringMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);
}
}
}
return metadata;
}
org.springframework.beans.factory.annotation.InjectionMetadata#inject
@Override
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {
//得到dao的属性名称
Field field = (Field) this.member;
Object value;
if (this.cached) {
value = resolvedCachedArgument(beanName, this.cachedFieldValue);
}
else {
DependencyDescriptor desc = new DependencyDescriptor(field, this.required);
desc.setContainingClass(bean.getClass());
Set<String> autowiredBeanNames = new LinkedHashSet<>(1);
Assert.state(beanFactory != null, "No BeanFactory available");
TypeConverter typeConverter = beanFactory.getTypeConverter();
try {
//得到注入的对象,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方法注入,把value注入到了bean里面
field.set(bean, value);
}
}
}
org.springframework.beans.factory.support.DefaultListableBeanFactory#resolveDependency
if (result == null) {
result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
}
org.springframework.beans.factory.support.DefaultListableBeanFactory#doResolveDependency
@Nullable
public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,
@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException { InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);
try {
Object shortcut = descriptor.resolveShortcut(this);
if (shortcut != null) {
return shortcut;
} Class<?> type = descriptor.getDependencyType();
Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);
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()));
} Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
if (multipleBeans != null) {
return multipleBeans;
}
//从Spring工厂中得到该接口下所有的实现类的bean;这个方法最终是通过beanDefinitionNames得到的bean
Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);
if (matchingBeans.isEmpty()) {
if (isRequired(descriptor)) {
raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);
}
return null;
} String autowiredBeanName;
Object instanceCandidate; //如果接口有多个实现类的话就走这边,要遍历这些bean找到适合的对象返回
if (matchingBeans.size() > 1) {
autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
if (autowiredBeanName == null) {
if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
return descriptor.resolveNotUnique(type, matchingBeans);
}
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 {
//如果接口只有一个实现类的话,就直接得到第一个实现类作为bean返回了
// We have exactly one match.
Map.Entry<String, Object> entry = matchingBeans.entrySet().iterator().next();
autowiredBeanName = entry.getKey();
instanceCandidate = entry.getValue();
} if (autowiredBeanNames != null) {
autowiredBeanNames.add(autowiredBeanName);
}
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;
}
if (!ClassUtils.isAssignableValue(type, result)) {
throw new BeanNotOfRequiredTypeException(autowiredBeanName, type, instanceCandidate.getClass());
}
return result;
}
finally {
ConstructorResolver.setCurrentInjectionPoint(previousInjectionPoint);
}
}

以上代码中:如果接口只有一个实现类的话就已经结束返回了,返回了接口的实现类,返回以后通过 field.set(bean, value);实现了注入;所以如果接口只有一个实现类的话,我们在注入接口的时候,属性名称可以随便写,他都可以注入成功;

下面就是有多个实现类的逻辑了;
我们来看下matchingBeans.size() > 1这段逻辑代码,调用了determineAutowireCandidate(matchingBeans, descriptor);

org.springframework.beans.factory.support.DefaultListableBeanFactory#determineAutowireCandidate

protected String determineAutowireCandidate(Map<String, Object> candidates, DependencyDescriptor descriptor) {
Class<?> requiredType = descriptor.getDependencyType();
//次处就是上面提到的处理@Primary注解的地方啦
String primaryCandidate = determinePrimaryCandidate(candidates, requiredType);
if (primaryCandidate != null) {
return primaryCandidate;
}
String priorityCandidate = determineHighestPriorityCandidate(candidates, requiredType);
if (priorityCandidate != null) {
return priorityCandidate;
}
// Fallback
for (Map.Entry<String, Object> entry : candidates.entrySet()) {
String candidateName = entry.getKey();
Object beanInstance = entry.getValue();
       //主要看这个方法:matchesBeanName; descriptor.getDependencyName()得到的是service类里面注入的接口的名称
if ((beanInstance != null && this.resolvableDependencies.containsValue(beanInstance)) ||
matchesBeanName(candidateName, descriptor.getDependencyName())) {
return candidateName;
}
}
return null;
}

org.springframework.beans.factory.support.DefaultListableBeanFactory#matchesBeanName

根据注入的属性的名称和bean的name做对比,是否equals,如果是的话就直接返回这个bean了,如果不是的话报错啦。
所以这个地方就解释了我们上面报错的问题了,注入了dao,会分别通过用userdao和userdao2和dao做字符串比较,如果两个都满足的话就会找不到bean,继而就报错,如果找到了就直接注入了找到的这个bean了。
protected boolean matchesBeanName(String beanName, @Nullable String candidateName) {
return (candidateName != null &&
(candidateName.equals(beanName) || ObjectUtils.containsElement(getAliases(beanName), candidateName)));
}

到此就结束了。。。

@Autowried入门和源码分析的更多相关文章

  1. Quartz学习--二 Hello Quartz! 和源码分析

    Quartz学习--二  Hello Quartz! 和源码分析 三.  Hello Quartz! 我会跟着 第一章 6.2 的图来 进行同步代码编写 简单入门示例: 创建一个新的java普通工程 ...

  2. Kubernetes Job Controller 原理和源码分析(一)

    概述什么是 JobJob 入门示例Job 的 specPod Template并发问题其他属性 概述 Job 是主要的 Kubernetes 原生 Workload 资源之一,是在 Kubernete ...

  3. Android Debuggerd 简要介绍和源码分析(转载)

    转载: http://dylangao.com/2014/05/16/android-debuggerd-%E7%AE%80%E8%A6%81%E4%BB%8B%E7%BB%8D%E5%92%8C%E ...

  4. Java并发编程(七)ConcurrentLinkedQueue的实现原理和源码分析

    相关文章 Java并发编程(一)线程定义.状态和属性 Java并发编程(二)同步 Java并发编程(三)volatile域 Java并发编程(四)Java内存模型 Java并发编程(五)Concurr ...

  5. Kubernetes Job Controller 原理和源码分析(二)

    概述程序入口Job controller 的创建Controller 对象NewController()podControlEventHandlerJob AddFunc DeleteFuncJob ...

  6. Kubernetes Job Controller 原理和源码分析(三)

    概述Job controller 的启动processNextWorkItem()核心调谐逻辑入口 - syncJob()Pod 数量管理 - manageJob()小结 概述 源码版本:kubern ...

  7. jQuery静态方法globalEval使用和源码分析

    Eval函数大家都很熟悉,但是globalEval方法却很少使用,大多数参考手册也没有相关api,下面就对其用法和源码相应介绍: jQuery.globalEval()函数用于全局性地执行一段Java ...

  8. 微服务生态组件之Spring Cloud LoadBalancer详解和源码分析

    Spring Cloud LoadBalancer 概述 Spring Cloud LoadBalancer目前Spring官方是放在spring-cloud-commons里,Spring Clou ...

  9. Android开发学习之路-LruCache使用和源码分析

    LruCache的Lru指的是LeastRecentlyUsed,也就是近期最少使用算法.也就是说,当我们进行缓存的时候,如果缓存满了,会先淘汰使用的最少的缓存对象. 为什么要用LruCache?其实 ...

随机推荐

  1. Linux文件权限-笔记

    文件权限共10个字符,第一个字符表示该文件是[文件夹]或[文件]——如果是字符“d"则表示该文件是文件夹:如果是字符“-”则表示是文件. 后九个字符,三个一组,共三组,分别表示[所有者权限] ...

  2. Microsoft Remote Desktop 10.3.12 下载

    下载地址:https://mac.softpedia.com/

  3. 操作系统-中断(2)IA-32/Linux的向量中断方式

    一.Intel定义下的异常和中断 不同体系和教材往往对异常和中断有不同的定义. Intel定义:中断是一种典型的由I/O设备触发的.与当前正在执行的指令无关的异步事件:而异常是处理器执行一条指令时,由 ...

  4. 2020.5.28 第八篇 Scrum冲刺博客

    Team:银河超级无敌舰队 Project:招新通 项目冲刺集合贴:链接 目录 一.每日站立会议 1.1 会议照片 1.2 项目完成情况 二.项目燃尽图 三.签入记录 3.1 代码/文档签入记录 3. ...

  5. Pytorch中torch.load()中出现AttributeError: Can't get attribute

    原因:保存下来的模型和参数不能在没有类定义时直接使用. Pytorch使用Pickle来处理保存/加载模型,这个问题实际上是Pickle的问题,而不是Pytorch. 解决方法也非常简单,只需显式地导 ...

  6. .NET Core实用技巧(一)如何将EF Core生成的SQL语句显示在控制台中

    目录 .NET Core实用技巧(一)如何将EF Core生成的SQL语句显示在控制台中 前言 笔者最近在开发和维护一个.NET Core项目,其中使用几个非常有意思的.NET Core相关的扩展,在 ...

  7. 「从零单排canal 07」 parser模块源码解析

    基于1.1.5-alpha版本,具体源码笔记可以参考我的github:https://github.com/saigu/JavaKnowledgeGraph/tree/master/code_read ...

  8. Redis主从复制(读写分离)

    主从复制(读写分离):读在从库读,写在主库写. 主从复制的好处:避免redis单点故障构建读写分离架构,满足读多写少的需求. 主从架构: 操作(启动实例,在一台机器上启动不同的实例,进行伪主从复制): ...

  9. GPU虚拟机创建时间深度优化

    ​桔妹导读:GPU虚拟机实例创建速度慢是公有云面临的普遍问题,由于通常情况下创建虚拟机属于低频操作而未引起业界的重视,实际生产中还是存在对GPU实例创建时间有苛刻要求的业务场景.本文将介绍滴滴云在解决 ...

  10. AcWing243一个简单的整数问题2(树状数组+差分+前缀和规律)

    题目地址:https://www.acwing.com/problem/content/244/ 题目描述: 给定一个长度为N的数列A,以及M条指令,每条指令可能是以下两种之一: 1.“C l r d ...