【记录版】SpringBoot下ObjectProvider类源码及其执行机制解读
主题: 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解读中再详细说明
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!