@Resource是属于jdk的注解:javax.annotation.Resource。我们一般认为他是ByName的方式注入!
@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!
- 这个公众号,无广告!!!每日更新!!!