我应该在动态注册的第三方 bean 上使用什么 Spring 框架钩子(Hook)?
我有一个BeanDefinitionRegistryPostProcessor
我用它来动态类路径扫描并实例化多个第三方 bean(gRPC AbstractStub
实例)。我需要注册ClientInterceptors
stub 上,以便增强的 AbstractStub
准备好进行应用程序处理。我使用动态创建的 *Stub
@Beans
来消除所有 @Bean
样板文件并确保一致的 channel 配置。
约束
AbstractStub
实现是 gRPC 生成的类。我的类扩展了AbstractStub
。- 首选使用的静态工厂方法是
builder(Channel)
方法;这是手动模板化@Bean
声明时使用的内容。 - 每个 stub 都需要一个
Channel
作为依赖项。有多个Channel
@Beans
。
尝试
我尝试了三种方法:
方法 1:BeanDefinitionBuilder
+ Supplier
函数
BeanDefinitionBuilder.genericBeanDefinition(Class, Supplier)
不允许注入(inject) 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:带有 CallOption
Hook 的 BeanDefinitionBuilder
无法在 BeanDefinition 上注册 ClientInterceptor
。
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() 调用
实例: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);
}
};
}
};
}
关于java - 动态注册的第三方 Bean 上的类似 Spring 的后处理器 Hook ,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/61513575/