Spring源码之依赖注入(二)

书接上文

文章目录

    • 一. @Autowire底层注入逻辑
      • 1. 属性注入逻辑

一. @Autowire底层注入逻辑

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

前面我们分析了Spring时如何找到某个目标类的所有注入点这一个核心逻辑,但还没又对核心注入方法inject进行详细分析,下面我们就来详细分析Spring拿到所有的注入点之后,是如何实现注入的逻辑的。首先我们回到inject方法。

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

上面代码最核心的就是遍历InjectionMetadata中的this.checkedElements,也就是注入点集合。然后针对不同的element类型会调用不同的注入方法(在注册注入点时已经说到了这两种类型,分别是AutowiredFieldElement和AutowiredMethodElement)。具体实现注入的逻辑是调用的这段代码element.inject(target, beanName, pvs);。然后对于不同的注入类型(属性注入和方法注入,它们是有不同的实现类的)
在这里插入图片描述

1. 属性注入逻辑

首先我们看字段注入的inject源码:

//PropertyValues 对象,封装了要应用到目标对象的属性值
@Overrideprotected void inject(Object bean, @Nullable String beanName, @Nullable PropertyValues pvs) throws Throwable {//获得当前要处理类的字段Field field = (Field) this.member;Object value;//检查是否启用了缓存。如果启用了缓存,会尝试从缓存中获取已解析的字段值。如果缓存中不存在,会捕获 NoSuchBeanDefinitionException 异常,然后重新解析字段值。if (this.cached) {// 对于原型Bean,第一次创建的时候,也找注入点,然后进行注入,此时cached为false,注入完了之后cached为true// 第二次创建的时候,先找注入点(此时会拿到缓存好的注入点),也就是AutowiredFieldElement对象,此时cache为true,也就进到此处了// 注入点内并没有缓存被注入的具体Bean对象,而是beanName,这样就能保证注入到不同的原型Bean对象try {//尝试从缓存中获取已解析的字段值。如果缓存中存在,则使用该值。否则,会重新调用 resolveFieldValue 方法解析字段值。value = resolvedCachedArgument(beanName, this.cachedFieldValue);}catch (NoSuchBeanDefinitionException ex) {//如果没有启用缓存,或者缓存中没有有效的值,将调用 resolveFieldValue 方法从 BeanFactory 中解析字段值。value = resolveFieldValue(field, bean, beanName);}}else {// 根据filed从BeanFactory中查到的匹配的Bean对象value = resolveFieldValue(field, bean, beanName);}// 反射给filed赋值if (value != null) {ReflectionUtils.makeAccessible(field);field.set(bean, value);}}

这段代码主要处理了字段注入的逻辑,包括缓存的使用、字段值的解析和反射赋值。这是 Spring 框架实现自动装配的一部分,确保依赖关系得到正确地建立。当我们缓存中没有我们要注入的有效值,或者是压根就没有缓存时,会调用 resolveFieldValue(field, bean, beanName);从BeanFactory中解析字段。

@Nullableprivate Object resolveFieldValue(Field field, Object bean, @Nullable String beanName) {//创建一个 DependencyDescriptor 对象,该对象包含有关要注入的字段的信息。设置字段所属的类以及是否为必需的标志DependencyDescriptor desc = new DependencyDescriptor(field, this.required);desc.setContainingClass(bean.getClass());//初始化用于存储自动装配的Bean名称的集合 autowiredBeanNames,检查 beanFactory 是否为null,并获取 TypeConverter 对象。Set<String> autowiredBeanNames = new LinkedHashSet<>(1);Assert.state(beanFactory != null, "No BeanFactory available");TypeConverter typeConverter = beanFactory.getTypeConverter();Object value;try {//使用 beanFactory 的 resolveDependency 方法解析字段值。如果解析失败,抛出 UnsatisfiedDependencyException 异常,其中包含相关的信息,如字段信息、目标Bean名称和异常原因。value = beanFactory.resolveDependency(desc, beanName, autowiredBeanNames, typeConverter);}catch (BeansException ex) {throw new UnsatisfiedDependencyException(null, beanName, new InjectionPoint(field), ex);}synchronized (this) {if (!this.cached) {Object cachedFieldValue = null;if (value != null || this.required) {cachedFieldValue = desc;// 注册一下beanName依赖了autowiredBeanNames,registerDependentBeans(beanName, autowiredBeanNames);if (autowiredBeanNames.size() == 1) {String autowiredBeanName = autowiredBeanNames.iterator().next();if (beanFactory.containsBean(autowiredBeanName) &&beanFactory.isTypeMatch(autowiredBeanName, field.getType())) {// 构造一个ShortcutDependencyDescriptor作为缓存,保存了当前filed所匹配的autowiredBeanName,而不是对应的bean对象(考虑原型bean)cachedFieldValue = new ShortcutDependencyDescriptor(desc, autowiredBeanName, field.getType());}}}this.cachedFieldValue = cachedFieldValue;this.cached = true;}}return value;}}

上面代码主要是解析字段的值和处理自动装配的逻辑。首先它调用了beanFactory.resolveDependency来解析字段值。

@Override@Nullable/**descriptor:要解析的依赖项的描述符requestingBeanName: 请求解析依赖项的 Bean 的名称(可能为null)autowiredBeanNames: 自动装配的Bean名称的集合(可能为null)。typeConverter: 用于类型转换的 TypeConverter 对象(可能为null)**/public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName,@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {// 用来获取方法入参名字的descriptor.initParameterNameDiscovery(getParameterNameDiscoverer());// 所需要的类型是Optionalif (Optional.class == descriptor.getDependencyType()) {return createOptionalDependency(descriptor, requestingBeanName);}// 所需要的的类型是ObjectFactory,或ObjectProviderelse if (ObjectFactory.class == descriptor.getDependencyType() ||ObjectProvider.class == descriptor.getDependencyType()) {return new DependencyObjectProvider(descriptor, requestingBeanName);}else if (javaxInjectProviderClass == descriptor.getDependencyType()) {return new Jsr330Factory().createDependencyProvider(descriptor, requestingBeanName);}else {// 在属性或set方法上使用了@Lazy注解,那么则构造一个代理对象并返回,真正使用该代理对象时才进行类型筛选BeanObject result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(descriptor, requestingBeanName);if (result == null) {// descriptor表示某个属性或某个set方法// requestingBeanName表示正在进行依赖注入的Beanresult = doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);}return result;}}

bject result = getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(descriptor, requestingBeanName); 代码段的目的是在实际进行依赖项解析之前,检查是否需要懒加载。如果需要懒加载,会通过调用 getLazyResolutionProxyIfNecessary 方法获取懒加载的代理对象,然后将该代理对象用作最终的依赖项。
在 Spring 中,懒加载是一种策略,用于延迟对象的实际创建和初始化,直到第一次被真正需要时。这有助于提高性能,特别是在应用程序启动时,不会立即创建所有可能不会被使用的 Bean。懒加载通常通过代理对象来实现,代理对象会在真正需要时触发目标 Bean 的创建和初始化。这在大型应用程序中可以显著减少启动时间。

上面代码的逻辑主要是首先处理了两种特殊类型的依赖,然后判断所依赖的对象是否是懒加载,如果是懒加载我们就会返回所依赖对象的代理对象返回(只有当我们真正使用到这个对象时,才会加载该对象),如果不是懒加载对象,就调用doResolveDependency开始真正解析要注入的bean。

@Nullable
/*
descriptor: 要解析的依赖项的描述符。
beanName: 当前正在进行依赖注入的 Bean 的名称(可能为null)。
autowiredBeanNames: 自动装配的Bean名称的集合(可能为null)。
typeConverter: 用于类型转换的 TypeConverter 对象(可能为null)。
*/public Object doResolveDependency(DependencyDescriptor descriptor, @Nullable String beanName,@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {//在进行依赖项解析之前,通过调用setCurrentInjectionPoint方法设置当前的注入点,以便后续的解析可以获取到相关的上下文信息InjectionPoint previousInjectionPoint = ConstructorResolver.setCurrentInjectionPoint(descriptor);try {// 如果当前descriptor之前做过依赖注入了,则可以直接取shortcut了,相当于缓存,尝试获取缓存的快捷方式,如果存在的话直接返回。这是为了提高性能,避免重复解析相同的依赖项。Object shortcut = descriptor.resolveShortcut(this);if (shortcut != null) {return shortcut;}Class<?> type = descriptor.getDependencyType();// 获取@Value所指定的值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);// 解析Spring表达式(#{})value = evaluateBeanDefinitionString(strVal, bd);}// 将value转化为descriptor所对应的类型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()));}}// 如果descriptor所对应的类型是数组、Map这些,就将descriptor对应的类型所匹配的所有bean方法,不用进一步做筛选了Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);if (multipleBeans != null) {return multipleBeans;}// 找到所有Bean,key是beanName, value有可能是bean对象,有可能是beanClassMap<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);if (matchingBeans.isEmpty()) {// required为true,抛异常if (isRequired(descriptor)) {raiseNoMatchingBeanFound(type, descriptor.getResolvableType(), descriptor);}return null;}String autowiredBeanName;Object instanceCandidate;if (matchingBeans.size() > 1) {// 根据类型找到了多个Bean,进一步筛选出某一个, @Primary-->优先级最高--->nameautowiredBeanName = determineAutowireCandidate(matchingBeans, descriptor);if (autowiredBeanName == null) {if (isRequired(descriptor) || !indicatesMultipleBeans(type)) {return descriptor.resolveNotUnique(descriptor.getResolvableType(), matchingBeans);}else {return null;}}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();}// 记录匹配过的beanNameif (autowiredBeanNames != null) {autowiredBeanNames.add(autowiredBeanName);}// 有可能筛选出来的是某个bean的类型,此处就进行实例化,调用getBeanif (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);}}

上面代码涵盖了对不同情况的处理,包括通过注解、类型、名称等方式进行 Bean 的筛选和解析。同时,支持了类型转换、@Value 注解、Spring 表达式的解析等功能,以满足复杂的依赖项解析需求。

首先处理了@Value注解,调用的是Object value = getAutowireCandidateResolver().getSuggestedValue(descriptor);

@Override@Nullablepublic Object getSuggestedValue(DependencyDescriptor descriptor) {//从 DependencyDescriptor 中获取注解(annotations),并通过 findValue 方法寻找与注解相关联的值。Object value = findValue(descriptor.getAnnotations());if (value == null) {MethodParameter methodParam = descriptor.getMethodParameter();if (methodParam != null) {value = findValue(methodParam.getMethodAnnotations());}}return value;}

整个方法的目的是根据依赖项的描述符,尝试从相关的注解中获取建议值。这可以用于处理一些特殊情况,例如通过注解指定的默认值等。在 Spring 中,@Value 注解就是一个常见的用例,通过它可以在字段或方法参数上指定默认值。(其实这里就会获得我们在@Value中写入的值)。

然后继续回到doResolveDependency方法。

//判断@Value注解是否指定了默认值
if (value != null) {//如果值的类型是String(一般情况下都是String)if (value instanceof String) {// 占位符填充(${})//resolveEmbeddedValue 方法会尝试解析这个占位符,替换成实际的属性值。String strVal = resolveEmbeddedValue((String) value);BeanDefinition bd = (beanName != null && containsBean(beanName) ?getMergedBeanDefinition(beanName) : null);// 解析Spring表达式(#{})value = evaluateBeanDefinitionString(strVal, bd);}// 将value转化为descriptor所对应的类型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()));}}

上面代码首先如果value是String类型它会首先处理占位符#{}${}填充,假设有一个Spring Bean的配置如下:

@Configuration
public class AppConfig {@Value("${app.message}")private String appMessage;@Beanpublic MyBean myBean() {return new MyBean(appMessage);}
}

在这个配置中,MyBean 类的实例化依赖于一个名为 app.message 的属性值。这个属性值从Spring的Enviroment中获取(Environment 接口是一个核心接口,用于表示应用程序运行环境的抽象。它提供了一种方式来访问应用程序配置的属性以及配置文件中的属性值,或者是操作系统相关配置。Environment 接口的实现类通常是 StandardEnvironment 或其子类。)

在 Spring 中,#{} 是用于表示 SpEL(Spring Expression Language)表达式的语法。SpEL 是一种强大的表达式语言,支持在运行时对属性进行计算和操作。让我们通过一个简单的案例来演示如何使用 #{} 进行 SpEL 表达式的解析

@Configuration
public class DataSourceConfig {@Value("#{environment['max.connections'] ?: 10}")private int maxConnections;@Beanpublic DataSource dataSource() {// 创建并配置数据源HikariConfig config = new HikariConfig();config.setMaximumPoolSize(maxConnections);// 其他配置...return new HikariDataSource(config);}
}

这个配置类中,我们使用了 @Value 注解和 #{} 表达式,从环境属性中获取 max.connections 的值,如果该属性不存在则使用默认值 10。这个值将用于设置 HikariCP 连接池的最大活动连接数。

继续回到doResolveDependency,处理完@Value的占位符的信息,下面就可以开始类型转换了,主要目的是将Value值转换为我们所需要注入的属性类型值。处理完Value注解后,就开始处理数组和Map类型了。

// 如果descriptor所对应的类型是数组、Map这些,就将descriptor对应的类型所匹配的所有bean方法,不用进一步做筛选了Object multipleBeans = resolveMultipleBeans(descriptor, beanName, autowiredBeanNames, typeConverter);if (multipleBeans != null) {return multipleBeans;}

如果依赖项的类型是数组、Map等,直接解析所有匹配的 Bean 方法,不进行进一步筛选。调用resolveMultipleBeans函数。

@Nullableprivate Object resolveMultipleBeans(DependencyDescriptor descriptor, @Nullable String beanName,@Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) {Class<?> type = descriptor.getDependencyType();//如果依赖项的类型是 Stream,则会找到所有与类型匹配的 Bean,构造成一个 Stream,并进行排序(如果有序)。这种情况下,返回的是 Stream 对象。if (descriptor instanceof StreamDependencyDescriptor) {// 找到type所匹配的所有beanMap<String, Object> matchingBeans = findAutowireCandidates(beanName, type, descriptor);if (autowiredBeanNames != null) {autowiredBeanNames.addAll(matchingBeans.keySet());}// 构造成一个streamStream<Object> stream = matchingBeans.keySet().stream().map(name -> descriptor.resolveCandidate(name, type, this)).filter(bean -> !(bean instanceof NullBean));// 排序if (((StreamDependencyDescriptor) descriptor).isOrdered()) {stream = stream.sorted(adaptOrderComparator(matchingBeans));}return stream;}//如果依赖项的类型是数组,首先获取数组元素的类型,然后找到所有与数组元素类型匹配的 Bean。最后,将匹配到的 Bean 转化为数组,并进行排序(如果有序)。else if (type.isArray()) {// 得到数组元素的类型Class<?> componentType = type.getComponentType();ResolvableType resolvableType = descriptor.getResolvableType();Class<?> resolvedArrayType = resolvableType.resolve(type);if (resolvedArrayType != type) {componentType = resolvableType.getComponentType().resolve();}if (componentType == null) {return null;}// 根据数组元素类型找到所匹配的所有BeanMap<String, Object> matchingBeans = findAutowireCandidates(beanName, componentType,new MultiElementDescriptor(descriptor));if (matchingBeans.isEmpty()) {return null;}if (autowiredBeanNames != null) {autowiredBeanNames.addAll(matchingBeans.keySet());}// 进行类型转化TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());Object result = converter.convertIfNecessary(matchingBeans.values(), resolvedArrayType);if (result instanceof Object[]) {Comparator<Object> comparator = adaptDependencyComparator(matchingBeans);if (comparator != null) {Arrays.sort((Object[]) result, comparator);}}return result;}//如果依赖项的类型是集合(实现了 Collection 接口),则获取集合元素的类型,找到所有与集合元素类型匹配的 Bean。最后,将匹配到的 Bean 转化为集合,并进行排序(如果有序)。else if (Collection.class.isAssignableFrom(type) && type.isInterface()) {Class<?> elementType = descriptor.getResolvableType().asCollection().resolveGeneric();if (elementType == null) {return null;}Map<String, Object> matchingBeans = findAutowireCandidates(beanName, elementType,new MultiElementDescriptor(descriptor));if (matchingBeans.isEmpty()) {return null;}if (autowiredBeanNames != null) {autowiredBeanNames.addAll(matchingBeans.keySet());}TypeConverter converter = (typeConverter != null ? typeConverter : getTypeConverter());Object result = converter.convertIfNecessary(matchingBeans.values(), type);if (result instanceof List) {if (((List<?>) result).size() > 1) {Comparator<Object> comparator = adaptDependencyComparator(matchingBeans);if (comparator != null) {((List<?>) result).sort(comparator);}}}return result;}//如果依赖项的类型是 Map,则获取 Map 的键和值的类型,如果键的类型不是 String,返回 null。然后,找到所有与值的类型匹配的 Bean,最后返回匹配到的 Bean 的 Map。else if (Map.class == type) {ResolvableType mapType = descriptor.getResolvableType().asMap();Class<?> keyType = mapType.resolveGeneric(0);// 如果Map的key不是Stringif (String.class != keyType) {return null;}Class<?> valueType = mapType.resolveGeneric(1);if (valueType == null) {return null;}Map<String, Object> matchingBeans = findAutowireCandidates(beanName, valueType,new MultiElementDescriptor(descriptor));if (matchingBeans.isEmpty()) {return null;}if (autowiredBeanNames != null) {autowiredBeanNames.addAll(matchingBeans.keySet());}return matchingBeans;}//对于其他类型的依赖项,返回 null,表示当前方法无法处理。else {return null;}}

这段代码是 Spring 框架中用于解析多个匹配的 Bean 的方法,具体来说是对数组、集合(Collection)、Map、以及 Stream 类型的依赖项进行解析。我们可以发现 每一种类型都调用findAutowireCandidates函数来获得一个匹配bean的map集合

protected Map<String, Object> findAutowireCandidates(@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {// 从BeanFactory中找出和requiredType所匹配的beanName,仅仅是beanName,这些bean不一定经过了实例化,只有到最终确定某个Bean了,如果这个Bean还没有实例化才会真正进行实例化String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this, requiredType, true, descriptor.isEager());Map<String, Object> result = CollectionUtils.newLinkedHashMap(candidateNames.length);// 根据类型从resolvableDependencies中匹配Bean,resolvableDependencies中存放的是类型:Bean对象,比如BeanFactory.class:BeanFactory对象,在Spring启动时设置for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {//获取class对象Class<?> autowiringType = classObjectEntry.getKey();//判断是否与当前所需要的class类型匹配if (autowiringType.isAssignableFrom(requiredType)) {Object autowiringValue = classObjectEntry.getValue();autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);if (requiredType.isInstance(autowiringValue)) {result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);break;}}}for (String candidate : candidateNames) {// 如果不是自己,则判断该candidate到底能不能用来进行自动注入if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {addCandidateEntry(result, candidate, descriptor, requiredType);}}// 为空要么是真的没有匹配的,要么是匹配的自己if (result.isEmpty()) {// 需要匹配的类型是不是Map、数组之类的boolean multiple = indicatesMultipleBeans(requiredType);// Consider fallback matches if the first pass failed to find anything...DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();for (String candidate : candidateNames) {if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor) &&(!multiple || getAutowireCandidateResolver().hasQualifier(descriptor))) {addCandidateEntry(result, candidate, descriptor, requiredType);}}// 匹配的是自己,被自己添加到result中if (result.isEmpty() && !multiple) {// Consider self references as a final pass...// but in the case of a dependency collection, not the very same bean itself.for (String candidate : candidateNames) {if (isSelfReference(beanName, candidate) &&(!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) &&isAutowireCandidate(candidate, fallbackDescriptor)) {addCandidateEntry(result, candidate, descriptor, requiredType);}}}}return result;}

上面代码首先是获取指定类型所有bean的名字String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this, requiredType, true, descriptor.isEager());

public static String[] beanNamesForTypeIncludingAncestors(ListableBeanFactory lbf, Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {Assert.notNull(lbf, "ListableBeanFactory must not be null");// 从本容器中找String[] result = lbf.getBeanNamesForType(type, includeNonSingletons, allowEagerInit);// 从父容器找并放入resultif (lbf instanceof HierarchicalBeanFactory) {HierarchicalBeanFactory hbf = (HierarchicalBeanFactory) lbf;if (hbf.getParentBeanFactory() instanceof ListableBeanFactory) {String[] parentResult = beanNamesForTypeIncludingAncestors((ListableBeanFactory) hbf.getParentBeanFactory(), type, includeNonSingletons, allowEagerInit);result = mergeNamesWithParent(result, parentResult, hbf);}}return result;}

上面代码的逻辑就是根据类型在本工厂和父工厂中找,如果都找到了然后合并两个结果并返回,那么bean 工厂是怎么根据类型来找名字的lbf.getBeanNamesForType

//includeNonSingletons表示是否包括原型bean
@Overridepublic String[] getBeanNamesForType(@Nullable Class<?> type, boolean includeNonSingletons, boolean allowEagerInit) {// 如果没有冻结,就根据类型去BeanFactory找,如果冻结了,可能就跳过这个if然后去缓存中去拿了if (!isConfigurationFrozen() || type == null || !allowEagerInit) {return doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, allowEagerInit);}// 把当前类型所匹配的beanName缓存起来(includeNonSingletons来判断具体保持到哪个集合)Map<Class<?>, String[]> cache =(includeNonSingletons ? this.allBeanNamesByType : this.singletonBeanNamesByType);String[] resolvedBeanNames = cache.get(type);if (resolvedBeanNames != null) {return resolvedBeanNames;}resolvedBeanNames = doGetBeanNamesForType(ResolvableType.forRawClass(type), includeNonSingletons, true);if (ClassUtils.isCacheSafe(type, getBeanClassLoader())) {cache.put(type, resolvedBeanNames);}return resolvedBeanNames;}

然后上面代码最终调用的是doGetBeanNamesForType来找的

private String[] doGetBeanNamesForType(ResolvableType type, boolean includeNonSingletons, boolean allowEagerInit) {List<String> result = new ArrayList<>();// Check all bean definitions.// 遍历所有的BeanDefinitions的namefor (String beanName : this.beanDefinitionNames) {// Only consider bean as eligible if the bean name is not defined as alias for some other bean.if (!isAlias(beanName)) {try {//根据名字拿到对应的BeanDefinition RootBeanDefinition mbd = getMergedLocalBeanDefinition(beanName);// Only check bean definition if it is complete.// 判断mbd允不允许获取对应类型// 首先mdb不能是抽象的,然后allowEagerInit为true,则直接去推测mdb的类型,并进行匹配// 如果allowEagerInit为false,那就继续判断,如果mdb还没有加载类并且是懒加载的并且不允许提前加载类,那mbd不能用来进行匹配(因为不允许提前加载类,只能在此mdb自己去创建bean对象时才能去创建类)// 如果allowEagerInit为false,并且mbd已经加载类了,或者是非懒加载的,或者允许提前加载类,并且不用必须提前初始化才能获取类型,那么就可以去进行匹配了// 这个条件有点复杂,但是如果只考虑大部分流程,则可以忽略这个判断,因为allowEagerInit传进来的基本上都是trueif (!mbd.isAbstract() && (allowEagerInit ||(mbd.hasBeanClass() || !mbd.isLazyInit() || isAllowEagerClassLoading()) &&!requiresEagerInitForType(mbd.getFactoryBeanName()))) {boolean isFactoryBean = isFactoryBean(beanName, mbd);BeanDefinitionHolder dbd = mbd.getDecoratedDefinition();boolean matchFound = false;boolean allowFactoryBeanInit = (allowEagerInit || containsSingleton(beanName));boolean isNonLazyDecorated = (dbd != null && !mbd.isLazyInit());// 当前BeanDefinition不是FactoryBean,就是普通Beanif (!isFactoryBean) {// 在筛选Bean时,如果仅仅只包括单例,但是beanName对应的又不是单例,则忽略if (includeNonSingletons || isSingleton(beanName, mbd, dbd)) {matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);}}else {if (includeNonSingletons || isNonLazyDecorated ||(allowFactoryBeanInit && isSingleton(beanName, mbd, dbd))) {matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);}if (!matchFound) {// In case of FactoryBean, try to match FactoryBean instance itself next.beanName = FACTORY_BEAN_PREFIX + beanName;matchFound = isTypeMatch(beanName, type, allowFactoryBeanInit);}}if (matchFound) {result.add(beanName);}}}catch (CannotLoadBeanClassException | BeanDefinitionStoreException ex) {if (allowEagerInit) {throw ex;}// Probably a placeholder: let's ignore it for type matching purposes.LogMessage message = (ex instanceof CannotLoadBeanClassException ?LogMessage.format("Ignoring bean class loading failure for bean '%s'", beanName) :LogMessage.format("Ignoring unresolvable metadata in bean definition '%s'", beanName));logger.trace(message, ex);// Register exception, in case the bean was accidentally unresolvable.onSuppressedException(ex);}catch (NoSuchBeanDefinitionException ex) {// Bean definition got removed while we were iterating -> ignore.}}}// Check manually registered singletons too.for (String beanName : this.manualSingletonNames) {try {// In case of FactoryBean, match object created by FactoryBean.if (isFactoryBean(beanName)) {if ((includeNonSingletons || isSingleton(beanName)) && isTypeMatch(beanName, type)) {result.add(beanName);// Match found for this bean: do not match FactoryBean itself anymore.continue;}// In case of FactoryBean, try to match FactoryBean itself next.beanName = FACTORY_BEAN_PREFIX + beanName;}// Match raw bean instance (might be raw FactoryBean).if (isTypeMatch(beanName, type)) {result.add(beanName);}}catch (NoSuchBeanDefinitionException ex) {// Shouldn't happen - probably a result of circular reference resolution...logger.trace(LogMessage.format("Failed to check manually registered singleton with name '%s'", beanName), ex);}}return StringUtils.toStringArray(result);}

上面代码核心逻辑就是遍历所有的beanname,然后调用isTypeMatch方法判断指定的bean是否和你需要的类型匹配。 isTypeMatch的主要思路是它首先从单例池中获取指定名称的bean比较类型,如果单例池中没有获取到,那就获取beanDefitniiton加载然后对比。然后返回匹配到的bean的beanname。回到findAutowireCandidates方法

protected Map<String, Object> findAutowireCandidates(@Nullable String beanName, Class<?> requiredType, DependencyDescriptor descriptor) {// 从BeanFactory中找出和requiredType所匹配的beanName,仅仅是beanName,这些bean不一定经过了实例化,只有到最终确定某个Bean了,如果这个Bean还没有实例化才会真正进行实例化String[] candidateNames = BeanFactoryUtils.beanNamesForTypeIncludingAncestors(this, requiredType, true, descriptor.isEager());Map<String, Object> result = CollectionUtils.newLinkedHashMap(candidateNames.length);// 根据类型从resolvableDependencies中匹配Bean,resolvableDependencies中存放的是类型:Bean对象,比如BeanFactory.class:BeanFactory对象,在Spring启动时设置 for (Map.Entry<Class<?>, Object> classObjectEntry : this.resolvableDependencies.entrySet()) {//获取class对象Class<?> autowiringType = classObjectEntry.getKey();//判断是否与当前所需要的class类型匹配if (autowiringType.isAssignableFrom(requiredType)) {Object autowiringValue = classObjectEntry.getValue();autowiringValue = AutowireUtils.resolveAutowiringValue(autowiringValue, requiredType);if (requiredType.isInstance(autowiringValue)) {result.put(ObjectUtils.identityToString(autowiringValue), autowiringValue);break;}}}//遍历bean的名称for (String candidate : candidateNames) {// 如果不是自己,则判断该candidate到底能不能用来进行自动注入if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, descriptor)) {addCandidateEntry(result, candidate, descriptor, requiredType);}}// 为空要么是真的没有匹配的,要么是匹配的自己vif (result.isEmpty()) {// 需要匹配的类型是不是Map、数组之类的boolean multiple = indicatesMultipleBeans(requiredType);// Consider fallback matches if the first pass failed to find anything...DependencyDescriptor fallbackDescriptor = descriptor.forFallbackMatch();for (String candidate : candidateNames) {if (!isSelfReference(beanName, candidate) && isAutowireCandidate(candidate, fallbackDescriptor) &&(!multiple || getAutowireCandidateResolver().hasQualifier(descriptor))) {addCandidateEntry(result, candidate, descriptor, requiredType);}}// 匹配的是自己,被自己添加到result中if (result.isEmpty() && !multiple) {// Consider self references as a final pass...// but in the case of a dependency collection, not the very same bean itself.for (String candidate : candidateNames) {if (isSelfReference(beanName, candidate) &&(!(descriptor instanceof MultiElementDescriptor) || !beanName.equals(candidate)) &&isAutowireCandidate(candidate, fallbackDescriptor)) {addCandidateEntry(result, candidate, descriptor, requiredType);}}}}return result;}

找到所有匹配的Beanname后,后面代码就会对beanname进行遍历,首先过滤掉自己作为匹配对象的bean,然后调用isAutowireCandidate判断其它匹配的bean是否可以进行依赖注入。例如下面bean利用autowireCandidate = false表示不能进行依赖注入的。

public class AppConfig {@Bean(autowireCandidate = false)public OrderService orderService(){return  new OrderService();}
}

到这里数组、Map就解析完了,回到doResolveDependency方法继续执行后面逻辑。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/592540.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【CASS精品教程】CASS11计算城镇建筑密度

CASS中可以很方便计算建筑密度。 文章目录 一、建筑密度介绍二、CASS计算建筑密度1. 绘制宗地范围2. 绘制建筑物3. 计算建筑密度三、注意事项一、建筑密度介绍 建筑密度(building density;building coverage ratio),指在一定范围内,建筑物的基底面积总和与占用地面积的比…

纠删码ReedSolomon

随着大数据技术的发展&#xff0c;HDFS作为Hadoop的核心模块之一得到了广泛的应用。为了数据的可靠性&#xff0c;HDFS通过多副本机制来保证。在HDFS中的每一份数据都有两个副本&#xff0c;1TB的原始数据需要占用3TB的磁盘空间&#xff0c;存储利用率只有1/3。而且系统中大部分…

Spring Boot 2.7.11 集成 GraphQL

GraphQL介绍 GraphQL&#xff08;Graph Query Language&#xff09;是一种用于API的查询语言和运行时环境&#xff0c;由Facebook于2012年创建并在2015年公开发布。与传统的RESTful API相比&#xff0c;GraphQL提供了更灵活、高效和强大的数据查询和操作方式。 以下是GraphQL…

Spring技术内幕笔记之SpringMvc

WebApplicationContext接口的类继承关系 org.springframework.web.context.ContextLoader#initWebApplicationContext 对IOC容器的初始化 SpringMvc如何设计 DispatcherServlet类继承关系 MVC处理流程图如下&#xff1a; DispatcherServlet的工作大致可以分为两个部分&#xf…

NFC物联网开发智能衣橱解决方案

智能衣橱是智能家居的重要内容&#xff0c;现代家居市场对家居智能化控制尤为重视。但是&#xff0c;传统家居生产功能和模式已经无法满足智能化时代的需求&#xff0c;所以家居智能化成为家居行业发展的主要需求。与传统衣橱对比&#xff0c;智能衣橱的功能强大方便人们的生活…

Android--Jetpack--WorkManager详解

2024已经到来&#xff0c;愿你安睡时&#xff0c;山河入梦。愿你醒来时&#xff0c;满目春风。愿你欢笑时&#xff0c;始终如一。愿你行进时&#xff0c;前程似锦&#xff0c;坦荡从容。 编程语言的未来&#xff1f; 目录 一&#xff0c;定义 二&#xff0c;特点 三&#xff0c…

‘vue-cli-service‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件。这个问题如何解决?

这个错误信息 vue-cli-service 不是内部或外部命令&#xff0c;也不是可运行的程序或批处理文件 表示 vue-cli-service 命令在你的系统上未被识别。这通常是因为 Vue CLI 没有被正确安装或其路径没有被加入到系统的环境变量中。以下是几个解决这个问题的步骤&#xff1a; 确认 …

LinkedList与ArrayList的比较

1.LinkedList 基于双向链表&#xff0c;无需连续内存 随机访问慢&#xff08;要沿着链表遍历&#xff09; 头尾插入删除性能高 占用内存多 2.ArrayList 基于数组&#xff0c;需要连续内存 随机访问快&#xff08;指根据下标访问&#xff09; 尾部插入、删除性能可以&…

面试算法84:包含重复元素集合的全排列

题目 给定一个包含重复数字的集合&#xff0c;请找出它的所有全排列。例如&#xff0c;集合[1&#xff0c;1&#xff0c;2]有3个全排列&#xff0c;分别是[1&#xff0c;1&#xff0c;2]、[1&#xff0c;2&#xff0c;1]和[2&#xff0c;1&#xff0c;1]。 分析 下面采用回溯…

从0搭建github.io网页

点击跳转到&#x1f517;我的博客文章目录 从0搭建github.io网页 文章目录 从0搭建github.io网页1.成果展示1.1 网址和源码1.2 页面展示 2.new对象2.1 创建仓库 3.github.io仓库的初始化3.1 千里之行&#xff0c;始于足下3.2 _config.yml3.3 一点杂活 4.PerCheung.github.io.p…

如何为开源项目和社区做贡献 -- 你应该知道的十件事(四)——如何创建自己的开源项目?

问题四&#xff1a;如何创建自己的开源项目&#xff1f; 在前文中&#xff0c;我们已经介绍了最常见的给开源项目做贡献的两种途径&#xff0c;但随着你做开源项目的深入&#xff0c;你可能就会发现一个新的方向并推出自己的开源项目&#xff0c;下面&#xff0c;我将结合我的讲…

Linux 命令echo

命令作用 输出一行字符串在shell中&#xff0c;可以打印变量的值输出结果写入到文件在显示器上显示一段文字&#xff0c;起到提示的作用 语法 echo [选项] [字符串] 参数 字符含义-n不自动换行-e解释转义字符-E不解释转义字符 如果-e有效&#xff0c;则识别以下序列&…

SpringBoot 项目如何生成 swagger 文档

推荐使用 springdoc-openapi 的理由 1、springdoc-openapi 是 spring 官方出品&#xff0c;与 springboot 兼容更好&#xff08;springfox 兼容有坑&#xff09; 2、springdoc-openapi 社区更活跃&#xff0c;springfox 已经 2 年没更新了 3、springdoc-openapi 的注解更接近 …

力导向图与矩阵排序

Graph-layout force directed&#xff08;力导向图布局&#xff09;是一种用于可视化网络图的布局算法。它基于物理模型&#xff0c;模拟了图中节点之间的相互排斥和连接弹性&#xff0c;以生成具有良好可读性和美观性的图形布局。 在力导向图布局中&#xff0c;每个节点被视为…

(一)Matlab数值计算基础

目录 1.1Matlab命令组成 1.1.1基本符号 1.1.2功能符号 1.1.3常用命令 1.1Matlab命令组成 1.1.1基本符号 #提示运算符&#xff0c;表示软件处于准备就绪状态。在提示符号后输入一条命令或者一段程序后按Enter键&#xff0c;软件将给出相应的结果 >> *…

基于SpringBoot的影院订票系统

文章目录 项目介绍主要功能截图:部分代码展示设计总结项目获取方式🍅 作者主页:超级无敌暴龙战士塔塔开 🍅 简介:Java领域优质创作者🏆、 简历模板、学习资料、面试题库【关注我,都给你】 🍅文末获取源码联系🍅 项目介绍 基于SpringBoot的影院订票系统,java项目…

牛客练习赛50-C

题目描述 在一个游戏中&#xff0c;tokitsukaze需要在n个士兵中选出一些士兵组成一个团去打副本。 第i个士兵的战力为v[i]&#xff0c;团的战力是团内所有士兵的战力之和。 但是这些士兵有特殊的要求&#xff1a;如果选了第i个士兵&#xff0c;这个士兵希望团的人数不超过s[i]…

【Proteus仿真】【Arduino单片机】汽车尾气检测报警系统

文章目录 一、功能简介二、软件设计三、实验现象联系作者 一、功能简介 本项目使用Proteus8仿真Arduino单片机控制器&#xff0c;使用按键、LCD1602液晶、蜂鸣器模块、CO、NOx、HC和PM2.5气体传感器等。 主要功能&#xff1a; 系统运行后&#xff0c;LCD1602显示CO、NOx、HC和…

手机录屏没有声音?让你的录屏有声有色

“有人知道手机录屏怎么录声音吗&#xff1f;今天录制了一个小时的直播视频&#xff0c;后面查看的时候发现没有声音&#xff0c;真的非常崩溃&#xff0c;想问问大家有没有办法&#xff0c;解决这个问题。” 在手机录屏的过程中&#xff0c;有时候我们可能会面临录制视频没有…

使用 Spectrum LSF 设置多集群和作业转发

使用 Spectrum LSF 设置多集群和作业转发 以下示例是有关如何使用 Spectrum LSF设置多集群和作业转发的指南。 此示例说明了集群是本地集群&#xff0c;另一个在云中的常见情况。 此示例假定标注为 “OnPremiseCluster” 的内部部署集群使用子网 192.168.0.0/24 &#xff0c;…