在面试的时候经常有面试官会问 @Autowire 和 @Resource 的区别,这篇文章我就通过源码详细的分析 @Autowire 依赖注入的过程和 @Resource 的注入过程,最后再来比较这两者的区别。
可能结果和你之前的认知不一样
@Autowire
既然要分析 @Autowire 的注入过程,首先要了解的是谁来执行注入的。这里就涉及到 Spring 的一个重要知识点BeanPostProcessor
,但是本篇文章的重点不是讲解 BeanPostProcessor
,小伙伴们只需要知道实现了这个接口可以做很多事情,而 AutowiredAnnotationBeanPostProcessor
就借此实现了依赖的自动注入。
注入点
在依赖注入的过程中涉及一个重要的知识点就是注入点,什么是注入点呢,简单来说就是需要依赖注入的地方。例如下面代码所示:
public class TestAutowired {
// 属性注入点
@Autowired
private User user;
// 方法注入点
@Autowired
public TestAutowired(User user1) {
}
}
想要依赖注入的话,我们首先要做的事情就是找到所有的注入点。
注入点的查找
注入点的查找,就需要关注 AutowiredAnnotationBeanPostProcessor
中的 postProcessMergedBeanDefinition 方法。
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
// 找到找到所有注入点
InjectionMetadata metadata = findAutowiringMetadata(beanName, beanType, null);
metadata.checkConfigMembers(beanDefinition);
}
// 找到注入点
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) {// 双重检查锁
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;
}
private InjectionMetadata buildAutowiringMetadata(final Class<?> clazz) {
// 用于存放所有的注入点
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
Class<?> targetClass = clazz;
do {
// 用于存放当前类的所有注入点
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
// 查找当前类的属性注入点
ReflectionUtils.doWithLocalFields(targetClass, field -> {
AnnotationAttributes ann = findAutowiredAnnotation(field);
if (ann != null) {
// 忽略 static 类型的属性,如果去掉这段代码,也能成功的注入静态的属性
// 所以并不是不能注入,只是 Spring 选择不去注入
if (Modifier.isStatic(field.getModifiers())) {
if (logger.isWarnEnabled()) {
logger.warn("Autowired annotation is not supported on static fields: " + field);
}
return;
}
// 是否一定要注入,可以通过 required 参数配置
boolean required = determineRequiredStatus(ann);
// 添加到注入点列表中
currElements.add(new AutowiredFieldElement(field, required));
}
});
// 查找当前类的方法注入点
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
// 找到被桥接方法或者说非桥接方法
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
// 得到标识自动注入的注解
AnnotationAttributes ann = findAutowiredAnnotation(bridgedMethod);
if (ann != null && method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
// 同样忽略静态方法,不是不能注入而是 Spring 选择不去注入
if (Modifier.isStatic(method.getModifiers())) {
if (logger.isWarnEnabled()) {
logger.warn("Autowired annotation is not supported on static methods: " + method);
}
return;
}
// 如果没有请求参数,说明根本就没有参数可以注入,但是这里并没有选择忽略
// 所以还是会调用该方法
if (method.getParameterCount() == 0) {
if (logger.isWarnEnabled()) {
logger.warn("Autowired annotation should only be used on methods with parameters: " +
method);
}
}
// 是否一定要注入,可以通过 required 参数配置
boolean required = determineRequiredStatus(ann);
// 得到属性描述
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new AutowiredMethodElement(method, required, pd));
}
});
// 添加到注入点列表中
elements.addAll(0, currElements);
// 递归解析,解析父类的出入点
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
// 封装到一个注入元数据,保存了一个类所有的注入点
return new InjectionMetadata(clazz, elements);
}
依赖注入
依赖注入的入口是 InjectedElement
中的 inject 方法,@Autowired
要注入的 Bean 的选择逻辑在 DefaultListableBeanFactory
中的 doResolveDependency 方法中。所以我们只需要关注这个方法即可。
// DefaultListableBeanFactory
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()));
}
// 处理需要注入的是一个集合的情况,例如 private List<UerService> list;
Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
if (multipleBeans != null) {
return multipleBeans;
}
// 这里会处理 Qualifier 注解,并得到所有类型和名称匹配的 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;
if (matchingBeans.size() > 1) {
/*
1. 处理 @Primary 注解,如果有且仅有一个则返回,如果超过一个抛出异常
2. 处理 @Priority 注解,找到序号最小的,有多个一样的则抛出异常
3. 找到 bean 名称和属性名称相等的 bean 对象
*/
autowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);
if (autowiredBeanName == null) {
if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {
return descriptor.resolveNotUnique(type, matchingBeans);
}
else {
return null;
}
}
instanceCandidate = matchingBeans.get(autowiredBeanName);
}
else {
// 如果只有一个匹配的就不需要再进行选择了
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);
}
}
@Resource
@Resource
和 @Autowired
同样有着注入点的概念。
注入点的查找
@Resource
的注入点查找同样也是在 BeanPostProcessor
中完成的,不过是在 CommonAnnotationBeanPostProcessor
中,但是具体的查找过程和 @Autowired
的查找过程是类似的。由于是通用的,所以除了会处理 @Resource
还会处理其他的注解,不过大家只需要关注我写注释的地方就好了。
// CommonAnnotationBeanPostProcessor
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
// 查找类的所有注入点
InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);
metadata.checkConfigMembers(beanDefinition);
}
private InjectionMetadata findResourceMetadata(String beanName, final 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) {// 双重检查锁的应用
metadata = this.injectionMetadataCache.get(cacheKey);
if (InjectionMetadata.needsRefresh(metadata, clazz)) {
if (metadata != null) {
metadata.clear(pvs);
}
// 解析并构建类的注入点
metadata = buildResourceMetadata(clazz);
this.injectionMetadataCache.put(cacheKey, metadata);
}
}
}
return metadata;
}
private InjectionMetadata buildResourceMetadata(final Class<?> clazz) {
// 用于存放所有的注入点
List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
Class<?> targetClass = clazz;
do {
final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
// 查找当前类的属性注入点
ReflectionUtils.doWithLocalFields(targetClass, field -> {
if (webServiceRefClass != null && field.isAnnotationPresent(webServiceRefClass)) {
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("@WebServiceRef annotation is not supported on static fields");
}
currElements.add(new WebServiceRefElement(field, field, null));
}
else if (ejbRefClass != null && field.isAnnotationPresent(ejbRefClass)) {
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("@EJB annotation is not supported on static fields");
}
currElements.add(new EjbRefElement(field, field, null));
}
// 如果属性上修饰了 @Resource 注解
else if (field.isAnnotationPresent(Resource.class)) {
// 忽略静态属性
if (Modifier.isStatic(field.getModifiers())) {
throw new IllegalStateException("@Resource annotation is not supported on static fields");
}
if (!this.ignoredResourceTypes.contains(field.getType().getName())) {
currElements.add(new ResourceElement(field, field, null));
}
}
});
// 查找当前类的方法注入点
ReflectionUtils.doWithLocalMethods(targetClass, method -> {
Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
return;
}
if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
if (webServiceRefClass != null && bridgedMethod.isAnnotationPresent(webServiceRefClass)) {
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("@WebServiceRef annotation is not supported on static methods");
}
if (method.getParameterCount() != 1) {
throw new IllegalStateException("@WebServiceRef annotation requires a single-arg method: " + method);
}
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new WebServiceRefElement(method, bridgedMethod, pd));
}
else if (ejbRefClass != null && bridgedMethod.isAnnotationPresent(ejbRefClass)) {
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("@EJB annotation is not supported on static methods");
}
if (method.getParameterCount() != 1) {
throw new IllegalStateException("@EJB annotation requires a single-arg method: " + method);
}
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new EjbRefElement(method, bridgedMethod, pd));
}
// 如果方法上修饰 @Resource 注解
else if (bridgedMethod.isAnnotationPresent(Resource.class)) {
// 忽略静态方法
if (Modifier.isStatic(method.getModifiers())) {
throw new IllegalStateException("@Resource annotation is not supported on static methods");
}
Class<?>[] paramTypes = method.getParameterTypes();
if (paramTypes.length != 1) {
throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);
}
if (!this.ignoredResourceTypes.contains(paramTypes[0].getName())) {
PropertyDescriptor pd = BeanUtils.findPropertyForMethod(bridgedMethod, clazz);
currElements.add(new ResourceElement(method, bridgedMethod, pd));
}
}
}
});
elements.addAll(0, currElements);
targetClass = targetClass.getSuperclass();
}
while (targetClass != null && targetClass != Object.class);
return new InjectionMetadata(clazz, elements);
}
依赖注入
我们只关注 Bean
的选择过程。
// CommonAnnotationBeanPostProcessor
protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName)
throws NoSuchBeanDefinitionException {
Object resource;
Set<String> autowiredBeanNames;
String name = element.name;
// 如果通过 name 找不到对应的 Bean,则通过类型匹配
// 最关键的判断条件就是 !factory.containsBean(name)
if (this.fallbackToDefaultTypeMatch && element.isDefaultName &&
factory instanceof AutowireCapableBeanFactory && !factory.containsBean(name)) {
autowiredBeanNames = new LinkedHashSet<>();
// 下面的 resolveDependency 在讲 @Autowired 的依赖注入的时候已经讲到了
resource = ((AutowireCapableBeanFactory) factory).resolveDependency(
element.getDependencyDescriptor(), requestingBeanName, autowiredBeanNames, null);
if (resource == null) {
throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");
}
}
else {// 通过名称找到对应的 Bean
resource = factory.getBean(name, element.lookupType);
autowiredBeanNames = Collections.singleton(name);
}
if (factory instanceof ConfigurableBeanFactory) {
ConfigurableBeanFactory beanFactory = (ConfigurableBeanFactory) factory;
for (String autowiredBeanName : autowiredBeanNames) {
if (requestingBeanName != null && beanFactory.containsBean(autowiredBeanName)) {
beanFactory.registerDependentBean(autowiredBeanName, requestingBeanName);
}
}
}
return resource;
}
@Autowired 和 @Resource 的区别
@Autowired
是 Spring 自己定义的,而@Resource
是 JDK 规范中定义的,Spring 支持这个规范。@Autowired
的 Bean 的选择过程是@Qualifier
->@Primary
->@Priority
-> 属性名称。@Resource 的选择过程是 @Resource name属性或被修饰属性名称 ->@Qualifier
->@Primary
->@Priority
。@Autowired
能被修饰在 CONSTRUCTOR、METHOD、PARAMETER、FIELD、ANNOTATION_TYPE 上。@Resource
只能被修饰在 TYPE、FIELD、METHOD 上。