Spring-like PostProcessor Hook на динамически зарегистрированном стороннем компоненте - PullRequest
2 голосов
/ 30 апреля 2020

Какой хук Spring Framework я использую на динамически зарегистрированных сторонних bean-компонентах?

У меня есть BeanDefinitionRegistryPostProcessor, который я использую для динамического сканирования пути к классам и создания нескольких третьих партийные бобы (экземпляры gRP C AbstractStub). Мне нужно зарегистрировать ClientInterceptors на заглушке, чтобы дополненный AbstractStub был готов к обработке заявки. Я использую динамически создаваемый *Stub @Beans, чтобы исключить все шаблоны @Bean и обеспечить согласованные конфигурации каналов.

Ограничения

  • Реализации AbstractStub соответствуют gRP C порожденные классы. Мои классы расширяются AbstractStub.
  • Предпочтительный фабричный метод stati c - метод 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 крючками

Невозможно зарегистрировать ClientInterceptor в BeanDefinition.

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
        }
    }
}

1 Ответ

0 голосов
/ 04 мая 2020

Я немного усложнил это, поскольку у меня было несколько отдельных модулей: решение состоит в том, чтобы использовать простой 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);
                    }
                };
            }
        };
    }

...