MessagingAnnotationPostProcessor no longer creates a proxy for annotated methods. It still creates and registers an endpoint if an "inputChannel" is specified, but for satisfying a "ref" in an XML-based endpoint configuration, it will be the responsibility of the corresponding consumer to create a Method-invoking adapter instance for the annotated Method. The ServiceActivator performs this role already, checking for a single @ServiceActivaotor-annotated method or falling back to a single public Method that is not declared on the Object class.
This commit is contained in:
@@ -284,6 +284,8 @@ public class DefaultMessageBus implements MessageBus, ApplicationContextAware, A
|
||||
}
|
||||
}
|
||||
|
||||
// Lifecycle implementation
|
||||
|
||||
public boolean isRunning() {
|
||||
synchronized (this.lifecycleMonitor) {
|
||||
return this.running;
|
||||
|
||||
@@ -23,13 +23,9 @@ import java.util.Arrays;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.concurrent.atomic.AtomicBoolean;
|
||||
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.aop.support.AopUtils;
|
||||
import org.springframework.aop.support.DelegatingIntroductionInterceptor;
|
||||
import org.springframework.beans.BeansException;
|
||||
import org.springframework.beans.factory.BeanClassLoaderAware;
|
||||
import org.springframework.beans.factory.BeanFactory;
|
||||
import org.springframework.beans.factory.BeanFactoryAware;
|
||||
import org.springframework.beans.factory.BeanNameAware;
|
||||
@@ -58,13 +54,12 @@ import org.springframework.util.ReflectionUtils;
|
||||
* @author Mark Fisher
|
||||
* @author Marius Bogoevici
|
||||
*/
|
||||
public class MessagingAnnotationPostProcessor implements BeanPostProcessor, BeanFactoryAware, InitializingBean, BeanClassLoaderAware {
|
||||
public class MessagingAnnotationPostProcessor implements BeanPostProcessor, BeanFactoryAware, InitializingBean {
|
||||
|
||||
private final MessageBus messageBus;
|
||||
|
||||
private volatile ConfigurableBeanFactory beanFactory;
|
||||
|
||||
private volatile ClassLoader beanClassLoader = ClassUtils.getDefaultClassLoader();
|
||||
|
||||
private final Map<Class<? extends Annotation>, MethodAnnotationPostProcessor<?>> postProcessors =
|
||||
new HashMap<Class<? extends Annotation>, MethodAnnotationPostProcessor<?>>();
|
||||
@@ -82,10 +77,6 @@ public class MessagingAnnotationPostProcessor implements BeanPostProcessor, Bean
|
||||
this.beanFactory = (ConfigurableBeanFactory) beanFactory;
|
||||
}
|
||||
|
||||
public void setBeanClassLoader(ClassLoader beanClassLoader) {
|
||||
this.beanClassLoader = beanClassLoader;
|
||||
}
|
||||
|
||||
public void afterPropertiesSet() {
|
||||
Assert.notNull(this.beanFactory, "BeanFactory must not be null");
|
||||
postProcessors.put(Aggregator.class, new AggregatorAnnotationPostProcessor(this.messageBus));
|
||||
@@ -100,16 +91,13 @@ public class MessagingAnnotationPostProcessor implements BeanPostProcessor, Bean
|
||||
return bean;
|
||||
}
|
||||
|
||||
public Object postProcessAfterInitialization(Object bean, final String beanName) throws BeansException {
|
||||
public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
|
||||
Assert.notNull(this.beanFactory, "BeanFactory must not be null");
|
||||
final Object originalBean = bean;
|
||||
final Class<?> beanClass = this.getBeanClass(bean);
|
||||
if (!this.isStereotype(beanClass)) {
|
||||
// we only post-process stereotype components
|
||||
return bean;
|
||||
}
|
||||
final ProxyFactory proxyFactory = new ProxyFactory(bean);
|
||||
final AtomicBoolean isProxy = new AtomicBoolean(false);
|
||||
ReflectionUtils.doWithMethods(beanClass, new ReflectionUtils.MethodCallback() {
|
||||
@SuppressWarnings("unchecked")
|
||||
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
|
||||
@@ -117,35 +105,13 @@ public class MessagingAnnotationPostProcessor implements BeanPostProcessor, Bean
|
||||
for (Annotation annotation : annotations) {
|
||||
MethodAnnotationPostProcessor postProcessor = postProcessors.get(annotation.annotationType());
|
||||
if (postProcessor != null) {
|
||||
Object result = postProcessor.postProcess(originalBean, beanName, method, annotation);
|
||||
if (result != null) {
|
||||
if (result instanceof MessageEndpoint) {
|
||||
String endpointBeanName = generateBeanName(beanName, method, annotation.annotationType());
|
||||
if (result instanceof BeanNameAware) {
|
||||
((BeanNameAware) result).setBeanName(endpointBeanName);
|
||||
}
|
||||
beanFactory.registerSingleton(endpointBeanName, result);
|
||||
}
|
||||
else {
|
||||
boolean shouldProxy = false;
|
||||
Class<?>[] interfaces = ClassUtils.getAllInterfaces(result);
|
||||
for (Class<?> iface : interfaces) {
|
||||
if (!iface.getPackage().getName().startsWith("org.springframework.integration")) {
|
||||
continue;
|
||||
}
|
||||
if (proxyFactory.isInterfaceProxied(iface)) {
|
||||
throw new IllegalStateException("interface [" + iface + "] is already proxied");
|
||||
}
|
||||
shouldProxy = true;
|
||||
}
|
||||
if (result instanceof ChannelRegistryAware) {
|
||||
((ChannelRegistryAware) result).setChannelRegistry(messageBus);
|
||||
}
|
||||
if (shouldProxy) {
|
||||
proxyFactory.addAdvice(new DelegatingIntroductionInterceptor(result));
|
||||
isProxy.set(true);
|
||||
}
|
||||
Object result = postProcessor.postProcess(bean, beanName, method, annotation);
|
||||
if (result != null && result instanceof MessageEndpoint) {
|
||||
String endpointBeanName = generateBeanName(beanName, method, annotation.annotationType());
|
||||
if (result instanceof BeanNameAware) {
|
||||
((BeanNameAware) result).setBeanName(endpointBeanName);
|
||||
}
|
||||
beanFactory.registerSingleton(endpointBeanName, result);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -154,9 +120,6 @@ public class MessagingAnnotationPostProcessor implements BeanPostProcessor, Bean
|
||||
if (bean instanceof ChannelRegistryAware) {
|
||||
((ChannelRegistryAware) bean).setChannelRegistry(messageBus);
|
||||
}
|
||||
if (isProxy.get()) {
|
||||
return proxyFactory.getProxy(this.beanClassLoader);
|
||||
}
|
||||
return bean;
|
||||
}
|
||||
|
||||
|
||||
@@ -16,10 +16,14 @@
|
||||
|
||||
package org.springframework.integration.endpoint;
|
||||
|
||||
import java.lang.reflect.Method;
|
||||
|
||||
import org.springframework.beans.factory.InitializingBean;
|
||||
import org.springframework.integration.annotation.ServiceActivator;
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.integration.message.MessageHandlingException;
|
||||
import org.springframework.integration.message.MessageMappingMethodInvoker;
|
||||
import org.springframework.integration.util.MethodUtils;
|
||||
import org.springframework.integration.util.MethodInvoker;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
@@ -39,8 +43,18 @@ public class ServiceActivatorEndpoint extends AbstractMessageHandlingEndpoint {
|
||||
this.invoker = invoker;
|
||||
}
|
||||
|
||||
public ServiceActivatorEndpoint(Object object) {
|
||||
this(object, DEFAULT_LISTENER_METHOD);
|
||||
public ServiceActivatorEndpoint(final Object object) {
|
||||
Assert.notNull(object, "object must not be null");
|
||||
Method method = MethodUtils.findMethodWithAnnotation(object.getClass(), ServiceActivator.class);
|
||||
if (method == null) {
|
||||
Method[] methods = MethodUtils.findPublicMethods(object.getClass(), false);
|
||||
if (methods.length == 1) {
|
||||
method = methods[0];
|
||||
}
|
||||
}
|
||||
Assert.notNull(method, "unable to resolve ServiceActivator method on target class ["
|
||||
+ object.getClass() + "]");
|
||||
this.invoker = new MessageMappingMethodInvoker(object, method);
|
||||
}
|
||||
|
||||
public ServiceActivatorEndpoint(Object object, String methodName) {
|
||||
|
||||
@@ -0,0 +1,86 @@
|
||||
/*
|
||||
* Copyright 2002-2008 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
|
||||
*
|
||||
* http://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.util;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Method;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.atomic.AtomicReference;
|
||||
|
||||
import org.springframework.core.annotation.AnnotationUtils;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ReflectionUtils;
|
||||
|
||||
/**
|
||||
* Helper methods for detecting Methods.
|
||||
*
|
||||
* @author Mark Fisher
|
||||
*/
|
||||
public abstract class MethodUtils {
|
||||
|
||||
/**
|
||||
* Find a <em>single</em> Method on the given Class that contains the
|
||||
* specified annotation type.
|
||||
*
|
||||
* @param clazz the Class instance to check for the annotation
|
||||
* @param annotationType the Method-level annotation type
|
||||
*
|
||||
* @return a single matching Method instance or <code>null</code> if the
|
||||
* Class contains no Methods with the specified annotation
|
||||
*
|
||||
* @throws IllegalArgumentException if more than one Method has the
|
||||
* specified annotation
|
||||
*/
|
||||
public static <T extends Annotation> Method findMethodWithAnnotation(
|
||||
final Class<?> clazz, final Class<T> annotationType) {
|
||||
final AtomicReference<Method> annotatedMethod = new AtomicReference<Method>();
|
||||
ReflectionUtils.doWithMethods(clazz, new ReflectionUtils.MethodCallback() {
|
||||
public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException {
|
||||
T annotation = AnnotationUtils.findAnnotation(method, annotationType);
|
||||
if (annotation != null) {
|
||||
Assert.isNull(annotatedMethod.get(), "found more than one method on target class ["
|
||||
+ clazz + "] with the annotation type [" + annotationType + "]");
|
||||
annotatedMethod.set(method);
|
||||
}
|
||||
}
|
||||
});
|
||||
return annotatedMethod.get();
|
||||
}
|
||||
|
||||
/**
|
||||
* Find all public Methods of a given Class.
|
||||
*
|
||||
* @param clazz the class to search
|
||||
* @param includeMethodsDeclaredOnObject whether to include Methods
|
||||
* that are declared on the Object class
|
||||
*
|
||||
* @return array of public Methods
|
||||
*/
|
||||
public static Method[] findPublicMethods(
|
||||
final Class<?> clazz, final boolean includeMethodsDeclaredOnObject) {
|
||||
final List<Method> methods = new ArrayList<Method>();
|
||||
for (Method method : clazz.getMethods()) {
|
||||
if (includeMethodsDeclaredOnObject
|
||||
|| !method.getDeclaringClass().equals(Object.class)) {
|
||||
methods.add(method);
|
||||
}
|
||||
}
|
||||
return methods.toArray(new Method[methods.size()]);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,6 +18,7 @@ package org.springframework.integration.config;
|
||||
|
||||
import java.util.concurrent.CountDownLatch;
|
||||
|
||||
import org.springframework.integration.annotation.ServiceActivator;
|
||||
import org.springframework.integration.message.Message;
|
||||
import org.springframework.integration.message.StringMessage;
|
||||
|
||||
@@ -46,6 +47,7 @@ public class TestHandler {
|
||||
this.replyMessageText = replyMessageText;
|
||||
}
|
||||
|
||||
@ServiceActivator
|
||||
public Message<?> handle(Message<?> message) {
|
||||
this.messageString = message.getPayload().toString();
|
||||
this.latch.countDown();
|
||||
|
||||
@@ -44,6 +44,7 @@ import org.springframework.integration.channel.DirectChannel;
|
||||
import org.springframework.integration.channel.MessageChannel;
|
||||
import org.springframework.integration.channel.PollableChannel;
|
||||
import org.springframework.integration.channel.QueueChannel;
|
||||
import org.springframework.integration.config.MessageBusParser;
|
||||
import org.springframework.integration.endpoint.ChannelPoller;
|
||||
import org.springframework.integration.endpoint.ServiceActivatorEndpoint;
|
||||
import org.springframework.integration.message.Message;
|
||||
@@ -51,7 +52,6 @@ import org.springframework.integration.message.MessageConsumer;
|
||||
import org.springframework.integration.message.StringMessage;
|
||||
import org.springframework.integration.scheduling.IntervalTrigger;
|
||||
import org.springframework.integration.scheduling.Trigger;
|
||||
import org.springframework.integration.util.MethodInvoker;
|
||||
|
||||
/**
|
||||
* @author Mark Fisher
|
||||
@@ -59,25 +59,32 @@ import org.springframework.integration.util.MethodInvoker;
|
||||
public class MessagingAnnotationPostProcessorTests {
|
||||
|
||||
@Test
|
||||
public void testHandlerAnnotation() {
|
||||
public void testServiceActivatorAnnotation() {
|
||||
GenericApplicationContext context = new GenericApplicationContext();
|
||||
DefaultMessageBus messageBus = new DefaultMessageBus();
|
||||
messageBus.setApplicationContext(context);
|
||||
QueueChannel inputChannel = new QueueChannel();
|
||||
inputChannel.setBeanName("inputChannel");
|
||||
messageBus.registerChannel(inputChannel);
|
||||
MessagingAnnotationPostProcessor postProcessor = new MessagingAnnotationPostProcessor(messageBus);
|
||||
postProcessor.setBeanFactory(context.getBeanFactory());
|
||||
postProcessor.afterPropertiesSet();
|
||||
HandlerAnnotatedBean bean = new HandlerAnnotatedBean();
|
||||
Object result = postProcessor.postProcessAfterInitialization(bean, "testBean");
|
||||
assertTrue(result instanceof MethodInvoker);
|
||||
ServiceActivatorAnnotatedBean bean = new ServiceActivatorAnnotatedBean();
|
||||
postProcessor.postProcessAfterInitialization(bean, "testBean");
|
||||
assertTrue(context.containsBean("testBean.test.serviceActivator"));
|
||||
Object endpoint = context.getBean("testBean.test.serviceActivator");
|
||||
assertTrue(endpoint instanceof org.springframework.integration.endpoint.MessageEndpoint);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSimpleHandlerWithContext() throws Exception {
|
||||
public void testServiceActivatorWithContext() throws Exception {
|
||||
AbstractApplicationContext context = new ClassPathXmlApplicationContext(
|
||||
"serviceActivatorAnnotationPostProcessorTests.xml", this.getClass());
|
||||
MethodInvoker invoker = (MethodInvoker) context.getBean("testBean");
|
||||
String reply = (String) invoker.invokeMethod(new StringMessage("world"));
|
||||
assertEquals("hello world", reply);
|
||||
MessageChannel inputChannel = (MessageChannel) context.getBean("inputChannel");
|
||||
PollableChannel outputChannel = (PollableChannel) context.getBean("outputChannel");
|
||||
inputChannel.send(new StringMessage("world"));
|
||||
Message<?> reply = outputChannel.receive(0);
|
||||
assertEquals("hello world", reply.getPayload());
|
||||
context.stop();
|
||||
}
|
||||
|
||||
@@ -367,15 +374,23 @@ public class MessagingAnnotationPostProcessorTests {
|
||||
@Test
|
||||
public void testTransformer() {
|
||||
GenericApplicationContext context = new GenericApplicationContext();
|
||||
DirectChannel inputChannel = new DirectChannel();
|
||||
inputChannel.setBeanName("inputChannel");
|
||||
context.getBeanFactory().registerSingleton("inputChannel", inputChannel);
|
||||
QueueChannel outputChannel = new QueueChannel();
|
||||
outputChannel.setBeanName("outputChannel");
|
||||
context.getBeanFactory().registerSingleton("outputChannel", outputChannel);
|
||||
DefaultMessageBus messageBus = new DefaultMessageBus();
|
||||
context.getBeanFactory().registerSingleton(MessageBusParser.MESSAGE_BUS_BEAN_NAME, messageBus);
|
||||
messageBus.setApplicationContext(context);
|
||||
MessagingAnnotationPostProcessor postProcessor = new MessagingAnnotationPostProcessor(messageBus);
|
||||
postProcessor.setBeanFactory(context.getBeanFactory());
|
||||
postProcessor.afterPropertiesSet();
|
||||
TransformerAnnotationTestBean testBean = new TransformerAnnotationTestBean();
|
||||
org.springframework.integration.transformer.Transformer transformer =
|
||||
(org.springframework.integration.transformer.Transformer) postProcessor.postProcessAfterInitialization(testBean, "testBean");
|
||||
Message<?> reply = transformer.transform(new StringMessage("foo"));
|
||||
postProcessor.postProcessAfterInitialization(testBean, "testBean");
|
||||
context.refresh();
|
||||
inputChannel.send(new StringMessage("foo"));
|
||||
Message<?> reply = outputChannel.receive(0);
|
||||
assertEquals("FOO", reply.getPayload());
|
||||
}
|
||||
|
||||
@@ -455,9 +470,9 @@ public class MessagingAnnotationPostProcessorTests {
|
||||
|
||||
|
||||
@MessageEndpoint
|
||||
private static class HandlerAnnotatedBean {
|
||||
private static class ServiceActivatorAnnotatedBean {
|
||||
|
||||
@ServiceActivator
|
||||
@ServiceActivator(inputChannel="inputChannel")
|
||||
public String test(String s) {
|
||||
return s + s;
|
||||
}
|
||||
@@ -479,7 +494,7 @@ public class MessagingAnnotationPostProcessorTests {
|
||||
@MessageEndpoint
|
||||
private static class TransformerAnnotationTestBean {
|
||||
|
||||
@Transformer
|
||||
@Transformer(inputChannel="inputChannel", outputChannel="outputChannel")
|
||||
public String transformBefore(String input) {
|
||||
return input.toUpperCase();
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<channel id="inputChannel"/>
|
||||
|
||||
<channel id="outputChannel">
|
||||
<queue capacity="5"/>
|
||||
<queue capacity="1"/>
|
||||
</channel>
|
||||
|
||||
<service-activator input-channel="inputChannel" ref="testBean" output-channel="outputChannel"/>
|
||||
|
||||
Reference in New Issue
Block a user