|
17 | 17 | package org.springframework.kafka.listener; |
18 | 18 |
|
19 | 19 | import java.time.Duration; |
| 20 | +import java.util.ArrayList; |
| 21 | +import java.util.Arrays; |
20 | 22 | import java.util.Collections; |
21 | 23 | import java.util.HashMap; |
| 24 | +import java.util.List; |
22 | 25 | import java.util.Map; |
23 | 26 | import java.util.Properties; |
24 | 27 | import java.util.regex.Pattern; |
25 | 28 |
|
| 29 | +import org.aopalliance.aop.Advice; |
| 30 | + |
| 31 | +import org.springframework.aop.framework.Advised; |
| 32 | +import org.springframework.aop.framework.ProxyFactory; |
| 33 | +import org.springframework.aop.support.AopUtils; |
26 | 34 | import org.springframework.core.task.AsyncListenableTaskExecutor; |
27 | 35 | import org.springframework.kafka.support.KafkaHeaders; |
28 | 36 | import org.springframework.kafka.support.TopicPartitionOffset; |
|
31 | 39 | import org.springframework.transaction.PlatformTransactionManager; |
32 | 40 | import org.springframework.transaction.TransactionDefinition; |
33 | 41 | import org.springframework.util.Assert; |
| 42 | +import org.springframework.util.CollectionUtils; |
34 | 43 |
|
35 | 44 | /** |
36 | 45 | * Contains runtime properties for a listener container. |
@@ -164,6 +173,8 @@ public enum EOSMode { |
164 | 173 |
|
165 | 174 | private final Map<String, String> micrometerTags = new HashMap<>(); |
166 | 175 |
|
| 176 | + private final List<Advice> adviceChain = new ArrayList<>(); |
| 177 | + |
167 | 178 | /** |
168 | 179 | * The ack mode to use when auto ack (in the configuration properties) is false. |
169 | 180 | * <ul> |
@@ -280,6 +291,7 @@ public ContainerProperties(TopicPartitionOffset... topicPartitions) { |
280 | 291 | */ |
281 | 292 | public void setMessageListener(Object messageListener) { |
282 | 293 | this.messageListener = messageListener; |
| 294 | + adviseListenerIfNeeded(); |
283 | 295 | } |
284 | 296 |
|
285 | 297 | /** |
@@ -709,6 +721,45 @@ public void setTransactionDefinition(TransactionDefinition transactionDefinition |
709 | 721 | this.transactionDefinition = transactionDefinition; |
710 | 722 | } |
711 | 723 |
|
| 724 | + /** |
| 725 | + * A chain of listener {@link Advice}s. |
| 726 | + * @return the adviceChain. |
| 727 | + * @since 2.5.6 |
| 728 | + */ |
| 729 | + public Advice[] getAdviceChain() { |
| 730 | + return this.adviceChain.toArray(new Advice[0]); |
| 731 | + } |
| 732 | + |
| 733 | + /** |
| 734 | + * Set a chain of listener {@link Advice}s; must not be null or have null elements. |
| 735 | + * @param adviceChain the adviceChain to set. |
| 736 | + * @since 2.5.6 |
| 737 | + */ |
| 738 | + public void setAdviceChain(Advice... adviceChain) { |
| 739 | + Assert.notNull(adviceChain, "'adviceChain' cannot be null"); |
| 740 | + Assert.noNullElements(adviceChain, "'adviceChain' cannot have null elements"); |
| 741 | + this.adviceChain.clear(); |
| 742 | + this.adviceChain.addAll(Arrays.asList(adviceChain)); |
| 743 | + if (this.messageListener != null) { |
| 744 | + adviseListenerIfNeeded(); |
| 745 | + } |
| 746 | + } |
| 747 | + |
| 748 | + private void adviseListenerIfNeeded() { |
| 749 | + if (!CollectionUtils.isEmpty(this.adviceChain)) { |
| 750 | + if (AopUtils.isAopProxy(this.messageListener)) { |
| 751 | + Advised advised = (Advised) this.messageListener; |
| 752 | + this.adviceChain.forEach(advised::removeAdvice); |
| 753 | + this.adviceChain.forEach(advised::addAdvice); |
| 754 | + } |
| 755 | + else { |
| 756 | + ProxyFactory pf = new ProxyFactory(this.messageListener); |
| 757 | + this.adviceChain.forEach(pf::addAdvice); |
| 758 | + this.messageListener = pf.getProxy(); |
| 759 | + } |
| 760 | + } |
| 761 | + } |
| 762 | + |
712 | 763 | @Override |
713 | 764 | public String toString() { |
714 | 765 | return "ContainerProperties [" |
|
0 commit comments