Spring注解系列 - @Autowired注解
文章目录
- 使用总结
- 注入原理
- @Autowired 注入过程
- InjectionMetadata
- InjectedElement
- 依赖注入查找过程
- findAutowireCandidates
- 缓存注入信息
- @Resource 注解
使用总结
@Autowired注解可以自动将所需的依赖对象注入到类的属性、构造方法或方法中,从而减少手动注入依赖的代码,并提高代码的可维护性和可测试性。它是Spring容器配置的一个重要注解,与@Required、@Primary、@Qualifier等注解同属容器配置范畴。
- @Autowired是按照类型注入依赖对象,默认情况下它要求依赖对象必须存在,如果允许null值,可以设置它的required属性为false。如果我们想使用按照Bean的名称来装配,可以结合
@Qualifier
注解一起使用。如果通过类型匹配找到多个candidate,在没有@Qualifier、@Primary注解的情况下,会使用对象名作为最后的fallback匹配
使用场景
- 属性注入(Field Injection):
在类的属性上使用@Autowired注解,Spring会自动注入一个相应类型的实例到该属性中。
例如:@Autowired private MyRepository myRepository;
- 构造方法注入(Constructor Injection):
在类的构造方法上使用@Autowired注解,Spring会在创建类实例时,通过构造方法注入相应的依赖。例如:
@Autowired
public MyService(MyRepository myRepository) { this.myRepository = myRepository;
}
注意:从Spring 4.3开始,如果目标bean只有一个构造器,则无需再单独放置@Autowired注解,Spring会自动完成注入。但如果存在多个构造器,则需要选择一个并标注上@Autowired注解。
- 方法注入(Setter Injection):
在类的setter方法上使用@Autowired注解,Spring会调用该setter方法并注入相应的依赖。
例如:
@Autowired public void setMyRepository(MyRepository myRepository) { this.myRepository = myRepository;
}
高级用法
- 数组注入:
如果需要将多个同类型的bean注入到一个数组中,可以在数组属性或方法上使用@Autowired注解。例如:T 为具体需要注入的类型
@Autowired
private T[] autowiredElements;
- 集合注入:
同样地,也可以将多个同类型的bean注入到一个集合(如Set、List、Collection等)中。例如:T 为具体需要注入的类型
@Autowired public void setMyRepositories(Set<T> set) { this.autowiredSet = set;
}
- Map注入:
如果注入的Map中Key为String类型,并且代表每个bean的name,value为指定的bean类型,则可以直接注入。例如:T 为具体需要注入的类型
@Autowired public void setMyRepositoryMap(Map<String, T> map) { this.autowiredMap = map;
}
注意事项
-
Bean的注册:
被注入的依赖必须是Spring容器中的一个Bean。可以通过@Component
、@Service
、@Repository
等注解将类注册为Spring容器中的Bean。 -
自动装配模式:
默认情况下@Autowired
按类型装配。如果有多个同类型的Bean,可以使用@Qualifier
注解来指定具体的Bean。 -
required属性:
默认情况下@Autowired
的required属性为true,这意味着Spring容器在启动时必须找到一个匹配的Bean。如果没有找到匹配的Bean,会抛出异常。可以通过将required属性设置为false来避免这种情况:
@Autowired(required=false)
private MyRepository myRepository;
注入原理
实现@Autowired注解功能的是一个后置处理器org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor
public class AutowiredAnnotationBeanPostProcessor implements SmartInstantiationAwareBeanPostProcessor,MergedBeanDefinitionPostProcessor, PriorityOrdered, BeanFactoryAware {
}
当一个Bean创建成功后,根据其生命周期,会进行Bean属性填充(populateBean)过程,源码位于org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory#populateBean
注意:这里@Autowired不是在postProcessAfterInstantiation里实现的
而是在postProcessProperties
这个回调里实现字段的注入的
@Autowired 注入过程
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {// 获取要进行注入的信息InjectionMetadata metadata = findAutowiringMetadata(beanName, bean.getClass(), pvs);try {metadata.inject(bean, beanName, pvs);} catch (XxxException ex) {// 省略异常处理}return pvs;
}private InjectionMetadata findAutowiringMetadata(String beanName, Class<?> clazz, @Nullable PropertyValues pvs) {// 从缓存中获取String cacheKey = (StringUtils.hasLength(beanName) ? beanName : clazz.getName());InjectionMetadata metadata = this.injectionMetadataCache.get(cacheKey);if (InjectionMetadata.needsRefresh(metadata, clazz)) {// 刷新缓存synchronized (this.injectionMetadataCache) {// Double-Checkmetadata = 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;
}
InjectionMetadata
org.springframework.beans.factory.annotation.InjectionMetadata包含了一个Class的注入的元数据信息,哪些字段和哪些方法需要进行注入
InjectionMetadata#inject进行注入实际上是调用 InjectedElement#inject
方法进行注入
public void inject(Object target, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {Collection<InjectedElement> checkedElements = this.checkedElements;Collection<InjectedElement> elementsToIterate =(checkedElements != null ? checkedElements : this.injectedElements);if (!elementsToIterate.isEmpty()) {for (InjectedElement element : elementsToIterate) {element.inject(target, beanName, pvs);}}
}
InjectedElement
InjectedElement包含以下几种类型:
- AutowiredFieldElement:@Autowire注解标注的字段
- AutowiredMethodElement:@Autowire注解标注的方法
- LookupElement:由CommonAnnotationBeanPostProcessor中定义的内部类LookupElement表示,这个CommonAnnotationBeanPostProcessor后面会提到
以字段注入为例
/*** Object bean: @Autowired所在类的bean实例* String beanName:@Autowired所在类的bean名称* PropertyValues pvs:属性值**/
@Override
protected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {Field field = (Field) this.member;Object value;if (this.cached) {try {value = resolveCachedArgument(beanName, this.cachedFieldValue);} catch (BeansException ex) {// 缓存的对象和想要注入的不兼容,因为是按名称注入// 可能多次注入过程中不同类型的对象因为使用了同一个名称而导致覆盖,从而不兼容// 兜底操作this.cached = false;value = resolveFieldValue(field, bean, beanName);}} else {// 获取该Field要注入的值value = resolveFieldValue(field, bean, beanName);}if (value != null) { // 反射进行赋值ReflectionUtils.makeAccessible(field);field.set(bean, value);}
}@Nullable
private Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {DependencyDescriptor desc = new DependencyDescriptor(field, this.required);desc.setContainingClass(bean.getClass());Set<String> autowiredBeanNames = new LinkedHashSet<>(2);Assert.state(beanFactory != null, "No BeanFactory available");TypeConverter typeConverter = beanFactory.getTypeConverter();Object value;try {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) {Object cachedFieldValue = desc;registerDependentBeans(beanName, autowiredBeanNames);if (value != null && autowiredBeanNames.size() == 1) {String autowiredBeanName = autowiredBeanNames.iterator().next();if (beanFactory.containsBean(autowiredBeanName) &&beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {cachedFieldValue = new ShortcutDependencyDescriptor(desc, autowiredBeanName);}}this.cachedFieldValue = cachedFieldValue;this.cached = true;} else {this.cachedFieldValue = null; // cached flag remains false}}}return value;
}
关键的就是beanFactory.resolveDependency(desc, beanName, 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;}// 例如 UserService 这个ClassClass<?> type = descriptor.getDependencyType();// 通过AutowireCandidateResolver获取一次值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());try {return converter.convertIfNecessary(value, type, descriptor.getTypeDescriptor());} catch (UnsupportedOperationException ex) {// A custom TypeConverter which does not support TypeDescriptor resolution...return (descriptor.getField() != null ?converter.convertIfNecessary(value, type, descriptor.getField()) :converter.convertIfNecessary(value, type, descriptor.getMethodParameter()));}}// 针对多个值一起注入的场景,比如@Autowired的是一个集合类型Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);if (multipleBeans != null) {return multipleBeans;}// 根据依赖描述符查找候选的Bean对象Map<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);if (matchingBeans.isEmpty()) {if (isRequired(descriptor)) {// required 为 true,但是没找到,直接抛异常raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);}return null;}String autowiredBeanName;Object instanceCandidate;if (matchingBeans.size() > 1) {autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);if (autowiredBeanName == null) {if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {return descriptor.resolveNotUnique(descriptor.getResolvableType(), 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 {// 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);}
}
findAutowireCandidates
根据类型查找所有候选的可注入的对象,下面就是@Autowired按类型注入的实现过程了
protected Map<String, Object> findAutowireCandidates(@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {// 根据类型查找BeanFactory 中该类型对应的所有Bean名称String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this, requiredType, true, descriptor.isEager());Map<String, Object> result = CollectionUtils.newLinkedHashMap(candidateNames.length);// 首先从 this.resolvableDependencies 这个缓存中获取for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {Class<?> autowiringType = classObjectEntry.getKey();if (autowiringType.isAssignableFrom(requiredType)) {Object autowiringValue = classObjectEntry.getValue();autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);if (requiredType.isInstance(autowiringValue)) {result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);break;}}}for (String candidate : candidateNames) {// isSelfReference: 判断是否@Autowired的是beanNameif (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {addCandidateEntry(result, candidate, descriptor, requiredType);}}if (result.isEmpty()) {boolean multiple = indicatesMultipleBeans(requiredType);// Consider fallback matches if the first pass failed to find anything...DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();for (String candidate : candidateNames) {if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor) &&(!multiple || getAutowireCandidateResolver().hasQualifier(descriptor))) {addCandidateEntry(result, candidate, descriptor, requiredType);}}if (result.isEmpty() && !multiple) {// Consider self references as a final pass...// but in the case of a dependency collection, not the very same bean itself.for (String candidate : candidateNames) {if (isSelfReference(beanName, candidate) &&(!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) &&isAutowireCandidate(candidate, fallbackDescriptor)) {addCandidateEntry(result, candidate, descriptor, requiredType);}}}}return result;
}
BeanFactoryUtils#beanNamesForTypeIncludingAncestors(ListableBeanFactory, Class<?>, boolean, boolean)
这个方法的作用是根据依赖的类型从BeanFactory中获取该类型所有的Bean名称,关键是String[] result = lbf.getBeanNamesForType(type, includeNonSingletons, allowEagerInit);
这句
public static String[] beanNamesForTypeIncludingAncestors(ListableBeanFactory lbf, Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {Assert.notNull(lbf, "ListableBeanFactory must not be null");String[] result = lbf.getBeanNamesForType(type, includeNonSingletons, allowEagerInit);if (lbf instanceof HierarchicalBeanFactory) {HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {String[] parentResult = beanNamesForTypeIncludingAncestors((ListableBeanFactory) hbf.getParentBeanFactory(), type, includeNonSingletons, allowEagerInit);result = mergeNamesWithParent(result, parentResult, hbf);}}return result;
}
缓存注入信息
当一个Bean创建时,populateBean这一步需要获取该类上有哪些元素是需要@Autowired的。AutowiredAnnotationBeanPostProcessor 同时实现了SmartInstantiationAwareBeanPostProcessor和MergedBeanDefinitionPostProcessor,解析注入信息这个过程就是在MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition
实现的
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);metadata.checkConfigMembers(beanDefinition);
}
@Resource 注解
@Resource默认按照名称自动注入,由J2EE提供,需要导入包javax.annotation.Resource(spring 6以后需要改成jakarta.annotation.Resource
)。
@Resource有两个重要的属性:name和type,而Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以,如果使用name属性,则使用按名称注入的自动注入策略,而使用type属性时则使用按类型自动注入的策略。如果既不指定name也不指定type属性,这时将通过反射机制使用按名称自动注入策略进行注入。
@Resource装配顺序:
- 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常。
- 如果指定了name,则从上下文中查找名称匹配的bean进行装配,找不到则抛出异常。
- 如果指定了type,则从上下文中找到类似匹配的唯一bean进行装配,找不到或是找到多个,都会抛出异常。
- 如果既没有指定name,又没有指定type,则自动按照按名称匹配方式进行装配;