在动态注册的第三方bean上使用什么Spring Framework钩子?
我有一个BeanDefinitionRegistryPostProcessor
,用于动态类路径扫描和实例化多个第三方bean(gRPCAbstractStub
实例(。我需要在存根上注册ClientInterceptors
,以便增强的AbstractStub
可以用于应用程序处理。我使用动态创建的*Stub
@Beans
来消除所有@Bean
样板,并确保一致的通道配置。
限制
AbstractStub
实现是gRPC生成的类。我的课程扩展了AbstractStub
- 首选的静态工厂方法是
builder(Channel)
方法;这是手动样板处理@Bean
声明时使用的内容 - 每个存根都需要一个
Channel
作为依赖项。存在多个Channel
@Beans
尝试次数
我尝试了三种方法:
方法1: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
挂钩
无法在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
,只对AbstractStub
实例调用withInterceptors()
:
@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);
}
};
}
};
}