Spring 依赖注入[4]

554 阅读3分钟

「这是我参与11月更文挑战的第10天,活动详情查看:2021最后一次更文挑战

Resource-CommonAnnotationBeanPP

resource注解的两个可选值:

  • name = "?"
  • type = ?.class

其实结构是一样的:

  • 找注入点(加了@Resource的)

    • 这里,在findResourceMetadata里调用的buildMetadata里,如果解析到static字段被@Resource注解了,就会报错,而autowired的不会,而是直接返回。
@Override
public void postProcessMergedBeanDefinition(RootBeanDefinition beanDefinition, Class<?> beanType, String beanName) {
   super.postProcessMergedBeanDefinition(beanDefinition, beanType, beanName);
   InjectionMetadata metadata = findResourceMetadata(beanName, beanType, null);
   metadata.checkConfigMembers(beanDefinition);
}

这里的metadata中的Element的实现类是ResourceElement:

public ResourceElement(Member member, AnnotatedElement ae, @Nullable PropertyDescriptor pd) {
   super(member, pd);
   Resource resource = ae.getAnnotation(Resource.class);
    //这里就是注解的值
   String resourceName = resource.name();
   Class<?> resourceType = resource.type();
    //这里指的就是:如果名字没配,那么就是走下面的
   this.isDefaultName = !StringUtils.hasLength(resourceName);
   if (this.isDefaultName) {
      resourceName = this.member.getName();
       //如果这里的resource的名字有一个set,那么名字就是从set方法里截取的
      if (this.member instanceof Method && resourceName.startsWith("set") && resourceName.length() > 3) {
         resourceName = Introspector.decapitalize(resourceName.substring(3));
      }
   }
   else if (embeddedValueResolver != null) {
      resourceName = embeddedValueResolver.resolveStringValue(resourceName);
   }
    //Object是type的默认值,这里会检查一下是否匹配
   if (Object.class != resourceType) {
      checkResourceType(resourceType);
   }
   else {
      // No resource type specified... check field/method.
      resourceType = getResourceType();
   }
   this.name = (resourceName != null ? resourceName : "");
   this.lookupType = resourceType;
   String lookupValue = resource.lookup();
   this.mappedName = (StringUtils.hasLength(lookupValue) ? lookupValue : resource.mappedName());
    //是否有lazy注解的相关配置
   Lazy lazy = ae.getAnnotation(Lazy.class);
   this.lazyLookup = (lazy != null && lazy.value());
}
  • 字段属性注入

    • 这里的inject,是通过父类来实现的
@Override
public PropertyValues postProcessProperties(PropertyValues pvs, Object bean, String beanName) {
   InjectionMetadata metadata = findResourceMetadata(beanName, bean.getClass(), pvs);
   try {
       //这里就可以发现其实两个自动注入的PP是类似的
      metadata.inject(bean, beanName, pvs);
   }
   catch (Throwable ex) {
      throw new BeanCreationException(beanName, "Injection of resource dependencies failed", ex);
   }
   return 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();
				}
			}
		}

//而这里的getResourceToInject的实现在子类里:
		protected Object getResourceToInject(Object target, @Nullable String requestingBeanName) {
            //这里就能看到lazy的作用了:构造代理对象,和autowired的类似
            //接下来就是getResource
			return (this.lazyLookup ? buildLazyResourceProxy(this, requestingBeanName) :
					getResource(this, requestingBeanName));
		}


protected Object getResource(LookupElement element, @Nullable String requestingBeanName)
			throws NoSuchBeanDefinitionException {
		//jndi,ejb相关
		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);
	}



protected Object autowireResource(BeanFactory factory, LookupElement element, @Nullable String requestingBeanName)
			throws NoSuchBeanDefinitionException {

		Object resource;
		Set<String> autowiredBeanNames;
		String name = element.name;
//default的BF就是这个类的
		if (factory instanceof AutowireCapableBeanFactory) {
			AutowireCapableBeanFactory beanFactory = (AutowireCapableBeanFactory) factory;
			DependencyDescriptor descriptor = element.getDependencyDescriptor();
            
            //@Resource查找bean的核心逻辑都在这里
            //这里isDefaultname,指的就是没有配,name就是属性名字的值
            //就会去bean工厂中,查看是否有bean,如果没有就会resolve,和上面的方法就是相同的
            //fallbackToDefaultTypeMatch,指的是之前是否通过class类型可以找到对应的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");
				}
			}
            //这里就是上面包括了这个bean,或者注解写了name,有的话就直接从BF里获取
			else {
				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;
	}

总结

依赖注入IOC选择步骤:

@Autowired
private Object resource;
  1. 先根据类型找bean

  2. 筛选:

    • candidate=true?

    • 是否是泛型?

    • Qualifier?(@Qualifier,可以修饰bean,如果有的话,那么对应的@Autowired如果指明了相同的名字,会优先选择通过该注解指定的同名bean)

    • Primary是否有?

    • Priority从小到大?

    • 名字

  • autowire和resource的差别:
    • autowired:
      • 静态变量autowired,会直接跳过
      • 先根据类型查找,再根据名字查找
      • Spring提供的
    • resource:
      • 静态变量resource,会报错
      • 先根据名字查找,再根据类型查找
      • javax提供的