【spring】依赖注入之@Resource注入

@Resource注入

  • 本文源码基于spring-framework-5.3.10。
  • 源码位置:org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.postProcessProperties(PropertyValues, Object, String)
  • 除了指定BeanName的Bean在注入的时候会进行ByType,我们在使用中的大部分时候都是使用ByName进行注入,所以我们认为@Resource是通过ByName的方式注入
  • @Resource注解属于jdk的注解:javax.annotation.Resource
  • @Resource在寻找注入点的时候,静态的方法直接抛异常,参数不是一个直接抛异常!

@Resource源码流程

  • 寻找注入点:字段、方法
  • 进行注入:没有写BeanName,并且默认的BeanName在Bean工厂找不到,先ByType后ByName。其他的大部分情况直接根据名称去Bean工厂找!

源码分析

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;
}

@Resource的寻找注入点

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.
	// 得到要注入的BeanName,未加载直接得到BeanName,实例化了的话去加载当前Bean的clazz的名称
	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);
				}
				// 真正的通过class文件去扫描
				metadata = buildResourceMetadata(clazz);
				// 添加到缓存中
				this.injectionMetadataCache.put(cacheKey, metadata);
			}
		}
	}
	// 返回得到的字段要注入的点,或者方法参数要注入的点
	return metadata;
}

/**
 * 寻找注入点
 */
private InjectionMetadata buildResourceMetadata(final Class<?> clazz) {
	// 要注入的返回值对应的类型是java.开头的类。或者上面的注解有org.springframework.core.Ordered这个注解及其子类的时候。去创建一个空的注入点的类
	if (!AnnotationUtils.isCandidateClass(clazz, resourceAnnotationTypes)) {
		return InjectionMetadata.EMPTY;
	}

	// 定义最后找到的注入点
	List<InjectionMetadata.InjectedElement> elements = new ArrayList<>();
	// 定义扫描的类,扫描完当前类会把当前值变为他的父类
	Class<?> targetClass = clazz;

	// 这个循环主要是为了去处理他的父类,直到没有父类
	do {
		// 当前类的注入点存放
		final List<InjectionMetadata.InjectedElement> currElements = new ArrayList<>();

		// 字段注入点处理
		ReflectionUtils.doWithLocalFields(targetClass, field -> {
			......前面非@Resource相关的代码暂时忽略
			// 字段上存在@Resource注解
			else if (field.isAnnotationPresent(Resource.class)) {
				// 静态的字段,直接抛异常!
				if (Modifier.isStatic(field.getModifiers())) {
					throw new IllegalStateException("@Resource annotation is not supported on static fields");
				}
				// 跳过@PostConstruct和@PreDestroy这俩个标记的注解。
				if (!this.ignoredResourceTypes.contains(field.getType().getName())) {
					currElements.add(new ResourceElement(field, field, null));
				}
			}
		});

		// 方法注入点处理
		ReflectionUtils.doWithLocalMethods(targetClass, method -> {
				......前面非@Resource相关的代码暂时忽略
				// 方法上存在@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();
					// 方法参数不为1,抛异常
					if (paramTypes.length != 1) {
						throw new IllegalStateException("@Resource annotation requires a single-arg method: " + method);
					}
					// 唯一的一个参数不存在@PostConstruct和@PreDestroy这俩个标记的注解。
					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 InjectionMetadata.forElements(elements, clazz);
}

/**
 * 构建注入点描述信息
 */
public ResourceElement(Member member, AnnotatedElement ae, @Nullable PropertyDescriptor pd) {
	super(member, pd);
	// 得到@Resource注解的具体信息
	Resource resource = ae.getAnnotation(Resource.class);
	// 得到@Resource自己配置的名称
	String resourceName = resource.name();
	// 得到@Resource自己配置的类型
	Class<?> resourceType = resource.type();

	// 使用@Resource时没有指定具体的name,那么则用field的name,或setXxx()中的xxx
	this.isDefaultName = !StringUtils.hasLength(resourceName);
	// 没有自定义名字
	if (this.isDefaultName) {
		// 得到字段的名称
		resourceName = this.member.getName();
		// 方法的名称处理,去除第三位,首字母小写
		if (this.member instanceof Method && resourceName.startsWith("set") && resourceName.length() > 3) {
			resourceName = Introspector.decapitalize(resourceName.substring(3));
		}
	}
	// 使用@Resource时指定了具体的name,进行占位符填充
	else if (embeddedValueResolver != null) {
		resourceName = embeddedValueResolver.resolveStringValue(resourceName);
	}

	// @Resource除开可以指定bean,还可以指定type,type默认为Object
	if (Object.class != resourceType) {
		// 如果指定了type,则验证一下和field的类型或set方法的第一个参数类型,是否和所指定的resourceType匹配
		checkResourceType(resourceType);
	}
	else {
		// No resource type specified... check field/method.
		// 没有指定资源类型。。。检查字段/方法。
		resourceType = getResourceType();
	}
	// null转空串
	this.name = (resourceName != null ? resourceName : "");
	this.lookupType = resourceType;

	// resource的lookup属性处理
	String lookupValue = resource.lookup();
	this.mappedName = (StringUtils.hasLength(lookupValue) ? lookupValue : resource.mappedName());

	Lazy lazy = ae.getAnnotation(Lazy.class);
	this.lazyLookup = (lazy != null && lazy.value());
}

字段、方法的注入

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);
		}
	}
}

