java - 动态注册的第三方 Bean 上的类似 Spring 的 PostProcessor Hook
问题描述
我在动态注册的第三方 bean 上使用什么 Spring Framework 挂钩?
我有一个BeanDefinitionRegistryPostProcessor
用于动态类路径扫描和实例化多个第三方 bean(gRPCAbstractStub
实例)的方法。我需要ClientInterceptors
在存根上注册,以便增强AbstractStub
的可以进行应用程序处理。我使用动态创建*Stub
@Beans
来消除所有@Bean
样板并确保一致的通道配置。
约束
AbstractStub
实现是 gRPC 生成的类。我的课程扩展AbstractStub
。- 首选的静态工厂方法是
builder(Channel)
方法;这是手动样板化@Bean
声明时使用的。 - 每个存根都需要 a
Channel
作为依赖项。有多个Channel
@Beans
.
尝试
我尝试了三种方法:
方法一:BeanDefinitionBuilder
+Supplier
函数
BeanDefinitionBuilder.genericBeanDefinition(Class, Supplier)
不允许注入Channel
依赖项。
void registerBeanDefintion(final Class<S> clazz, final BeanDefinitionRegistry registry) {
Supplier<S> stubSupplier = () -> {
clazz.getConstructor({Channel.class});
return BeanUtils.instantiateClass(constructor, null); // fails here; no Channel
}
BeanDefinitionBuilder builder =
BeanDefinitionBuilder.genericBeanDefinition(clazz, stubSupplier);
builder.addDependsOn(MANAGED_CHANNEL_BEAN_NAME);
builder.addConstructorArgReference(MANAGED_CHANNEL_BEAN_NAME);
registry.registerBeanDefinition(clazz.getName(), builder.getBeanDefinition());
方法2:BeanDefinitionBuilder
用CallOption
钩子
无法ClientInterceptor
在 BeanDefinition 上注册 a。
void registerBeanDefintion(final Class<S> clazz, final BeanDefinitionRegistry registry) {
builder.addDependsOn(MANAGED_CHANNEL_BEAN_NAME);
builder.addConstructorArgReference(MANAGED_CHANNEL_BEAN_NAME);
CallOptions callOptions = CallOptions.DEFAULT;
// no hook in CallOptions to register ClientInterceptor
registry.registerBeanDefinition(clazz.getName(), builder.getBeanDefinition());
方法3:postProcessBeanFactory()
postProcessBeanFactory
不在实例化的 bean 上运行,因此依赖关系不是预先解决的。
public void postProcessBeanFactory(ConfigurableListableBeanFactory configurableListableBeanFactory) throws BeansException {
Iterator<String> iterator = configurableListableBeanFactory.getBeanNamesIterator();
while (iterator.hasNext()) {
String beanName = iterator.next();
if (beanName.endsWith("Stub")) {
AbstractStub stub = (AbstractStub) configurableListableBeanFactory.getBean(beanName); //fails
stub.withInterceptors(newClientInterceptor()); // never gets executed
}
}
}
解决方案
因为我有一些单独的模块,所以我有点过于复杂了:解决方案是使用简单的并仅对实例BeanPostProcessor
调用:withInterceptors()
AbstractStub
@Override
public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
if (bean instanceof AbstractStub) {
AbstractStub stub = (AbstractStub) bean;
log.debug("modify bean '{}': add timeout client interceptor", beanName);
ClientInterceptor timeoutClientInterceptor = this.newTimeoutClientInterceptor(stub);
AbstractStub result = stub.withInterceptors(timeoutClientInterceptor);
return result;
}
return bean;
}
ClientInterceptor newTimeoutClientInterceptor(final AbstractStub stub) {
final Deadline deadline = this.getDeadlineTimeout(stub);
return new ClientInterceptor() {
@Override
public <ReqT, RespT> ClientCall<ReqT, RespT> interceptCall(MethodDescriptor<ReqT, RespT> method, CallOptions callOptions, Channel next) {
final ClientCall<ReqT, RespT> clientCall = next.newCall(method, callOptions.withDeadline(deadline));
return new ClientInterceptors.CheckedForwardingClientCall<ReqT, RespT>(clientCall) {
@Override
protected void checkedStart(Listener<RespT> listener, Metadata metadata) {
log.debug("execute call with deadline {}", deadline);
delegate().start(listener, metadata);
}
};
}
};
}
推荐阅读
- css - 我可以让 div 像按钮一样缩小到文本宽度吗?
- node.js - 如何设置本地 AWS Secrets Manager Docker 容器以进行本地测试?
- python - 访问 SharePoint 导致文件损坏
- excel - 如何从工作表中提取公式并在前面添加“=”以运行
- swiftui - SwiftUI:目标视图的导航栏没有背景,滚动时没有动画
- python - Subprocess.run() 找不到文件,即使 path.exists() 返回 true
- rust - 在 and_then 的类型定义中,T 来自哪里?
- android - java中的Firebase身份验证登录问题
- sql - 使用 JPA 投影重写 MariaDB 的 SQL 查询
- git - 在 Jenkins 管道中访问 currentBuild.changeSets 时抛出 NotSerializableException