@Resource注解的原理、源码

文章目录

  • 前言
  • 一、@Resource怎么用?
  • 二、那么注入做了些啥
    • 1.查找一个类的所有注入点
      • 1.1 在Spring中Bean的生态位置
      • 1.2 detail,源码解读
    • 2. 注入
      • 2.1 在Spring中bean的生态的位置
      • 2.2 detail, show me the code
  • 总结


前言

@Resource注解和@Autowired注解是咱们使用Spring的两大利器,用来进行属性注入。这篇文章来简单分析下@Resource的原理


一、@Resource怎么用?

很简单的啦,譬如如下:

@Service
public class TestService {
    @Resource
    ResourceLockDAO testDao;
}

二、那么注入做了些啥

1.查找一个类的所有注入点

1.1 在Spring中Bean的生态位置

Spring的生态代码过程中,在推断出类的构造方法,且根据构造方法创建实例之后,就进行了该bean的注入点查询过程。
详见AbstractAutowireCapableBeanFactory的doCreateBean方法中的applyMergedBeanDefinitionPostProcessors,看源码么,点进去!!

/**
 * Apply MergedBeanDefinitionPostProcessors to the specified bean definition,
 * invoking their {@code postProcessMergedBeanDefinition} methods.
 * @param mbd the merged bean definition for the bean
 * @param beanType the actual type of the managed bean instance
 * @param beanName the name of the bean
 * @see MergedBeanDefinitionPostProcessor#postProcessMergedBeanDefinition
 */
protected void applyMergedBeanDefinitionPostProcessors(RootBeanDefinition mbd, Class<?> beanType, String beanName) {
	for (BeanPostProcessor bp : getBeanPostProcessors()) {
		if (bp instanceof MergedBeanDefinitionPostProcessor) {
			MergedBeanDefinitionPostProcessor bdp = (MergedBeanDefinitionPostProcessor) bp;
			bdp.postProcessMergedBeanDefinition(mbd, beanType, beanName);
		}
	}
}

可以看到循环调用了所有BeanPostProcessor,找到MergedBeanDefinitionPostProcessor子类,调用其postProcessMergedBeanDefinition方法,此处不累赘,我直接说了,调用的CommonAnnotationBeanPostProcessor的对应方法。细心的你可能会发现,还有个叫AutowiredAnnotationBeanPostProcessor的类也有该方法,从类名,聪明的你应该猜到,这个类的该方法是为了处理@Autowired注解。
废话不多说,咱们来看看@Resource注解查询

1.2 detail,源码解读

@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
	super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
	InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);
	metadata.checkConfigMembers(beanDefinition);
}

咱们主要看findResourceMetadata方法

private InjectionMetadata findResourceMetadata(String beanName, final 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());
		// Quick check on the concurrent map first, with minimal locking.
		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;
	}
  1. 可以看到此处先从injectionMetadataCache这个map结构的cache里获取,如果获取不到才会进行下面的操作,大家是不是觉得没有必要,因为Spring是单例的,但是Spring只是默认是单例的,万一你使用的scope是原型呢?是吧?查找bean的注入点这个操作就不需要多次执行了。
  2. 看到此处的synchronized原语的上下代码,熟悉单例设计模式的同学应该兴奋了。因为这是咱们的单例模式–懒汉实现方式的原理,也就是双重检查锁,也就是在synchronized的前后分别进行判断,防止多线程同时处理,最后出错。
    下面是最重要的代码逻辑,根据重要程度,此处源码不会全部贴上来,会进行适当删减