/**
 * 对每个注入点进行注入
 */
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();
		}
	}
}

寻找需要注入的值

protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
	// 懒加载返回一个空的代理对象,否则调用getResource
	return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) :
			getResource(this, requestingBeanName));
}

protected Object getResource(LookupElement element, @Nullable String requestingBeanName)
		throws NoSuchBeanDefinitionException {
	// 直接调用通过名称调用getBean
	if (StringUtils.hasLength(element.mappedName)) {
		return this.jndiFactory.getBean(element.mappedName, element.lookupType);
	}
	// 直接调用通过名称调用getBean
	if (this.alwaysUseJndiLookup) {
		return this.jndiFactory.getBean(element.name, element.lookupType);
	}
	// 直接调用通过名称调用getBean
	if (this.resourceFactory == null) {
		throw new NoSuchBeanDefinitionException(element.lookupType,
				"No resource factory configured - specify the 'resourceFactory' property");
	}
	// 根据LookupElement从BeanFactory找到适合的bean对象
	return autowireResource(this.resourceFactory, element, requestingBeanName);
}

/**
 * 根据类型或者名称去获取要设置的bean
 */
protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName)
		throws NoSuchBeanDefinitionException {

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

	// 他是一个自动装配的BeanFactory
	if (factory instanceof AutowireCapableBeanFactory) {
		AutowireCapableBeanFactory beanFactory = (AutowireCapableBeanFactory) factory;
		DependencyDescriptor descriptor = element.getDependencyDescriptor();

		// 假设@Resource中没有指定name,并且field的name或setXxx()的xxx不存在对应的bean,那么则根据field类型或方法参数类型从BeanFactory去找
		if (this.fallbackToDefaultTypeMatch && element.isDefaultName && !factory.containsBean(name)) {
			// 这里与@Autowired类型,先根据类型去筛选Bean、再根据name去筛选
			autowiredBeanNames = new LinkedHashSet<>();
			resource = beanFactory.resolveDependency(descriptor, requestingBeanName, autowiredBeanNames, null);
			if (resource == null) {
				throw new NoSuchBeanDefinitionException(element.getLookupType(), "No resolvable resource object");
			}
		}
		else {
			// 内部直接通过名称调用getBean
			resource = beanFactory.resolveBeanByName(name, descriptor);
			// 设置一下注入了哪个Bean
			autowiredBeanNames = Collections.singleton(name);
		}
	}
	else {
		// 大部分情况直接通过名称调用getBean
		resource = factory.getBean(name, element.lookupType);
		// 设置一下注入了哪个Bean
		autowiredBeanNames = Collections.singleton(name);
	}

	// 处理Bean的依赖关系吗,当前Bean被那些Bean依赖,依赖了那些Bean
	if (factory instanceof ConfigurableBeanFactory) {
		ConfigurableBeanFactory beanFactory = (ConfigurableBeanFactory) factory;
		for (String autowiredBeanName : autowiredBeanNames) {
			if (requestingBeanName != null && beanFactory.containsBean(autowiredBeanName)) {
				beanFactory.registerDependentBean(autowiredBeanName, requestingBeanName);
			}
		}
	}

	return resource;
}

结束语

  • 获取更多本文的前置知识文章,以及新的有价值的文章,让我们一起成为架构师!
  • 关注公众号,可以让你对MySQL、并发编程、spring源码有深入的了解!
  • 关注公众号,后续持续高效的学习JVM!
  • 这个公众号,无广告!!!每日更新!!!
    作者公众号.jpg
posted @ 2022-02-22 23:49  程序java圈  阅读(389)  评论(0编辑  收藏  举报