java - 动态注册的第三方 Bean 上的类似 Spring 的后处理器 Hook

标签 java spring grpc spring-bean grpc-java

我应该在动态注册的第三方 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/

相关文章:

java - 注册日期需要以特定格式在请求正文中传递

戈朗 : grpc call timeout

ios - grpc 在 Macbook 中运行 ionic 项目的问题

java - 什么时候应该在 GRPC 上关闭 channel ?

java - 如何在 Eclipse 中运行来自 github 的应用程序

java - Qt中如何调用context.getExternalFilesDir

java - 具有 namespace 解码的 JAXB(使用来自 REST 服务的 Jersey)

java.net.MalformedURLException : unknown protocol: classpath

java - 如何防止密码重新生成?

java - AspectJ 表达式在切入点错误中给出正式的未绑定(bind)