private InjectionMetadata buildResourceMetadata(final Class<?> clazz) {
		List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
		Class<?> targetClass = clazz;

		do {
			final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();
			// 处理属性上的@Resource注解
			ReflectionUtils.doWithLocalFields(targetClass, field -> {
				if (webServiceRefClass != null && field.isAnnotationPresent(webServiceRefClass)) {
				// 如果有@WebServiceRef注解,也处理
				}
				else if (ejbRefClass != null && field.isAnnotationPresent(ejbRefClass)) {
				// 如果有@EJB注解也处理
				}
				else if (field.isAnnotationPresent(Resource.class)) {
					// 如果加了@Resource注解的field是static的,那就直接报错
					if (Modifier.isStatic(field.getModifiers())) {
						throw new IllegalStateException("@Resource annotation is not supported on static fields");
					}
					if (!this.ignoredResourceTypes.contains(field.getType().getName())) {
					// 把field包装一下加入到currElements里面
						currElements.add(new ResourceElement(field, field, null));
					}
				}
			});

			//处理方法上的@Resource注解
			ReflectionUtils.doWithLocalMethods(targetClass, method -> {
				//查找该方法的桥接方法
				Method bridgedMethod = BridgeMethodResolver.findBridgedMethod(method);
				//判断桥接方法和method的方法签名是否相同
				if (!BridgeMethodResolver.isVisibilityBridgeMethodPair(method, bridgedMethod)) {
					return;
				}
				if (method.equals(ClassUtils.getMostSpecificMethod(method, clazz))) {
					if (webServiceRefClass != null && bridgedMethod.isAnnotationPresent(webServiceRefClass)) {
						// 处理@WebServiceRef注解
					}
					else if (ejbRefClass != null && bridgedMethod.isAnnotationPresent(ejbRefClass)) {
						// 处理@EJB注解
					}
					else if (bridgedMethod.isAnnotationPresent(Resource.class)) {
						// 静态方法如果加了@Resource注解,则报错
						if (Modifier.isStatic(method.getModifiers())) {
							throw new IllegalStateException("@Resource annotation is not supported on static methods");
						}
						Class<?>[] paramTypes = method.getParameterTypes();
						// 此处还限定了加了@Resource注解的方法必须只有一个入参
						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);
		// 把field和method区分开后,封装成InjectionMetaData返回findResourceMetadata方法
		return new InjectionMetadata(clazz, elements);
	}

在findResourceMetadata方法中,会把该InjectionMetadata放入到injectionMetadataCache中。
结构如下图:
@Resource注解的原理、源码_第1张图片

2. 注入

2.1 在Spring中bean的生态的位置

回到AbstractAutowireCapableBeanFactory的doCreateBean方法,在设置了SingletonFactory之后,会调用populateBean方法,这个方法就是我们的属性注入方法。

populate方法执行分为几种情况:

1. 调用BeanPostProcessor类的postProcessAfterInstantiation,如果有一个返回了false,那么就会停止这个bean的属性注入。目前默认全是true,这是框架留给程序员的一个扩展点
2. 判断当前的mbd(merged bean definition)设置的自动注入mode,不过一般情况下,我们都不会设置,无非是byName或者byType
3. 查看当前的mbd有么有设置propertyValues,如果设置了,就拿上进入到最重要的实际注入逻辑

遍历所有的BeanPostProcessor,判断是不是InstantiationAwareBeanPostProcessor,然后再调用其postProcessProperties方法。聪明的你一定又猜到了此处我们实际调用的CommonAnnotationBeanPostProcessor的postProcessProperties方法。看源码吧

2.2 detail, show me the code

@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
	InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
	try {
		metadata.inject(bean, beanName, pvs);
	}
	catch (Throwable ex) {
		throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);
	}
	return pvs;
}
  1. 可以看到第一步是获取InjectionMetadata,也就是咱们在上面查找注入点时生成的InjectionMetadata,此处就是直接从injectionMetadataCache中拿的
  2. 第二步就是真实的注入过程了metadata.inject(bean, beanName, pvs);
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) {
			// log
			element.inject(target, beanName, pvs);
		}
	}
}

获取该mbd的所有注入点,for循环调用InjectMetadata的inject方法:

/**
 * Either this or {@link #getResourceToInject} needs to be overridden.
 */
protected void inject(Object target, @Nullable String requestingBeanName, @Nullable PropertyValues pvs)
		throws Throwable {

	if (this.isField) {
		Field field = (Field) this.member;
		ReflectionUtils.makeAccessible(field);
		field.set(target, getResourceToInject(target, requestingBeanName));
	}
	else {
		if (checkPropertySkipping(pvs)) {
			return;
		}
		try {
			Method method = (Method) this.member;
			ReflectionUtils.makeAccessible(method);
			method.invoke(target, getResourceToInject(target, requestingBeanName));
		}
		catch (InvocationTargetException ex) {
			throw ex.getTargetException();
		}
	}
}

如果是属性(field)的话,最终会调用ResourceElement的getResourceToInject方法:

protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
			return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) :
					getResource(this, requestingBeanName));
		}

