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:
Mark Fisher
2008-10-05 18:16:31 +00:00
parent 5c16131ce1
commit 88df7711e0
7 changed files with 145 additions and 63 deletions

View File

@@ -284,6 +284,8 @@ public class DefaultMessageBus implements MessageBus, ApplicationContextAware, A
}
}
// Lifecycle implementation
public boolean isRunning() {
synchronized (this.lifecycleMonitor) {
return this.running;

View File

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

View File

@@ -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) {

View File

@@ -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()]);
}
}