|
62 | 62 | import org.springframework.context.ApplicationContextAware;
|
63 | 63 | import org.springframework.context.ConfigurableApplicationContext;
|
64 | 64 | import org.springframework.context.expression.StandardBeanExpressionResolver;
|
| 65 | +import org.springframework.context.support.GenericApplicationContext; |
65 | 66 | import org.springframework.core.MethodIntrospector;
|
66 | 67 | import org.springframework.core.OrderComparator;
|
67 | 68 | import org.springframework.core.Ordered;
|
|
83 | 84 | import org.springframework.kafka.config.MethodKafkaListenerEndpoint;
|
84 | 85 | import org.springframework.kafka.config.MultiMethodKafkaListenerEndpoint;
|
85 | 86 | import org.springframework.kafka.listener.ContainerGroupSequencer;
|
| 87 | +import org.springframework.kafka.listener.KafkaConsumerBackoffManager; |
86 | 88 | import org.springframework.kafka.listener.KafkaListenerErrorHandler;
|
87 | 89 | import org.springframework.kafka.listener.adapter.RecordFilterStrategy;
|
| 90 | +import org.springframework.kafka.retrytopic.DestinationTopicResolver; |
88 | 91 | import org.springframework.kafka.retrytopic.RetryTopicBeanNames;
|
89 | 92 | import org.springframework.kafka.retrytopic.RetryTopicConfiguration;
|
| 93 | +import org.springframework.kafka.retrytopic.RetryTopicConfigurationSupport; |
90 | 94 | import org.springframework.kafka.retrytopic.RetryTopicConfigurer;
|
| 95 | +import org.springframework.kafka.retrytopic.RetryTopicSchedulerWrapper; |
91 | 96 | import org.springframework.kafka.support.TopicPartitionOffset;
|
92 | 97 | import org.springframework.lang.Nullable;
|
93 | 98 | import org.springframework.messaging.converter.GenericMessageConverter;
|
|
96 | 101 | import org.springframework.messaging.handler.annotation.support.MessageHandlerMethodFactory;
|
97 | 102 | import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver;
|
98 | 103 | import org.springframework.messaging.handler.invocation.InvocableHandlerMethod;
|
| 104 | +import org.springframework.scheduling.TaskScheduler; |
| 105 | +import org.springframework.scheduling.concurrent.ThreadPoolTaskScheduler; |
99 | 106 | import org.springframework.util.Assert;
|
100 | 107 | import org.springframework.util.ReflectionUtils;
|
101 | 108 | import org.springframework.util.StringUtils;
|
@@ -516,14 +523,50 @@ private RetryTopicConfigurer getRetryTopicConfigurer() {
|
516 | 523 | .getBean(RetryTopicBeanNames.RETRY_TOPIC_CONFIGURER_BEAN_NAME, RetryTopicConfigurer.class);
|
517 | 524 | }
|
518 | 525 | catch (NoSuchBeanDefinitionException ex) {
|
519 |
| - this.logger.error("A 'RetryTopicConfigurer' with name " |
520 |
| - + RetryTopicBeanNames.RETRY_TOPIC_CONFIGURER_BEAN_NAME + "is required."); |
521 |
| - throw ex; |
| 526 | + this.retryTopicConfigurer = createDefaultConfigurer(); |
522 | 527 | }
|
523 | 528 | }
|
524 | 529 | return this.retryTopicConfigurer;
|
525 | 530 | }
|
526 | 531 |
|
| 532 | + private RetryTopicConfigurer createDefaultConfigurer() { |
| 533 | + if (this.applicationContext instanceof GenericApplicationContext) { |
| 534 | + GenericApplicationContext gac = (GenericApplicationContext) this.applicationContext; |
| 535 | + gac.registerBean( |
| 536 | + RetryTopicBeanNames.DEFAULT_RETRY_TOPIC_CONFIG_SUPPORT_BEAN_NAME, |
| 537 | + RetryTopicConfigurationSupport.class, |
| 538 | + () -> new RetryTopicConfigurationSupport()); |
| 539 | + RetryTopicConfigurationSupport rtcs = this.applicationContext.getBean( |
| 540 | + RetryTopicBeanNames.DEFAULT_RETRY_TOPIC_CONFIG_SUPPORT_BEAN_NAME, |
| 541 | + RetryTopicConfigurationSupport.class); |
| 542 | + DestinationTopicResolver destResolver = rtcs.destinationTopicResolver(); |
| 543 | + RetryTopicSchedulerWrapper schedW = gac.getBeanProvider(RetryTopicSchedulerWrapper.class).getIfUnique(); |
| 544 | + TaskScheduler sched = gac.getBeanProvider(TaskScheduler.class).getIfUnique(); |
| 545 | + if (schedW == null && sched == null) { |
| 546 | + RetryTopicSchedulerWrapper newSchedW = new RetryTopicSchedulerWrapper(new ThreadPoolTaskScheduler()); |
| 547 | + gac.registerBean(RetryTopicBeanNames.DEFAULT_SCHEDULER_WRAPPER_BEAN_NAME, |
| 548 | + RetryTopicSchedulerWrapper.class, () -> newSchedW); |
| 549 | + schedW = gac.getBean(RetryTopicSchedulerWrapper.class); |
| 550 | + } |
| 551 | + KafkaConsumerBackoffManager bom = |
| 552 | + rtcs.kafkaConsumerBackoffManager(this.applicationContext, this.registrar.getEndpointRegistry(), |
| 553 | + schedW, sched); |
| 554 | + RetryTopicConfigurer rtc = rtcs.retryTopicConfigurer(bom, destResolver, this.beanFactory); |
| 555 | + |
| 556 | + gac.registerBean(RetryTopicBeanNames.DESTINATION_TOPIC_RESOLVER_BEAN_NAME, DestinationTopicResolver.class, |
| 557 | + () -> destResolver); |
| 558 | + gac.registerBean(KafkaListenerConfigUtils.KAFKA_CONSUMER_BACK_OFF_MANAGER_BEAN_NAME, |
| 559 | + KafkaConsumerBackoffManager.class, () -> bom); |
| 560 | + gac.registerBean(RetryTopicBeanNames.RETRY_TOPIC_CONFIGURER_BEAN_NAME, RetryTopicConfigurer.class, |
| 561 | + () -> rtc); |
| 562 | + |
| 563 | + return this.beanFactory |
| 564 | + .getBean(RetryTopicBeanNames.RETRY_TOPIC_CONFIGURER_BEAN_NAME, RetryTopicConfigurer.class); |
| 565 | + } |
| 566 | + throw new IllegalStateException("When there is no RetryTopicConfigurationSupport bean, the application context " |
| 567 | + + "must be a GenericApplicationContext"); |
| 568 | + } |
| 569 | + |
527 | 570 | private Method checkProxy(Method methodArg, Object bean) {
|
528 | 571 | Method method = methodArg;
|
529 | 572 | if (AopUtils.isJdkDynamicProxy(bean)) {
|
|
0 commit comments