【记录版】SpringBoot下ObjectProvider类源码及其执行机制解读

2023-12-22 16:48:06

主题: SpringBoot + ObjectProvider + DependencyDescriptor + ConstructorResolver

背景: SpringBoot是目前最受欢迎的开源框架之一,其自动配置特性让我们从以前繁复的配置中解放出来,如果愿意了解某组件封装的机制,其自动配置类源码阅读必不可少。在自动配置类源码中,@Bean注解的方法中,ObjectProvider类参数出现的频率极高,为什么框架层喜欢使用这个类呢?基于好奇,个人对此相关源码进行阅读,并在此记录和个人解读。

内置容器自动配置框架代码示例:

package org.springframework.boot.autoconfigure.web.servlet;

@Configuration(proxyBeanMethods = false)
class ServletWebServerFactoryConfiguration {
    @Configuration(proxyBeanMethods = false)
    @ConditionalOnClass({Servlet.class, Tomcat.class, UpgradeProtocol.class})
    @ConditionalOnMissingBean(value = {ServletWebServerFactory.class},search = SearchStrategy.CURRENT)
    static class EmbeddedTomcat {
        @Bean
        TomcatServletWebServerFactory tomcatServletWebServerFactory(ObjectProvider<TomcatConnectorCustomizer> connectorCustomizers, ObjectProvider<TomcatContextCustomizer> contextCustomizers, ObjectProvider<TomcatProtocolHandlerCustomizer<?>> protocolHandlerCustomizers) {
            TomcatServletWebServerFactory factory = new TomcatServletWebServerFactory();
            factory.getTomcatConnectorCustomizers().addAll((Collection)connectorCustomizers.orderedStream().collect(Collectors.toList()));
            factory.getTomcatContextCustomizers().addAll((Collection)contextCustomizers.orderedStream().collect(Collectors.toList()));
            factory.getTomcatProtocolHandlerCustomizers().addAll((Collection)protocolHandlerCustomizers.orderedStream().collect(Collectors.toList()));
            return factory;
        }
    }
}

可以看到tomcatServletWebServerFactory方法的三个参数都是都是ObjectProvider类型,泛型为各特性自定义类。

一、ObjectProvider接口定义

public interface ObjectProvider<T> extends ObjectFactory<T>, Iterable<T> {
    // ObjectFactory接口方法,获取无参构造参数化泛型类实例,没有则报NoSuchBeanDefinitionException
	T getObject() throws BeansException;

    // 根据构造参数获取对应参数化泛型类实例,没有则报NoSuchBeanDefinitionException
	T getObject(Object... var1) throws BeansException;

	// 获取无参构造参数化泛型类实例,没有则返回空
    @Nullable
    T getIfAvailable() throws BeansException;

	..................省略.........................
	
	// 唯一限制,如果存在多个实例,则返回null
    @Nullable
    T getIfUnique() throws BeansException;

	// 多参数化包装类实例,作为迭代器使用
    default Iterator<T> iterator() {
        return this.stream().iterator();
    }

	// 参数化包装类集合流式使用
    default Stream<T> stream() {
        throw new UnsupportedOperationException("Multi element access not supported");
    }

	// 参数化包装类集合有序流式使用
    default Stream<T> orderedStream() {
        throw new UnsupportedOperationException("Ordered element access not supported");
    }
}

接口定义部分方法已隐藏,根据ObjectProvider的接口可以看到,此类其实将其泛型参数化类的注入处理做了一层封装,将泛型参数化类实现类是否存在、是否唯一、多实例流式处理等场景归纳为统一的接口方法,提升框架层代码的抽象和整洁。

二、ObjectProvider解析源码
仅通过类参数定义ObjectProvider实例是没有意义的,其底层肯定会有对应的解析,我们真正需要的还是参数化类型中的实例对象,这其中要依靠BeanFactory和DependencyDescriptor等相关类的支持。
我们知道@Configuration + @Bean 注解发布的实例和普通@Componet注解发布的实例,无论解析还是实例化的逻辑都存在差异,@Bean注解的方法实例,其解析后的BeanDefinition的factoryMethodName属性为对应的方法名,会使用BeanFactory.instantiateUsingFactoryMethod(beanName, mbd, args) 方法实例化,方法参数则作为dependsOn依赖注入并完成其初始化。

执行流程如下:
1、BeanFactory实例化@Bean注解Bean

BeanFactory.instantiateUsingFactoryMethod(beanName, mbd, args)

2、构造器解析类包装实例化流程

(new ConstructorResolver(BeanFactory)).instantiateUsingFactoryMethod(beanName, mbd, explicitArgs)

3、ConstructorResolver解析方法参数