如果加了@Lazy注解,那么先给返回一个代理对象。否则调用:

/**
 * Obtain the resource object for the given name and type.
 * @param element the descriptor for the annotated field/method
 * @param requestingBeanName the name of the requesting bean
 * @return the resource object (never {@code null})
 * @throws NoSuchBeanDefinitionException if no corresponding target resource found
 */
protected Object getResource(LookupElement element, @Nullable String requestingBeanName)
		throws NoSuchBeanDefinitionException {

	if (StringUtils.hasLength(element.mappedName)) {
		return this.jndiFactory.getBean(element.mappedName, element.lookupType);
	}
	if (this.alwaysUseJndiLookup) {
		return this.jndiFactory.getBean(element.name, element.lookupType);
	}
	if (this.resourceFactory == null) {
		throw new NoSuchBeanDefinitionException(element.lookupType,
				"No resource factory configured - specify the 'resourceFactory' property");
	}
	return autowireResource(this.resourceFactory, element, requestingBeanName);
}

可以看到最终应该会进入autowireResource方法:

/**
	 * Obtain a resource object for the given name and type through autowiring
	 * based on the given factory.
	 * @param factory the factory to autowire against
	 * @param element the descriptor for the annotated field/method
	 * @param requestingBeanName the name of the requesting bean
	 * @return the resource object (never {@code null})
	 * @throws NoSuchBeanDefinitionException if no corresponding target resource found
	 */
	protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName)
			throws NoSuchBeanDefinitionException {

		Object resource;
		Set<String> autowiredBeanNames;
		String name = element.name;

		if (factory instanceof AutowireCapableBeanFactory) {
			AutowireCapableBeanFactory beanFactory = (AutowireCapableBeanFactory) factory;
			DependencyDescriptor descriptor = element.getDependencyDescriptor();
			// 如果@Resource没有配置name属性,且对应name的bean还没加入到工厂
			if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) {
				autowiredBeanNames = new LinkedHashSet<>();
				resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);
				if (resource == null) {
					throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");
				}
			}
			else {
				// 此处代表要注入的某个依赖已经被解析过了或者@Resource配置了name属性,就直接拿了用
				resource = beanFactory.resolveBeanByName(name, descriptor);
				autowiredBeanNames = Collections.singleton(name);
			}
		}
		else {
			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;
	}

上面这处的resolveDependency方法就显得很重要了,这个方法是为了根据依赖的属性名找到依赖的bean

@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);
			// 此处只有使用了@Qualifier注解的bean才有意义
			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()));
				}
			}

			// 如果需要注入的类型是List,Array,Map,在此处进行处理
			Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);
			if (multipleBeans != null) {
				return multipleBeans;
			}

			// 此处map的value既有可能是Class的对象,也有可能是bean,多数时候是Class的对象,非Class对象是因为注入的是
			//applicationContext、BeanFactory等属性
			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) {
				// 获取属性名
				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;
					}
				}
				// 根据属性名获取对应的class
				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);
			}
			// 大多数情况此处的判断都是true,那么就根据instanceCandidate去容器中查找或者创建bean
			// 不过如果注入的属性是ApplicationContext之类的,为false,此时的instanceCandidate就是一个已经直接可用的bean
			if (instanceCandidate instanceof Class) {
				// resolveCandidate方法具体内容就是BeanFactory.getBean
				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);
		}
	}

返回的Object,最终会在InjectionMetadata类的inject方法中通过反射,要么是调用field.set,要么是调用method.invoke。

总结

所以可以看到@Resource注解并不是大家传统意义说的先byName后byType。下次咱们分析@Autowired注解的源码,并进行对比。点击这里查看@Autowired注解的原理分析和两者的对比。

  1. 关于this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name) 这个判断,由于前两个大多数情况都是返回true,那么焦灼点就是第三个条件。
  2. 如果第三个条件返回false,就会执行后面的resolveBeanByName,否则执行resolveDependency;
  3. resolveBeanByName的逻辑是根据name和type调用getBean;resolveDependency执行的逻辑是先根据类型查找,再根据名字确定。如果此处***!factory.containsBean(name)***返回true了,name执行resolveDependency时,相当于只执行了byType,给用户造成了一种先byName后byType的假象。

你可能感兴趣的:(spring,源码分析)