diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/MessagingAnnotationBeanPostProcessor.java b/spring-integration-core/src/main/java/org/springframework/integration/config/MessagingAnnotationBeanPostProcessor.java new file mode 100644 index 00000000000..e9554ebc66d --- /dev/null +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/MessagingAnnotationBeanPostProcessor.java @@ -0,0 +1,248 @@ +/* + * Copyright 2023 the original author or authors. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * https://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.springframework.integration.config; + +import java.lang.annotation.Annotation; +import java.lang.reflect.Method; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; + +import org.springframework.aop.support.AopUtils; +import org.springframework.beans.BeansException; +import org.springframework.beans.factory.BeanFactory; +import org.springframework.beans.factory.BeanFactoryAware; +import org.springframework.beans.factory.SmartInitializingSingleton; +import org.springframework.beans.factory.config.BeanPostProcessor; +import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.BeanDefinitionRegistry; +import org.springframework.beans.factory.support.RootBeanDefinition; +import org.springframework.context.annotation.Bean; +import org.springframework.core.annotation.AnnotationUtils; +import org.springframework.core.annotation.MergedAnnotation; +import org.springframework.core.annotation.MergedAnnotations; +import org.springframework.integration.annotation.EndpointId; +import org.springframework.integration.annotation.Role; +import org.springframework.integration.config.annotation.MethodAnnotationPostProcessor; +import org.springframework.integration.endpoint.AbstractEndpoint; +import org.springframework.integration.util.MessagingAnnotationUtils; +import org.springframework.util.Assert; +import org.springframework.util.ClassUtils; +import org.springframework.util.ReflectionUtils; +import org.springframework.util.StringUtils; + +/** + * An infrastructure {@link BeanPostProcessor} implementation that processes method-level + * messaging annotations such as @Transformer, @Splitter, @Router, and @Filter. + * + * @author Artem Bilan + * + * @since 6.2 + */ +public class MessagingAnnotationBeanPostProcessor + implements BeanPostProcessor, BeanFactoryAware, SmartInitializingSingleton { + + private final Set> noAnnotationsCache = Collections.newSetFromMap(new ConcurrentHashMap<>(256)); + + private final Map, MethodAnnotationPostProcessor> postProcessors; + + private final List methodsToPostProcessAfterContextInitialization = new ArrayList<>(); + + private final BeanDefinitionRegistry registry; + + private ConfigurableListableBeanFactory beanFactory; + + private volatile boolean initialized; + + public MessagingAnnotationBeanPostProcessor(BeanDefinitionRegistry registry, + Map, MethodAnnotationPostProcessor> postProcessors) { + + this.registry = registry; + this.postProcessors = postProcessors; + } + + @Override + public void setBeanFactory(BeanFactory beanFactory) throws BeansException { + this.beanFactory = (ConfigurableListableBeanFactory) beanFactory; + } + + @Override + public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException { + Assert.notNull(this.beanFactory, "BeanFactory must not be null"); + + Class beanClass = AopUtils.getTargetClass(bean); + + // the set will hold records of prior class scans and indicate if no messaging annotations were found + if (this.noAnnotationsCache.contains(beanClass)) { + return bean; + } + + ReflectionUtils.doWithMethods(beanClass, + method -> doWithMethod(method, bean, beanName, beanClass), + ReflectionUtils.USER_DECLARED_METHODS); + + return bean; + } + + private void doWithMethod(Method method, Object bean, String beanName, Class beanClass) { + MergedAnnotations mergedAnnotations = MergedAnnotations.from(method); + + boolean noMessagingAnnotations = true; + + // See MessagingAnnotationPostProcessor.postProcessBeanDefinitionRegistry(BeanDefinitionRegistry) + if (!mergedAnnotations.isPresent(Bean.class)) { + List messagingAnnotations = + obtainMessagingAnnotations(this.postProcessors.keySet(), mergedAnnotations, + method.toGenericString()); + + for (MessagingMetaAnnotation messagingAnnotation : messagingAnnotations) { + noMessagingAnnotations = false; + Class annotationType = messagingAnnotation.annotationType; + List annotationChain = + MessagingAnnotationUtils.getAnnotationChain(messagingAnnotation.annotation, annotationType); + processAnnotationTypeOnMethod(bean, beanName, method, annotationType, annotationChain); + } + } + if (noMessagingAnnotations) { + this.noAnnotationsCache.add(beanClass); + } + } + + private void processAnnotationTypeOnMethod(Object bean, String beanName, Method method, + Class annotationType, List annotations) { + + MethodAnnotationPostProcessor postProcessor = this.postProcessors.get(annotationType); + if (postProcessor != null && postProcessor.supportsPojoMethod() + && postProcessor.shouldCreateEndpoint(method, annotations)) { + + Method targetMethod = method; + if (AopUtils.isJdkDynamicProxy(bean)) { + try { + targetMethod = bean.getClass().getMethod(method.getName(), method.getParameterTypes()); + } + catch (NoSuchMethodException e) { + throw new IllegalArgumentException("Service methods must be extracted to the service " + + "interface for JdkDynamicProxy. The affected bean is: '" + beanName + "' " + + "and its method: '" + method + "'", e); + } + } + + if (this.initialized) { + postProcessMethodAndRegisterEndpointIfAny(bean, beanName, method, annotationType, annotations, + postProcessor, targetMethod); + } + else { + Method methodToPostProcess = targetMethod; + this.methodsToPostProcessAfterContextInitialization.add(() -> + postProcessMethodAndRegisterEndpointIfAny(bean, beanName, method, annotationType, annotations, + postProcessor, methodToPostProcess)); + } + } + } + + @SuppressWarnings("unchecked") + private void postProcessMethodAndRegisterEndpointIfAny(Object bean, String beanName, Method method, + Class annotationType, List annotations, + MethodAnnotationPostProcessor postProcessor, Method targetMethod) { + + Object result = postProcessor.postProcess(bean, beanName, targetMethod, annotations); + if (result instanceof AbstractEndpoint endpoint) { + String autoStartup = MessagingAnnotationUtils.resolveAttribute(annotations, "autoStartup", String.class); + if (StringUtils.hasText(autoStartup)) { + autoStartup = this.beanFactory.resolveEmbeddedValue(autoStartup); + if (StringUtils.hasText(autoStartup)) { + endpoint.setAutoStartup(Boolean.parseBoolean(autoStartup)); + } + } + + String phase = MessagingAnnotationUtils.resolveAttribute(annotations, "phase", String.class); + if (StringUtils.hasText(phase)) { + phase = this.beanFactory.resolveEmbeddedValue(phase); + if (StringUtils.hasText(phase)) { + endpoint.setPhase(Integer.parseInt(phase)); + } + } + + Role role = AnnotationUtils.findAnnotation(method, Role.class); + if (role != null) { + endpoint.setRole(role.value()); + } + + String endpointBeanName = generateBeanName(beanName, method, annotationType); + endpoint.setBeanName(endpointBeanName); + this.registry.registerBeanDefinition(endpointBeanName, + new RootBeanDefinition((Class) endpoint.getClass(), () -> endpoint)); + this.beanFactory.getBean(endpointBeanName); + } + } + + + protected String generateBeanName(String originalBeanName, Method method, + Class annotationType) { + + String name = MessagingAnnotationUtils.endpointIdValue(method); + if (!StringUtils.hasText(name)) { + String baseName = originalBeanName + "." + method.getName() + "." + + ClassUtils.getShortNameAsProperty(annotationType); + name = baseName; + int count = 1; + while (this.beanFactory.containsBean(name)) { + name = baseName + "#" + (++count); + } + } + return name; + } + + @Override + public void afterSingletonsInstantiated() { + this.initialized = true; + this.methodsToPostProcessAfterContextInitialization.forEach(Runnable::run); + this.methodsToPostProcessAfterContextInitialization.clear(); + } + + protected static List obtainMessagingAnnotations( + Set> postProcessors, MergedAnnotations annotations, String identified) { + + List messagingAnnotations = new ArrayList<>(); + + for (Class annotationType : postProcessors) { + annotations.stream() + .filter((ann) -> ann.getType().equals(annotationType)) + .map(MergedAnnotation::getRoot) + .map(MergedAnnotation::synthesize) + .map((ann) -> new MessagingMetaAnnotation(ann, annotationType)) + .forEach(messagingAnnotations::add); + } + + if (annotations.get(EndpointId.class, (ann) -> ann.hasNonDefaultValue("value")).isPresent() + && messagingAnnotations.size() > 1) { + + throw new IllegalStateException("@EndpointId on " + identified + + " can only have one EIP annotation, found: " + messagingAnnotations.size()); + } + + return messagingAnnotations; + } + + public record MessagingMetaAnnotation(Annotation annotation, Class annotationType) { + + } + +} diff --git a/spring-integration-core/src/main/java/org/springframework/integration/config/MessagingAnnotationPostProcessor.java b/spring-integration-core/src/main/java/org/springframework/integration/config/MessagingAnnotationPostProcessor.java index 766ad1a2518..8813a1a3717 100644 --- a/spring-integration-core/src/main/java/org/springframework/integration/config/MessagingAnnotationPostProcessor.java +++ b/spring-integration-core/src/main/java/org/springframework/integration/config/MessagingAnnotationPostProcessor.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -16,58 +16,38 @@ package org.springframework.integration.config; +import java.beans.Introspector; import java.lang.annotation.Annotation; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.Collections; import java.util.HashMap; import java.util.List; import java.util.Map; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import org.apache.commons.logging.Log; -import org.apache.commons.logging.LogFactory; - -import org.springframework.aop.support.AopUtils; -import org.springframework.aot.AotDetector; import org.springframework.beans.BeansException; import org.springframework.beans.factory.BeanFactory; import org.springframework.beans.factory.BeanFactoryAware; -import org.springframework.beans.factory.SmartInitializingSingleton; import org.springframework.beans.factory.annotation.AnnotatedBeanDefinition; import org.springframework.beans.factory.config.BeanDefinition; import org.springframework.beans.factory.config.BeanPostProcessor; import org.springframework.beans.factory.config.ConfigurableListableBeanFactory; +import org.springframework.beans.factory.support.BeanDefinitionBuilder; import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor; import org.springframework.beans.factory.support.BeanDefinitionValidationException; -import org.springframework.beans.factory.support.RootBeanDefinition; -import org.springframework.context.annotation.Bean; -import org.springframework.core.annotation.AnnotationUtils; -import org.springframework.core.annotation.MergedAnnotation; import org.springframework.core.annotation.MergedAnnotations; import org.springframework.core.type.MethodMetadata; import org.springframework.core.type.StandardMethodMetadata; import org.springframework.integration.annotation.Aggregator; import org.springframework.integration.annotation.BridgeFrom; import org.springframework.integration.annotation.BridgeTo; -import org.springframework.integration.annotation.EndpointId; import org.springframework.integration.annotation.Filter; import org.springframework.integration.annotation.InboundChannelAdapter; -import org.springframework.integration.annotation.Role; import org.springframework.integration.annotation.Router; import org.springframework.integration.annotation.ServiceActivator; import org.springframework.integration.annotation.Splitter; import org.springframework.integration.annotation.Transformer; import org.springframework.integration.config.annotation.MethodAnnotationPostProcessor; -import org.springframework.integration.endpoint.AbstractEndpoint; import org.springframework.integration.util.MessagingAnnotationUtils; -import org.springframework.util.Assert; -import org.springframework.util.ClassUtils; import org.springframework.util.CollectionUtils; -import org.springframework.util.ReflectionUtils; -import org.springframework.util.StringUtils; /** * A {@link BeanPostProcessor} implementation that processes method-level @@ -79,26 +59,18 @@ * @author Gary Russell * @author Rick Hogge */ -public class MessagingAnnotationPostProcessor - implements BeanDefinitionRegistryPostProcessor, BeanPostProcessor, SmartInitializingSingleton { - - protected final Log logger = LogFactory.getLog(this.getClass()); // NOSONAR +public class MessagingAnnotationPostProcessor implements BeanDefinitionRegistryPostProcessor { private final Map, MethodAnnotationPostProcessor> postProcessors = new HashMap<>(); - private final Set> noAnnotationsCache = Collections.newSetFromMap(new ConcurrentHashMap<>(256)); - - private final List methodsToPostProcessAfterContextInitialization = new ArrayList<>(); - private BeanDefinitionRegistry registry; private ConfigurableListableBeanFactory beanFactory; - private volatile boolean initialized; - @Override public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException { this.registry = registry; + this.postProcessors.put(Filter.class, new FilterAnnotationPostProcessor()); this.postProcessors.put(Router.class, new RouterAnnotationPostProcessor()); this.postProcessors.put(Transformer.class, new TransformerAnnotationPostProcessor()); @@ -108,6 +80,7 @@ public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) t this.postProcessors.put(InboundChannelAdapter.class, new InboundChannelAdapterAnnotationPostProcessor()); this.postProcessors.put(BridgeFrom.class, new BridgeFromAnnotationPostProcessor()); this.postProcessors.put(BridgeTo.class, new BridgeToAnnotationPostProcessor()); + Map, MethodAnnotationPostProcessor> customPostProcessors = setupCustomPostProcessors(); if (!CollectionUtils.isEmpty(customPostProcessors)) { @@ -118,16 +91,22 @@ public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) t .map(BeanFactoryAware.class::cast) .forEach((processor) -> processor.setBeanFactory((BeanFactory) this.registry)); - if (!AotDetector.useGeneratedArtifacts()) { - String[] beanNames = registry.getBeanDefinitionNames(); + this.registry.registerBeanDefinition( + Introspector.decapitalize(MessagingAnnotationBeanPostProcessor.class.getName()), + BeanDefinitionBuilder.rootBeanDefinition(MessagingAnnotationBeanPostProcessor.class) + .setRole(BeanDefinition.ROLE_INFRASTRUCTURE) + .addConstructorArgValue(this.registry) + .addConstructorArgValue(this.postProcessors) + .getBeanDefinition()); - for (String beanName : beanNames) { - BeanDefinition beanDef = registry.getBeanDefinition(beanName); - if (beanDef instanceof AnnotatedBeanDefinition annotatedBeanDefinition - && annotatedBeanDefinition.getFactoryMethodMetadata() != null) { + String[] beanNames = registry.getBeanDefinitionNames(); - processCandidate(beanName, annotatedBeanDefinition); - } + for (String beanName : beanNames) { + BeanDefinition beanDef = registry.getBeanDefinition(beanName); + if (beanDef instanceof AnnotatedBeanDefinition annotatedBeanDefinition + && annotatedBeanDefinition.getFactoryMethodMetadata() != null) { + + processCandidate(beanName, annotatedBeanDefinition); } } } @@ -139,39 +118,19 @@ private void processCandidate(String beanName, AnnotatedBeanDefinition beanDefin annotations = MergedAnnotations.from(standardMethodMetadata.getIntrospectedMethod()); } - List messagingAnnotations = obtainMessagingAnnotations(annotations, beanName); + List messagingAnnotations = + MessagingAnnotationBeanPostProcessor.obtainMessagingAnnotations(this.postProcessors.keySet(), + annotations, beanName); - for (MessagingMetaAnnotation messagingAnnotation : messagingAnnotations) { - Class annotationType = messagingAnnotation.annotationType; + for (MessagingAnnotationBeanPostProcessor.MessagingMetaAnnotation messagingAnnotation : messagingAnnotations) { + Class annotationType = messagingAnnotation.annotationType(); List annotationChain = - MessagingAnnotationUtils.getAnnotationChain(messagingAnnotation.annotation, annotationType); + MessagingAnnotationUtils.getAnnotationChain(messagingAnnotation.annotation(), annotationType); processMessagingAnnotationOnBean(beanName, beanDefinition, annotationType, annotationChain); } } - private List obtainMessagingAnnotations(MergedAnnotations annotations, String identified) { - List messagingAnnotations = new ArrayList<>(); - - for (Class annotationType : this.postProcessors.keySet()) { - annotations.stream() - .filter((ann) -> ann.getType().equals(annotationType)) - .map(MergedAnnotation::getRoot) - .map(MergedAnnotation::synthesize) - .map((ann) -> new MessagingMetaAnnotation(ann, annotationType)) - .forEach(messagingAnnotations::add); - } - - if (annotations.get(EndpointId.class, (ann) -> ann.hasNonDefaultValue("value")).isPresent() - && messagingAnnotations.size() > 1) { - - throw new IllegalStateException("@EndpointId on " + identified - + " can only have one EIP annotation, found: " + messagingAnnotations.size()); - } - - return messagingAnnotations; - } - private void processMessagingAnnotationOnBean(String beanName, AnnotatedBeanDefinition beanDefinition, Class annotationType, List annotationChain) { @@ -220,150 +179,8 @@ public void addMessagingAnnotationPostProcessor(Class this.postProcessors.put(annotation, postProcessor); } - @Override - public void afterSingletonsInstantiated() { - this.initialized = true; - this.methodsToPostProcessAfterContextInitialization.forEach(Runnable::run); - this.methodsToPostProcessAfterContextInitialization.clear(); - } - - @Override - public Object postProcessBeforeInitialization(Object bean, String beanName) throws BeansException { - return bean; - } - - @Override - public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException { - Assert.notNull(this.beanFactory, "BeanFactory must not be null"); - - Class beanClass = AopUtils.getTargetClass(bean); - - // the set will hold records of prior class scans and indicate if no messaging annotations were found - if (this.noAnnotationsCache.contains(beanClass)) { - return bean; - } - - ReflectionUtils.doWithMethods(beanClass, - method -> doWithMethod(method, bean, beanName, beanClass), - ReflectionUtils.USER_DECLARED_METHODS); - - return bean; - } - - private void doWithMethod(Method method, Object bean, String beanName, Class beanClass) { - MergedAnnotations mergedAnnotations = MergedAnnotations.from(method); - - boolean noMessagingAnnotations = true; - - if (!mergedAnnotations.isPresent(Bean.class)) { // See postProcessBeanDefinitionRegistry(BeanDefinitionRegistry) - List messagingAnnotations = - obtainMessagingAnnotations(mergedAnnotations, method.toGenericString()); - - for (MessagingMetaAnnotation messagingAnnotation : messagingAnnotations) { - noMessagingAnnotations = false; - Class annotationType = messagingAnnotation.annotationType; - List annotationChain = - MessagingAnnotationUtils.getAnnotationChain(messagingAnnotation.annotation, annotationType); - processAnnotationTypeOnMethod(bean, beanName, method, annotationType, annotationChain); - } - } - if (noMessagingAnnotations) { - this.noAnnotationsCache.add(beanClass); - } - } - - protected void processAnnotationTypeOnMethod(Object bean, String beanName, Method method, - Class annotationType, List annotations) { - - MethodAnnotationPostProcessor postProcessor = - MessagingAnnotationPostProcessor.this.postProcessors.get(annotationType); - if (postProcessor != null && postProcessor.supportsPojoMethod() - && postProcessor.shouldCreateEndpoint(method, annotations)) { - - Method targetMethod = method; - if (AopUtils.isJdkDynamicProxy(bean)) { - try { - targetMethod = bean.getClass().getMethod(method.getName(), method.getParameterTypes()); - } - catch (NoSuchMethodException e) { - throw new IllegalArgumentException("Service methods must be extracted to the service " - + "interface for JdkDynamicProxy. The affected bean is: '" + beanName + "' " - + "and its method: '" + method + "'", e); - } - } - - if (this.initialized) { - postProcessMethodAndRegisterEndpointIfAny(bean, beanName, method, annotationType, annotations, - postProcessor, targetMethod); - } - else { - Method methodToPostProcess = targetMethod; - this.methodsToPostProcessAfterContextInitialization.add(() -> - postProcessMethodAndRegisterEndpointIfAny(bean, beanName, method, annotationType, annotations, - postProcessor, methodToPostProcess)); - } - } - } - - @SuppressWarnings("unchecked") - private void postProcessMethodAndRegisterEndpointIfAny(Object bean, String beanName, Method method, - Class annotationType, List annotations, - MethodAnnotationPostProcessor postProcessor, Method targetMethod) { - - Object result = postProcessor.postProcess(bean, beanName, targetMethod, annotations); - if (result instanceof AbstractEndpoint endpoint) { - String autoStartup = MessagingAnnotationUtils.resolveAttribute(annotations, "autoStartup", String.class); - if (StringUtils.hasText(autoStartup)) { - autoStartup = this.beanFactory.resolveEmbeddedValue(autoStartup); - if (StringUtils.hasText(autoStartup)) { - endpoint.setAutoStartup(Boolean.parseBoolean(autoStartup)); - } - } - - String phase = MessagingAnnotationUtils.resolveAttribute(annotations, "phase", String.class); - if (StringUtils.hasText(phase)) { - phase = this.beanFactory.resolveEmbeddedValue(phase); - if (StringUtils.hasText(phase)) { - endpoint.setPhase(Integer.parseInt(phase)); - } - } - - Role role = AnnotationUtils.findAnnotation(method, Role.class); - if (role != null) { - endpoint.setRole(role.value()); - } - - String endpointBeanName = generateBeanName(beanName, method, annotationType); - endpoint.setBeanName(endpointBeanName); - getBeanDefinitionRegistry() - .registerBeanDefinition(endpointBeanName, - new RootBeanDefinition((Class) endpoint.getClass(), () -> endpoint)); - this.beanFactory.getBean(endpointBeanName); - } - } - - protected String generateBeanName(String originalBeanName, Method method, - Class annotationType) { - - String name = MessagingAnnotationUtils.endpointIdValue(method); - if (!StringUtils.hasText(name)) { - String baseName = originalBeanName + "." + method.getName() + "." - + ClassUtils.getShortNameAsProperty(annotationType); - name = baseName; - int count = 1; - while (this.beanFactory.containsBean(name)) { - name = baseName + "#" + (++count); - } - } - return name; - } - protected Map, MethodAnnotationPostProcessor> getPostProcessors() { return this.postProcessors; } - protected record MessagingMetaAnnotation(Annotation annotation, Class annotationType) { - - } - } diff --git a/spring-integration-core/src/test/java/org/springframework/integration/bus/DirectChannelSubscriptionTests.java b/spring-integration-core/src/test/java/org/springframework/integration/bus/DirectChannelSubscriptionTests.java index 17708cb9708..d037d92733f 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/bus/DirectChannelSubscriptionTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/bus/DirectChannelSubscriptionTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,7 +20,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.integration.annotation.MessageEndpoint; import org.springframework.integration.annotation.ServiceActivator; import org.springframework.integration.channel.DirectChannel; @@ -55,6 +54,7 @@ public class DirectChannelSubscriptionTests { @BeforeEach public void setupChannels() { + this.context.registerBean(MessagingAnnotationPostProcessor.class); this.context.registerChannel("sourceChannel", this.sourceChannel); this.context.registerChannel("targetChannel", this.targetChannel); } @@ -80,13 +80,9 @@ public void sendAndReceiveForRegisteredEndpoint() { @Test public void sendAndReceiveForAnnotatedEndpoint() { - MessagingAnnotationPostProcessor postProcessor = new MessagingAnnotationPostProcessor(); - postProcessor.postProcessBeanDefinitionRegistry((BeanDefinitionRegistry) this.context.getBeanFactory()); - postProcessor.postProcessBeanFactory(this.context.getBeanFactory()); - postProcessor.afterSingletonsInstantiated(); - TestEndpoint endpoint = new TestEndpoint(); - postProcessor.postProcessAfterInitialization(endpoint, "testEndpoint"); + this.context.registerEndpoint("testEndpoint", new TestEndpoint()); this.context.refresh(); + this.sourceChannel.send(new GenericMessage<>("foo")); Message response = this.targetChannel.receive(); assertThat(response.getPayload()).isEqualTo("foo-from-annotated-endpoint"); @@ -113,11 +109,7 @@ public Object handleRequestMessage(Message message) { public void exceptionThrownFromAnnotatedEndpoint() { QueueChannel errorChannel = new QueueChannel(); this.context.registerChannel(IntegrationContextUtils.ERROR_CHANNEL_BEAN_NAME, errorChannel); - MessagingAnnotationPostProcessor postProcessor = new MessagingAnnotationPostProcessor(); - postProcessor.postProcessBeanFactory(this.context.getBeanFactory()); - postProcessor.afterSingletonsInstantiated(); - FailingTestEndpoint endpoint = new FailingTestEndpoint(); - postProcessor.postProcessAfterInitialization(endpoint, "testEndpoint"); + this.context.registerEndpoint("testEndpoint", new FailingTestEndpoint()); this.context.refresh(); assertThatExceptionOfType(MessagingException.class) .isThrownBy(() -> this.sourceChannel.send(new GenericMessage<>("foo"))); diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/annotation/FilterAnnotationPostProcessorTests.java b/spring-integration-core/src/test/java/org/springframework/integration/config/annotation/FilterAnnotationPostProcessorTests.java index e894d5026ee..76047981a9f 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/annotation/FilterAnnotationPostProcessorTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/annotation/FilterAnnotationPostProcessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,11 +23,11 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.integration.annotation.Filter; import org.springframework.integration.annotation.MessageEndpoint; import org.springframework.integration.channel.DirectChannel; import org.springframework.integration.channel.QueueChannel; +import org.springframework.integration.config.MessagingAnnotationBeanPostProcessor; import org.springframework.integration.config.MessagingAnnotationPostProcessor; import org.springframework.integration.endpoint.EventDrivenConsumer; import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice; @@ -50,19 +50,15 @@ public class FilterAnnotationPostProcessorTests { private final TestApplicationContext context = TestUtils.createTestApplicationContext(); - private final MessagingAnnotationPostProcessor postProcessor = new MessagingAnnotationPostProcessor(); - private final DirectChannel inputChannel = new DirectChannel(); private final QueueChannel outputChannel = new QueueChannel(); @BeforeEach public void init() { + this.context.registerBean(MessagingAnnotationPostProcessor.class); this.context.registerChannel("input", this.inputChannel); this.context.registerChannel("output", this.outputChannel); - this.postProcessor.postProcessBeanDefinitionRegistry((BeanDefinitionRegistry) this.context.getBeanFactory()); - this.postProcessor.postProcessBeanFactory(this.context.getBeanFactory()); - this.postProcessor.afterSingletonsInstantiated(); } @AfterEach @@ -175,21 +171,26 @@ public void filterAnnotationWithBooleanWrapperClass() { @Test public void invalidMethodWithStringReturnType() { - Object filter = new TestFilterWithStringReturnType(); + context.refresh(); + var postProcessor = context.getBean(MessagingAnnotationBeanPostProcessor.class); assertThatIllegalArgumentException() - .isThrownBy(() -> this.postProcessor.postProcessAfterInitialization(filter, "testFilter")); + .isThrownBy(() -> + postProcessor.postProcessAfterInitialization( + new TestFilterWithStringReturnType(), "testFilter")); } @Test public void invalidMethodWithVoidReturnType() { - Object filter = new TestFilterWithVoidReturnType(); + context.refresh(); + var postProcessor = context.getBean(MessagingAnnotationBeanPostProcessor.class); assertThatIllegalArgumentException() - .isThrownBy(() -> postProcessor.postProcessAfterInitialization(filter, "testFilter")); + .isThrownBy(() -> + postProcessor.postProcessAfterInitialization(new TestFilterWithVoidReturnType(), "testFilter")); } private void testValidFilter(Object filter) { - postProcessor.postProcessAfterInitialization(filter, "testFilter"); + context.registerEndpoint("testFilter", filter); context.refresh(); inputChannel.send(new GenericMessage<>("good")); Message passed = outputChannel.receive(0); diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/annotation/MessagingAnnotationPostProcessorTests.java b/spring-integration-core/src/test/java/org/springframework/integration/config/annotation/MessagingAnnotationPostProcessorTests.java index b12505c12e0..be954f5c38c 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/annotation/MessagingAnnotationPostProcessorTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/annotation/MessagingAnnotationPostProcessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -27,8 +27,6 @@ import org.junit.jupiter.api.Test; import org.springframework.aop.framework.ProxyFactory; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; -import org.springframework.context.ConfigurableApplicationContext; import org.springframework.context.support.AbstractApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext; import org.springframework.integration.annotation.MessageEndpoint; @@ -36,6 +34,7 @@ import org.springframework.integration.annotation.Transformer; import org.springframework.integration.channel.DirectChannel; import org.springframework.integration.channel.QueueChannel; +import org.springframework.integration.config.MessagingAnnotationBeanPostProcessor; import org.springframework.integration.config.MessagingAnnotationPostProcessor; import org.springframework.integration.endpoint.AbstractEndpoint; import org.springframework.integration.handler.advice.AbstractRequestHandlerAdvice; @@ -63,9 +62,10 @@ public void serviceActivatorAnnotation() { TestApplicationContext context = TestUtils.createTestApplicationContext(); DirectChannel inputChannel = new DirectChannel(); context.registerChannel("inputChannel", inputChannel); + context.registerBean(MessagingAnnotationPostProcessor.class); context.refresh(); - MessagingAnnotationPostProcessor postProcessor = prepareMessagingAnnotationPostProcessor(context); + MessagingAnnotationBeanPostProcessor postProcessor = context.getBean(MessagingAnnotationBeanPostProcessor.class); ServiceActivatorAnnotatedBean bean = new ServiceActivatorAnnotatedBean(); postProcessor.postProcessAfterInitialization(bean, "testBean"); assertThat(context.containsBean("testBean.test.serviceActivator")).isTrue(); @@ -135,11 +135,11 @@ public void typeConvertingHandler() { @Test public void outboundOnlyServiceActivator() throws InterruptedException { TestApplicationContext context = TestUtils.createTestApplicationContext(); + context.registerBean(MessagingAnnotationPostProcessor.class); context.registerChannel("testChannel", new DirectChannel()); - MessagingAnnotationPostProcessor postProcessor = prepareMessagingAnnotationPostProcessor(context); CountDownLatch latch = new CountDownLatch(1); OutboundOnlyTestBean testBean = new OutboundOnlyTestBean(latch); - postProcessor.postProcessAfterInitialization(testBean, "testBean"); + context.registerEndpoint("testBean", testBean); context.refresh(); DestinationResolver channelResolver = new BeanFactoryChannelResolver(context); MessageChannel testChannel = channelResolver.resolveDestination("testChannel"); @@ -153,15 +153,15 @@ public void outboundOnlyServiceActivator() throws InterruptedException { @Test public void testChannelResolution() { TestApplicationContext context = TestUtils.createTestApplicationContext(); + context.registerBean(MessagingAnnotationPostProcessor.class); DirectChannel inputChannel = new DirectChannel(); QueueChannel outputChannel = new QueueChannel(); DirectChannel eventBus = new DirectChannel(); context.registerChannel("inputChannel", inputChannel); context.registerChannel("outputChannel", outputChannel); context.registerChannel("eventBus", eventBus); - MessagingAnnotationPostProcessor postProcessor = prepareMessagingAnnotationPostProcessor(context); ServiceActivatorAnnotatedBean bean = new ServiceActivatorAnnotatedBean(); - postProcessor.postProcessAfterInitialization(bean, "testBean"); + context.registerEndpoint("testBean", bean); context.refresh(); Message message = MessageBuilder.withPayload("test") .setReplyChannelName("outputChannel").build(); @@ -178,14 +178,14 @@ public void testChannelResolution() { @Test public void testProxiedMessageEndpointAnnotation() { TestApplicationContext context = TestUtils.createTestApplicationContext(); + context.registerBean(MessagingAnnotationPostProcessor.class); DirectChannel inputChannel = new DirectChannel(); QueueChannel outputChannel = new QueueChannel(); context.registerChannel("inputChannel", inputChannel); context.registerChannel("outputChannel", outputChannel); - MessagingAnnotationPostProcessor postProcessor = prepareMessagingAnnotationPostProcessor(context); ProxyFactory proxyFactory = new ProxyFactory(new AnnotatedTestService()); Object proxy = proxyFactory.getProxy(); - postProcessor.postProcessAfterInitialization(proxy, "proxy"); + context.registerEndpoint("proxy", proxy); context.refresh(); inputChannel.send(new GenericMessage<>("world")); Message message = outputChannel.receive(1000); @@ -196,12 +196,12 @@ public void testProxiedMessageEndpointAnnotation() { @Test public void testMessageEndpointAnnotationInherited() { TestApplicationContext context = TestUtils.createTestApplicationContext(); + context.registerBean(MessagingAnnotationPostProcessor.class); DirectChannel inputChannel = new DirectChannel(); QueueChannel outputChannel = new QueueChannel(); context.registerChannel("inputChannel", inputChannel); context.registerChannel("outputChannel", outputChannel); - MessagingAnnotationPostProcessor postProcessor = prepareMessagingAnnotationPostProcessor(context); - postProcessor.postProcessAfterInitialization(new SimpleAnnotatedEndpointSubclass(), "subclass"); + context.registerEndpoint("subclass", new SimpleAnnotatedEndpointSubclass()); context.refresh(); inputChannel.send(new GenericMessage<>("world")); Message message = outputChannel.receive(1000); @@ -212,14 +212,14 @@ public void testMessageEndpointAnnotationInherited() { @Test public void testMessageEndpointAnnotationInheritedWithProxy() { TestApplicationContext context = TestUtils.createTestApplicationContext(); + context.registerBean(MessagingAnnotationPostProcessor.class); DirectChannel inputChannel = new DirectChannel(); QueueChannel outputChannel = new QueueChannel(); context.registerChannel("inputChannel", inputChannel); context.registerChannel("outputChannel", outputChannel); - MessagingAnnotationPostProcessor postProcessor = prepareMessagingAnnotationPostProcessor(context); ProxyFactory proxyFactory = new ProxyFactory(new SimpleAnnotatedEndpointSubclass()); Object proxy = proxyFactory.getProxy(); - postProcessor.postProcessAfterInitialization(proxy, "proxy"); + context.registerEndpoint("proxy", proxy); context.refresh(); inputChannel.send(new GenericMessage<>("world")); Message message = outputChannel.receive(1000); @@ -230,12 +230,12 @@ public void testMessageEndpointAnnotationInheritedWithProxy() { @Test public void testMessageEndpointAnnotationInheritedFromInterface() { TestApplicationContext context = TestUtils.createTestApplicationContext(); + context.registerBean(MessagingAnnotationPostProcessor.class); DirectChannel inputChannel = new DirectChannel(); QueueChannel outputChannel = new QueueChannel(); context.registerChannel("inputChannel", inputChannel); context.registerChannel("outputChannel", outputChannel); - MessagingAnnotationPostProcessor postProcessor = prepareMessagingAnnotationPostProcessor(context); - postProcessor.postProcessAfterInitialization(new SimpleAnnotatedEndpointImplementation(), "impl"); + context.registerEndpoint("impl", new SimpleAnnotatedEndpointImplementation()); context.refresh(); inputChannel.send(new GenericMessage<>("ABC")); Message message = outputChannel.receive(1000); @@ -246,12 +246,12 @@ public void testMessageEndpointAnnotationInheritedFromInterface() { @Test public void testMessageEndpointAnnotationInheritedFromInterfaceWithAutoCreatedChannels() { TestApplicationContext context = TestUtils.createTestApplicationContext(); + context.registerBean(MessagingAnnotationPostProcessor.class); DirectChannel inputChannel = new DirectChannel(); QueueChannel outputChannel = new QueueChannel(); context.registerChannel("inputChannel", inputChannel); context.registerChannel("outputChannel", outputChannel); - MessagingAnnotationPostProcessor postProcessor = prepareMessagingAnnotationPostProcessor(context); - postProcessor.postProcessAfterInitialization(new SimpleAnnotatedEndpointImplementation(), "impl"); + context.registerEndpoint("impl", new SimpleAnnotatedEndpointImplementation()); context.refresh(); inputChannel.send(new GenericMessage<>("ABC")); Message message = outputChannel.receive(1000); @@ -262,14 +262,13 @@ public void testMessageEndpointAnnotationInheritedFromInterfaceWithAutoCreatedCh @Test public void testMessageEndpointAnnotationInheritedFromInterfaceWithProxy() { TestApplicationContext context = TestUtils.createTestApplicationContext(); + context.registerBean(MessagingAnnotationPostProcessor.class); DirectChannel inputChannel = new DirectChannel(); QueueChannel outputChannel = new QueueChannel(); context.registerChannel("inputChannel", inputChannel); context.registerChannel("outputChannel", outputChannel); - MessagingAnnotationPostProcessor postProcessor = prepareMessagingAnnotationPostProcessor(context); ProxyFactory proxyFactory = new ProxyFactory(new SimpleAnnotatedEndpointImplementation()); - Object proxy = proxyFactory.getProxy(); - postProcessor.postProcessAfterInitialization(proxy, "proxy"); + context.registerEndpoint("proxy", proxyFactory.getProxy()); context.refresh(); inputChannel.send(new GenericMessage<>("ABC")); Message message = outputChannel.receive(1000); @@ -280,13 +279,12 @@ public void testMessageEndpointAnnotationInheritedFromInterfaceWithProxy() { @Test public void testTransformer() { TestApplicationContext context = TestUtils.createTestApplicationContext(); + context.registerBean(MessagingAnnotationPostProcessor.class); DirectChannel inputChannel = new DirectChannel(); context.registerChannel("inputChannel", inputChannel); QueueChannel outputChannel = new QueueChannel(); context.registerChannel("outputChannel", outputChannel); - MessagingAnnotationPostProcessor postProcessor = prepareMessagingAnnotationPostProcessor(context); - TransformerAnnotationTestBean testBean = new TransformerAnnotationTestBean(); - postProcessor.postProcessAfterInitialization(testBean, "testBean"); + context.registerEndpoint("testBean", new TransformerAnnotationTestBean()); context.refresh(); inputChannel.send(new GenericMessage<>("foo")); Message reply = outputChannel.receive(0); @@ -300,16 +298,6 @@ public void testTransformer() { context.close(); } - private static MessagingAnnotationPostProcessor prepareMessagingAnnotationPostProcessor( - ConfigurableApplicationContext context) { - - MessagingAnnotationPostProcessor postProcessor = new MessagingAnnotationPostProcessor(); - postProcessor.postProcessBeanDefinitionRegistry((BeanDefinitionRegistry) context.getBeanFactory()); - postProcessor.postProcessBeanFactory(context.getBeanFactory()); - postProcessor.afterSingletonsInstantiated(); - return postProcessor; - } - @MessageEndpoint public static class OutboundOnlyTestBean { diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/annotation/RouterAnnotationPostProcessorTests.java b/spring-integration-core/src/test/java/org/springframework/integration/config/annotation/RouterAnnotationPostProcessorTests.java index 9baae7bc94c..9fe68682c1d 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/annotation/RouterAnnotationPostProcessorTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/annotation/RouterAnnotationPostProcessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -23,7 +23,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.integration.annotation.MessageEndpoint; import org.springframework.integration.annotation.Router; import org.springframework.integration.channel.DirectChannel; @@ -44,8 +43,6 @@ public class RouterAnnotationPostProcessorTests { private final TestApplicationContext context = TestUtils.createTestApplicationContext(); - private final MessagingAnnotationPostProcessor postProcessor = new MessagingAnnotationPostProcessor(); - private final DirectChannel inputChannel = new DirectChannel(); private final QueueChannel outputChannel = new QueueChannel(); @@ -59,14 +56,12 @@ public class RouterAnnotationPostProcessorTests { @BeforeEach public void init() { + this.context.registerBean(MessagingAnnotationPostProcessor.class); context.registerChannel("input", inputChannel); context.registerChannel("output", outputChannel); context.registerChannel("routingChannel", routingChannel); context.registerChannel("integerChannel", integerChannel); context.registerChannel("stringChannel", stringChannel); - this.postProcessor.postProcessBeanDefinitionRegistry((BeanDefinitionRegistry) this.context.getBeanFactory()); - this.postProcessor.postProcessBeanFactory(this.context.getBeanFactory()); - this.postProcessor.afterSingletonsInstantiated(); } @AfterEach @@ -76,8 +71,7 @@ public void tearDown() { @Test public void testRouter() { - TestRouter testRouter = new TestRouter(); - postProcessor.postProcessAfterInitialization(testRouter, "test"); + context.registerEndpoint("test", new TestRouter()); context.refresh(); inputChannel.send(new GenericMessage<>("foo")); Message replyMessage = outputChannel.receive(0); @@ -87,8 +81,7 @@ public void testRouter() { @Test public void testRouterWithListParam() { - TestRouter testRouter = new TestRouter(); - postProcessor.postProcessAfterInitialization(testRouter, "test"); + context.registerEndpoint("test", new TestRouter()); context.refresh(); routingChannel.send(new GenericMessage<>(Collections.singletonList("foo"))); diff --git a/spring-integration-core/src/test/java/org/springframework/integration/config/annotation/SplitterAnnotationPostProcessorTests.java b/spring-integration-core/src/test/java/org/springframework/integration/config/annotation/SplitterAnnotationPostProcessorTests.java index abe52ccb351..bcdd6a7ef8a 100644 --- a/spring-integration-core/src/test/java/org/springframework/integration/config/annotation/SplitterAnnotationPostProcessorTests.java +++ b/spring-integration-core/src/test/java/org/springframework/integration/config/annotation/SplitterAnnotationPostProcessorTests.java @@ -1,5 +1,5 @@ /* - * Copyright 2002-2022 the original author or authors. + * Copyright 2002-2023 the original author or authors. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -20,7 +20,6 @@ import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.Test; -import org.springframework.beans.factory.support.BeanDefinitionRegistry; import org.springframework.context.Lifecycle; import org.springframework.integration.annotation.MessageEndpoint; import org.springframework.integration.annotation.Splitter; @@ -50,6 +49,7 @@ public class SplitterAnnotationPostProcessorTests { @BeforeEach public void init() { + this.context.registerBean(MessagingAnnotationPostProcessor.class); this.context.registerChannel("input", this.inputChannel); this.context.registerChannel("output", this.outputChannel); } @@ -61,12 +61,8 @@ public void tearDown() { @Test public void testSplitterAnnotation() { - MessagingAnnotationPostProcessor postProcessor = new MessagingAnnotationPostProcessor(); - postProcessor.postProcessBeanDefinitionRegistry((BeanDefinitionRegistry) this.context.getBeanFactory()); - postProcessor.postProcessBeanFactory(this.context.getBeanFactory()); - postProcessor.afterSingletonsInstantiated(); TestSplitter splitter = new TestSplitter(); - postProcessor.postProcessAfterInitialization(splitter, "testSplitter"); + context.registerEndpoint("testSplitter", splitter); context.refresh(); inputChannel.send(new GenericMessage<>("this.is.a.test")); Message message1 = outputChannel.receive(500);