ArgumentsHolder argsHolder = ConstructorResolver.createArgumentArray(beanName, mbd, resolvedValues, bw, paramTypes, paramNames, candidate, autowiring, ((List)candidates).size() == 1);

// createArgumentArray方法的核心内容,可以看到按顺序逐个解析方法参数
try {
    convertedValue = ConstructorResolver.resolveAutowiredArgument(methodParam, beanName, autowiredBeanNames, (TypeConverter)converter, fallback);
    args.rawArguments[paramIndex] = convertedValue;
    args.arguments[paramIndex] = convertedValue;
    args.preparedArguments[paramIndex] = autowiredArgumentMarker;
    args.resolveNecessary = true;
} catch (BeansException var24) {
  	.....................
}

createArgumentArray方法参数说明:

  • beanName:方法名
  • mbd:@Bean扫描后生成的RootBeanDefinition
  • resolvedValues:null
  • bw:BeanWrapperImpl
  • paramTypes|paramNames:参数Class类型|参数名-集合
  • candidate:@Bean注解的Method对象实例
  • autowiring:true
  • fallback:true

如上展示了Bean方法参数大体解析流程,针对ObjectProvider类型,肯定会有其对应的适配解析流程,如下:

// DefaultListableBeanFactory 实例方法
public Object resolveDependency(DependencyDescriptor descriptor, @Nullable String requestingBeanName, @Nullable Set<String> autowiredBeanNames, @Nullable TypeConverter typeConverter) throws BeansException {
    descriptor.initParameterNameDiscovery(this.getParameterNameDiscoverer());
    if (Optional.class == descriptor.getDependencyType()) {
        return this.createOptionalDependency(descriptor, requestingBeanName);
    } else if (ObjectFactory.class != descriptor.getDependencyType() && ObjectProvider.class != descriptor.getDependencyType()) {
        if (javaxInjectProviderClass == descriptor.getDependencyType()) {
            return (new Jsr330Factory()).createDependencyProvider(descriptor, requestingBeanName);
        } else {
            Object result = this.getAutowireCandidateResolver().getLazyResolutionProxyIfNecessary(descriptor, requestingBeanName);
            if (result == null) {
                result = this.doResolveDependency(descriptor, requestingBeanName, autowiredBeanNames, typeConverter);
            }
            return result;
        }
    } else {
		// 执行此处
        return new DependencyObjectProvider(descriptor, requestingBeanName);
    }
}

上面一段if…else…语句,我们可以看到对Optional、ObjectFactory、ObjectProvider都做了判断,其中ObjectProvider类型参数被DependencyObjectProvider封装,至此我们知道了在@Bean方法实际执行的时候,其ObjectProvider参数的值其实就是封装了方法及参数元数据的DependencyObjectProvider对象,这个对象实现了我们开头所列举的getObject等一系列方法,本质和Proxy类似。

关于ObjectProvider的解析基本结束,最后创建@Bean方法的实例时,其实就是通过Method.invoke(args) 反射机制来完成实例化,args就是上述解析完的参数,方法体相当于类的init等初始化方法,至此一个完整的@Bean实例就创建好了。

总结:
1、 关于DependencyObjectProvider类的源码不作过多解读,各位有兴趣自行查看,核心就是根据方法解析的元数据,通过BeanFactory提供的实例化能力和ObjectProvider接口的暴露能力,供第三方类使用。
2、 DependencyObjectProvider针对泛型有特殊处理,其构造函数中通过构造NestedDependencyDescriptor实例,完成了MethodParameter内部变量nestingLevel值+1,进而在MethodParameter。getNestedGenericParameterType方法中,获取了ParameterizedType泛型化类,BeanFactory在其doResolveDependency方法中完成泛型类的实例创建和获取。
3、 通过以上说明,ObjectProvider其实就是完成BeanFactory对某一类实例【是否存在、单个获取、流式获取】的封装,避免我们在类组合的过程中掺杂过多的条件判断,保证了代码的安全和稳定性。
4、 除了在方法参数中这样定义,类实例变量也同样支持,目测在Autowired的Processor处理中依然会生成DependencyObjectProvider对象【未实际验证】,HttpServletRequest全局注入也有类似的特性【Proxy实现】
5、 ObjectProvider的getObject(Object… args)需要注意,这个方法我们普通业务开发应该用不到,因为参数如果是容器内存在的实例,会自动注入,如果是普通String或int参数,容器实例化后也会报错,只能在此类上加@Lazy注解,然后再通过getObject(String, int)的形式获取其实例。即确保注入类既被扫描到生成Definitiono,又要延迟实例化。
6、 关于@Bean方式的实例定义和解析,后续会在ConfigurationClassParser解读中再详细说明

文章来源:https://blog.csdn.net/src1025/article/details/135151585
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。