@Autowried入门和源码分析
Posted ychuang
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了@Autowried入门和源码分析相关的知识,希望对你有一定的参考价值。
话不多说直接上代码:
声明一个接口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对象创建的时候注入的,看下面的代码,对象创建之前初始化环境的代码太多,这里就不分析了:
// 调用doCreateBean 创建bean
Object beanInstance = doCreateBean(beanName, mbdToUse, args);
//设置属性,非常重要
populateBean(beanName, mbd, instanceWrapper);
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;
//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;
@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);
if (result == null)
result = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
@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
protected boolean matchesBeanName(String beanName, @Nullable String candidateName)
return (candidateName != null &&
(candidateName.equals(beanName) || ObjectUtils.containsElement(getAliases(beanName), candidateName)));
到此就结束了。。。
以上是关于@Autowried入门和源码分析的主要内容,如果未能解决你的问题,请参考以下文章
ThreadLocal源码分析_01 入门案例以及表层源码分析
ThreadLocal源码分析_01 入门案例以及表层源码分析