All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.annotation.RabbitListenerAnnotationBeanPostProcessor Maven / Gradle / Ivy

The newest version!
/*
 * Copyright 2014-2017 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 io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.annotation;

import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;

import io.bitsensor.plugins.shaded.org.apache.commons.logging.Log;
import io.bitsensor.plugins.shaded.org.apache.commons.logging.LogFactory;

import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.core.AbstractDeclarable;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.core.AbstractExchange;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.core.Binding;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.core.Binding.DestinationType;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.core.DirectExchange;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.core.Exchange;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.core.ExchangeTypes;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.core.FanoutExchange;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.core.HeadersExchange;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.core.Queue;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.core.TopicExchange;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.config.RabbitListenerConfigUtils;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.core.RabbitAdmin;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.listener.MethodRabbitListenerEndpoint;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.listener.MultiMethodRabbitListenerEndpoint;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistrar;
import io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.listener.RabbitListenerEndpointRegistry;
import io.bitsensor.plugins.shaded.org.springframework.aop.framework.Advised;
import io.bitsensor.plugins.shaded.org.springframework.aop.support.AopUtils;
import io.bitsensor.plugins.shaded.org.springframework.beans.BeansException;
import io.bitsensor.plugins.shaded.org.springframework.beans.factory.BeanClassLoaderAware;
import io.bitsensor.plugins.shaded.org.springframework.beans.factory.BeanFactory;
import io.bitsensor.plugins.shaded.org.springframework.beans.factory.BeanFactoryAware;
import io.bitsensor.plugins.shaded.org.springframework.beans.factory.BeanInitializationException;
import io.bitsensor.plugins.shaded.org.springframework.beans.factory.ListableBeanFactory;
import io.bitsensor.plugins.shaded.org.springframework.beans.factory.NoSuchBeanDefinitionException;
import io.bitsensor.plugins.shaded.org.springframework.beans.factory.SmartInitializingSingleton;
import io.bitsensor.plugins.shaded.org.springframework.beans.factory.config.BeanExpressionContext;
import io.bitsensor.plugins.shaded.org.springframework.beans.factory.config.BeanExpressionResolver;
import io.bitsensor.plugins.shaded.org.springframework.beans.factory.config.BeanPostProcessor;
import io.bitsensor.plugins.shaded.org.springframework.beans.factory.config.ConfigurableBeanFactory;
import io.bitsensor.plugins.shaded.org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import io.bitsensor.plugins.shaded.org.springframework.context.EnvironmentAware;
import io.bitsensor.plugins.shaded.org.springframework.context.expression.StandardBeanExpressionResolver;
import io.bitsensor.plugins.shaded.org.springframework.core.Ordered;
import io.bitsensor.plugins.shaded.org.springframework.core.annotation.AnnotationUtils;
import io.bitsensor.plugins.shaded.org.springframework.core.convert.ConversionService;
import io.bitsensor.plugins.shaded.org.springframework.core.convert.support.DefaultConversionService;
import io.bitsensor.plugins.shaded.org.springframework.core.env.Environment;
import io.bitsensor.plugins.shaded.org.springframework.messaging.handler.annotation.support.DefaultMessageHandlerMethodFactory;
import io.bitsensor.plugins.shaded.org.springframework.messaging.handler.annotation.support.MessageHandlerMethodFactory;
import io.bitsensor.plugins.shaded.org.springframework.messaging.handler.invocation.InvocableHandlerMethod;
import io.bitsensor.plugins.shaded.org.springframework.util.Assert;
import io.bitsensor.plugins.shaded.org.springframework.util.ClassUtils;
import io.bitsensor.plugins.shaded.org.springframework.util.ReflectionUtils;
import io.bitsensor.plugins.shaded.org.springframework.util.StringUtils;

/**
 * Bean post-processor that registers methods annotated with {@link RabbitListener}
 * to be invoked by a AMQP message listener container created under the cover
 * by a {@link io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory}
 * according to the parameters of the annotation.
 *
 * 

Annotated methods can use flexible arguments as defined by {@link RabbitListener}. * *

This post-processor is automatically registered by Spring's * {@code } XML element, and also by the {@link EnableRabbit} * annotation. * *

Auto-detect any {@link RabbitListenerConfigurer} instances in the container, * allowing for customization of the registry to be used, the default container * factory or for fine-grained control over endpoints registration. See * {@link EnableRabbit} Javadoc for complete usage details. * * @author Stephane Nicoll * @author Juergen Hoeller * @author Gary Russell * @since 1.4 * @see RabbitListener * @see EnableRabbit * @see RabbitListenerConfigurer * @see RabbitListenerEndpointRegistrar * @see RabbitListenerEndpointRegistry * @see io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.listener.RabbitListenerEndpoint * @see MethodRabbitListenerEndpoint */ public class RabbitListenerAnnotationBeanPostProcessor implements BeanPostProcessor, Ordered, BeanFactoryAware, BeanClassLoaderAware, EnvironmentAware, SmartInitializingSingleton { /** * The bean name of the default {@link io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.listener.RabbitListenerContainerFactory}. */ public static final String DEFAULT_RABBIT_LISTENER_CONTAINER_FACTORY_BEAN_NAME = "rabbitListenerContainerFactory"; public static final String RABBIT_EMPTY_STRING_ARGUMENTS_PROPERTY = "spring.rabbitmq.emptyStringArguments"; private static final ConversionService CONVERSION_SERVICE = new DefaultConversionService(); private final Log logger = LogFactory.getLog(this.getClass()); private final Set emptyStringArguments = new HashSet(); private RabbitListenerEndpointRegistry endpointRegistry; private String containerFactoryBeanName = DEFAULT_RABBIT_LISTENER_CONTAINER_FACTORY_BEAN_NAME; private BeanFactory beanFactory; private ClassLoader beanClassLoader; private final RabbitHandlerMethodFactoryAdapter messageHandlerMethodFactory = new RabbitHandlerMethodFactoryAdapter(); private final RabbitListenerEndpointRegistrar registrar = new RabbitListenerEndpointRegistrar(); private final AtomicInteger counter = new AtomicInteger(); private final ConcurrentMap, TypeMetadata> typeCache = new ConcurrentHashMap, TypeMetadata>(); private BeanExpressionResolver resolver = new StandardBeanExpressionResolver(); private BeanExpressionContext expressionContext; private int increment; @Override public int getOrder() { return LOWEST_PRECEDENCE; } public RabbitListenerAnnotationBeanPostProcessor() { this.emptyStringArguments.add("x-dead-letter-exchange"); } /** * Set the {@link RabbitListenerEndpointRegistry} that will hold the created * endpoint and manage the lifecycle of the related listener container. * @param endpointRegistry the {@link RabbitListenerEndpointRegistry} to set. */ public void setEndpointRegistry(RabbitListenerEndpointRegistry endpointRegistry) { this.endpointRegistry = endpointRegistry; } /** * Set the name of the {@link RabbitListenerContainerFactory} to use by default. *

If none is specified, "rabbitListenerContainerFactory" is assumed to be defined. * @param containerFactoryBeanName the {@link RabbitListenerContainerFactory} bean name. */ public void setContainerFactoryBeanName(String containerFactoryBeanName) { this.containerFactoryBeanName = containerFactoryBeanName; } /** * Set the {@link MessageHandlerMethodFactory} to use to configure the message * listener responsible to serve an endpoint detected by this processor. *

By default, {@link DefaultMessageHandlerMethodFactory} is used and it * can be configured further to support additional method arguments * or to customize conversion and validation support. See * {@link DefaultMessageHandlerMethodFactory} Javadoc for more details. * @param messageHandlerMethodFactory the {@link MessageHandlerMethodFactory} instance. */ public void setMessageHandlerMethodFactory(MessageHandlerMethodFactory messageHandlerMethodFactory) { this.messageHandlerMethodFactory.setMessageHandlerMethodFactory(messageHandlerMethodFactory); } /** * Making a {@link BeanFactory} available is optional; if not set, * {@link RabbitListenerConfigurer} beans won't get autodetected and an * {@link #setEndpointRegistry endpoint registry} has to be explicitly configured. * @param beanFactory the {@link BeanFactory} to be used. */ @Override public void setBeanFactory(BeanFactory beanFactory) { this.beanFactory = beanFactory; if (beanFactory instanceof ConfigurableListableBeanFactory) { this.resolver = ((ConfigurableListableBeanFactory) beanFactory).getBeanExpressionResolver(); this.expressionContext = new BeanExpressionContext((ConfigurableListableBeanFactory) beanFactory, null); } } @Override public void setBeanClassLoader(ClassLoader classLoader) { this.beanClassLoader = classLoader; } @Override public void setEnvironment(Environment environment) { String property = environment.getProperty(RABBIT_EMPTY_STRING_ARGUMENTS_PROPERTY, String.class); if (property != null) { this.emptyStringArguments.addAll(StringUtils.commaDelimitedListToSet(property)); } } @Override public void afterSingletonsInstantiated() { this.registrar.setBeanFactory(this.beanFactory); if (this.beanFactory instanceof ListableBeanFactory) { Map instances = ((ListableBeanFactory) this.beanFactory).getBeansOfType(RabbitListenerConfigurer.class); for (RabbitListenerConfigurer configurer : instances.values()) { configurer.configureRabbitListeners(this.registrar); } } if (this.registrar.getEndpointRegistry() == null) { if (this.endpointRegistry == null) { Assert.state(this.beanFactory != null, "BeanFactory must be set to find endpoint registry by bean name"); this.endpointRegistry = this.beanFactory.getBean( RabbitListenerConfigUtils.RABBIT_LISTENER_ENDPOINT_REGISTRY_BEAN_NAME, RabbitListenerEndpointRegistry.class); } this.registrar.setEndpointRegistry(this.endpointRegistry); } if (this.containerFactoryBeanName != null) { this.registrar.setContainerFactoryBeanName(this.containerFactoryBeanName); } // Set the custom handler method factory once resolved by the configurer MessageHandlerMethodFactory handlerMethodFactory = this.registrar.getMessageHandlerMethodFactory(); if (handlerMethodFactory != null) { this.messageHandlerMethodFactory.setMessageHandlerMethodFactory(handlerMethodFactory); } // Actually register all listeners this.registrar.afterPropertiesSet(); // clear the cache - prototype beans will be re-cached. this.typeCache.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 { Class targetClass = AopUtils.getTargetClass(bean); TypeMetadata metadata = this.typeCache.get(targetClass); if (metadata == null) { metadata = buildMetadata(targetClass); this.typeCache.putIfAbsent(targetClass, metadata); } for (ListenerMethod lm : metadata.listenerMethods) { for (RabbitListener rabbitListener : lm.annotations) { processAmqpListener(rabbitListener, lm.method, bean, beanName); } } if (metadata.handlerMethods.length > 0) { processMultiMethodListeners(metadata.classAnnotations, metadata.handlerMethods, bean, beanName); } return bean; } private TypeMetadata buildMetadata(Class targetClass) { Collection classLevelListeners = findListenerAnnotations(targetClass); final boolean hasClassLevelListeners = classLevelListeners.size() > 0; final List methods = new ArrayList(); final List multiMethods = new ArrayList(); ReflectionUtils.doWithMethods(targetClass, new ReflectionUtils.MethodCallback() { @Override public void doWith(Method method) throws IllegalArgumentException, IllegalAccessException { Collection listenerAnnotations = findListenerAnnotations(method); if (listenerAnnotations.size() > 0) { methods.add(new ListenerMethod(method, listenerAnnotations.toArray(new RabbitListener[listenerAnnotations.size()]))); } if (hasClassLevelListeners) { RabbitHandler rabbitHandler = AnnotationUtils.findAnnotation(method, RabbitHandler.class); if (rabbitHandler != null) { multiMethods.add(method); } } } }, ReflectionUtils.USER_DECLARED_METHODS); if (methods.isEmpty() && multiMethods.isEmpty()) { return TypeMetadata.EMPTY; } return new TypeMetadata( methods.toArray(new ListenerMethod[methods.size()]), multiMethods.toArray(new Method[multiMethods.size()]), classLevelListeners.toArray(new RabbitListener[classLevelListeners.size()])); } /* * AnnotationUtils.getRepeatableAnnotations does not look at interfaces */ private Collection findListenerAnnotations(Class clazz) { Set listeners = new HashSet(); RabbitListener ann = AnnotationUtils.findAnnotation(clazz, RabbitListener.class); if (ann != null) { listeners.add(ann); } RabbitListeners anns = AnnotationUtils.findAnnotation(clazz, RabbitListeners.class); if (anns != null) { Collections.addAll(listeners, anns.value()); } return listeners; } /* * AnnotationUtils.getRepeatableAnnotations does not look at interfaces */ private Collection findListenerAnnotations(Method method) { Set listeners = new HashSet(); RabbitListener ann = AnnotationUtils.findAnnotation(method, RabbitListener.class); if (ann != null) { listeners.add(ann); } RabbitListeners anns = AnnotationUtils.findAnnotation(method, RabbitListeners.class); if (anns != null) { Collections.addAll(listeners, anns.value()); } return listeners; } private void processMultiMethodListeners(RabbitListener[] classLevelListeners, Method[] multiMethods, Object bean, String beanName) { List checkedMethods = new ArrayList(); for (Method method : multiMethods) { checkedMethods.add(checkProxy(method, bean)); } for (RabbitListener classLevelListener : classLevelListeners) { MultiMethodRabbitListenerEndpoint endpoint = new MultiMethodRabbitListenerEndpoint(checkedMethods, bean); endpoint.setBeanFactory(this.beanFactory); processListener(endpoint, classLevelListener, bean, bean.getClass(), beanName); } } protected void processAmqpListener(RabbitListener rabbitListener, Method method, Object bean, String beanName) { Method methodToUse = checkProxy(method, bean); MethodRabbitListenerEndpoint endpoint = new MethodRabbitListenerEndpoint(); endpoint.setMethod(methodToUse); endpoint.setBeanFactory(this.beanFactory); processListener(endpoint, rabbitListener, bean, methodToUse, beanName); } private Method checkProxy(Method method, Object bean) { if (AopUtils.isJdkDynamicProxy(bean)) { try { // Found a @RabbitListener method on the target class for this JDK proxy -> // is it also present on the proxy itself? method = bean.getClass().getMethod(method.getName(), method.getParameterTypes()); Class[] proxiedInterfaces = ((Advised) bean).getProxiedInterfaces(); for (Class iface : proxiedInterfaces) { try { method = iface.getMethod(method.getName(), method.getParameterTypes()); break; } catch (NoSuchMethodException noMethod) { } } } catch (SecurityException ex) { ReflectionUtils.handleReflectionException(ex); } catch (NoSuchMethodException ex) { throw new IllegalStateException(String.format( "@RabbitListener method '%s' found on bean target class '%s', " + "but not found in any interface(s) for bean JDK proxy. Either " + "pull the method up to an interface or switch to subclass (CGLIB) " + "proxies by setting proxy-target-class/proxyTargetClass " + "attribute to 'true'", method.getName(), method.getDeclaringClass().getSimpleName())); } } return method; } protected void processListener(MethodRabbitListenerEndpoint endpoint, RabbitListener rabbitListener, Object bean, Object adminTarget, String beanName) { endpoint.setBean(bean); endpoint.setMessageHandlerMethodFactory(this.messageHandlerMethodFactory); endpoint.setId(getEndpointId(rabbitListener)); endpoint.setQueueNames(resolveQueues(rabbitListener)); String group = rabbitListener.group(); if (StringUtils.hasText(group)) { Object resolvedGroup = resolveExpression(group); if (resolvedGroup instanceof String) { endpoint.setGroup((String) resolvedGroup); } } endpoint.setExclusive(rabbitListener.exclusive()); String priority = resolve(rabbitListener.priority()); if (StringUtils.hasText(priority)) { try { endpoint.setPriority(Integer.valueOf(priority)); } catch (NumberFormatException ex) { throw new BeanInitializationException("Invalid priority value for " + rabbitListener + " (must be an integer)", ex); } } String rabbitAdmin = resolve(rabbitListener.admin()); if (StringUtils.hasText(rabbitAdmin)) { Assert.state(this.beanFactory != null, "BeanFactory must be set to resolve RabbitAdmin by bean name"); try { endpoint.setAdmin(this.beanFactory.getBean(rabbitAdmin, RabbitAdmin.class)); } catch (NoSuchBeanDefinitionException ex) { throw new BeanInitializationException("Could not register rabbit listener endpoint on [" + adminTarget + "], no " + RabbitAdmin.class.getSimpleName() + " with id '" + rabbitAdmin + "' was found in the application context", ex); } } RabbitListenerContainerFactory factory = null; String containerFactoryBeanName = resolve(rabbitListener.containerFactory()); if (StringUtils.hasText(containerFactoryBeanName)) { Assert.state(this.beanFactory != null, "BeanFactory must be set to obtain container factory by bean name"); try { factory = this.beanFactory.getBean(containerFactoryBeanName, RabbitListenerContainerFactory.class); } catch (NoSuchBeanDefinitionException ex) { throw new BeanInitializationException("Could not register rabbit listener endpoint on [" + adminTarget + "] for bean " + beanName + ", no " + RabbitListenerContainerFactory.class.getSimpleName() + " with id '" + containerFactoryBeanName + "' was found in the application context", ex); } } this.registrar.registerEndpoint(endpoint, factory); } private String getEndpointId(RabbitListener rabbitListener) { if (StringUtils.hasText(rabbitListener.id())) { return resolve(rabbitListener.id()); } else { return "io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.RabbitListenerEndpointContainer#" + this.counter.getAndIncrement(); } } private String[] resolveQueues(RabbitListener rabbitListener) { String[] queues = rabbitListener.queues(); QueueBinding[] bindings = rabbitListener.bindings(); if (queues.length > 0 && bindings.length > 0) { throw new BeanInitializationException("@RabbitListener can have 'queues' or 'bindings' but not both"); } List result = new ArrayList(); if (queues.length > 0) { for (int i = 0; i < queues.length; i++) { Object resolvedValue = resolveExpression(queues[i]); resolveAsString(resolvedValue, result); } } else { return registerBeansForDeclaration(rabbitListener); } return result.toArray(new String[result.size()]); } @SuppressWarnings("unchecked") private void resolveAsString(Object resolvedValue, List result) { Object resolvedValueToUse = resolvedValue; if (resolvedValue instanceof String[]) { resolvedValueToUse = Arrays.asList((String[]) resolvedValue); } if (resolvedValueToUse instanceof Queue) { result.add(((Queue) resolvedValueToUse).getName()); } else if (resolvedValueToUse instanceof String) { result.add((String) resolvedValueToUse); } else if (resolvedValueToUse instanceof Iterable) { for (Object object : (Iterable) resolvedValueToUse) { resolveAsString(object, result); } } else { throw new IllegalArgumentException(String.format( "@RabbitListener can't resolve '%s' as either a String or a Queue", resolvedValue)); } } private String[] registerBeansForDeclaration(RabbitListener rabbitListener) { List queues = new ArrayList(); if (this.beanFactory instanceof ConfigurableBeanFactory) { for (QueueBinding binding : rabbitListener.bindings()) { String queueName = declareQueue(binding); queues.add(queueName); declareExchangeAndBinding(binding, queueName); } } return queues.toArray(new String[queues.size()]); } private String declareQueue(QueueBinding binding) { io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.annotation.Queue bindingQueue = binding.value(); String queueName = (String) resolveExpression(bindingQueue.value()); boolean exclusive = false; boolean autoDelete = false; if (!StringUtils.hasText(queueName)) { queueName = UUID.randomUUID().toString(); // default exclusive/autodelete to true when anonymous if (!StringUtils.hasText(bindingQueue.exclusive()) || resolveExpressionAsBoolean(bindingQueue.exclusive())) { exclusive = true; } if (!StringUtils.hasText(bindingQueue.autoDelete()) || resolveExpressionAsBoolean(bindingQueue.autoDelete())) { autoDelete = true; } } else { exclusive = resolveExpressionAsBoolean(bindingQueue.exclusive()); autoDelete = resolveExpressionAsBoolean(bindingQueue.autoDelete()); } Queue queue = new Queue(queueName, resolveExpressionAsBoolean(bindingQueue.durable()), exclusive, autoDelete, resolveArguments(bindingQueue.arguments())); queue.setIgnoreDeclarationExceptions(resolveExpressionAsBoolean(bindingQueue.ignoreDeclarationExceptions())); ((ConfigurableBeanFactory) this.beanFactory).registerSingleton(queueName + ++this.increment, queue); return queueName; } private void declareExchangeAndBinding(QueueBinding binding, String queueName) { io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.annotation.Exchange bindingExchange = binding.exchange(); String exchangeName = resolveExpressionAsString(bindingExchange.value(), "@Exchange.exchange"); String exchangeType = resolveExpressionAsString(bindingExchange.type(), "@Exchange.type"); String routingKey = resolveExpressionAsString(binding.key(), "@QueueBinding.key"); Exchange exchange; Binding actualBinding; if (exchangeType.equals(ExchangeTypes.DIRECT)) { exchange = directExchange(bindingExchange, exchangeName); actualBinding = new Binding(queueName, DestinationType.QUEUE, exchangeName, routingKey, resolveArguments(binding.arguments())); } else if (exchangeType.equals(ExchangeTypes.FANOUT)) { exchange = fanoutExchange(bindingExchange, exchangeName); actualBinding = new Binding(queueName, DestinationType.QUEUE, exchangeName, "", resolveArguments(binding.arguments())); } else if (exchangeType.equals(ExchangeTypes.TOPIC)) { exchange = topicExchange(bindingExchange, exchangeName); actualBinding = new Binding(queueName, DestinationType.QUEUE, exchangeName, routingKey, resolveArguments(binding.arguments())); } else if (exchangeType.equals(ExchangeTypes.HEADERS)) { exchange = headersExchange(bindingExchange, exchangeName); actualBinding = new Binding(queueName, DestinationType.QUEUE, exchangeName, routingKey, resolveArguments(binding.arguments())); } else { throw new BeanInitializationException("Unexpected exchange type: " + exchangeType); } AbstractExchange abstractExchange = (AbstractExchange) exchange; abstractExchange.setInternal(resolveExpressionAsBoolean(bindingExchange.internal())); abstractExchange.setDelayed(resolveExpressionAsBoolean(bindingExchange.delayed())); abstractExchange.setIgnoreDeclarationExceptions(resolveExpressionAsBoolean(bindingExchange.ignoreDeclarationExceptions())); ((AbstractDeclarable) actualBinding) .setIgnoreDeclarationExceptions(resolveExpressionAsBoolean(binding.ignoreDeclarationExceptions())); ((ConfigurableBeanFactory) this.beanFactory).registerSingleton(exchangeName + ++this.increment, exchange); ((ConfigurableBeanFactory) this.beanFactory).registerSingleton( exchangeName + "." + queueName + ++this.increment, actualBinding); } private Exchange directExchange(io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.annotation.Exchange bindingExchange, String exchangeName) { return new DirectExchange(exchangeName, resolveExpressionAsBoolean(bindingExchange.durable()), resolveExpressionAsBoolean(bindingExchange.autoDelete()), resolveArguments(bindingExchange.arguments())); } private Exchange fanoutExchange(io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.annotation.Exchange bindingExchange, String exchangeName) { return new FanoutExchange(exchangeName, resolveExpressionAsBoolean(bindingExchange.durable()), resolveExpressionAsBoolean(bindingExchange.autoDelete()), resolveArguments(bindingExchange.arguments())); } private Exchange topicExchange(io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.annotation.Exchange bindingExchange, String exchangeName) { return new TopicExchange(exchangeName, resolveExpressionAsBoolean(bindingExchange.durable()), resolveExpressionAsBoolean(bindingExchange.autoDelete()), resolveArguments(bindingExchange.arguments())); } private Exchange headersExchange(io.bitsensor.plugins.shaded.io.bitsensor.plugins.shaded.org.springframework.amqp.rabbit.annotation.Exchange bindingExchange, String exchangeName) { return new HeadersExchange(exchangeName, resolveExpressionAsBoolean(bindingExchange.durable()), resolveExpressionAsBoolean(bindingExchange.autoDelete()), resolveArguments(bindingExchange.arguments())); } private Map resolveArguments(Argument[] arguments) { Map map = new HashMap(); for (Argument arg : arguments) { String key = resolveExpressionAsString(arg.name(), "@Argument.name"); if (StringUtils.hasText(key)) { Object value = resolveExpression(arg.value()); Object type = resolveExpression(arg.type()); Class typeClass; String typeName; if (type instanceof Class) { typeClass = (Class) type; typeName = typeClass.getName(); } else { Assert.isTrue(type instanceof String, "Type must resolve to a Class or String, but resolved to [" + type.getClass().getName() + "]"); typeName = (String) type; try { typeClass = ClassUtils.forName(typeName, this.beanClassLoader); } catch (Exception e) { throw new IllegalStateException("Could not load class", e); } } addToMap(map, key, value, typeClass, typeName); } else { if (this.logger.isDebugEnabled()) { this.logger.debug("@Argument ignored because the name resolved to an empty String"); } } } return map.size() < 1 ? null : map; } private void addToMap(Map map, String key, Object value, Class typeClass, String typeName) { if (value.getClass().getName().equals(typeName)) { if (typeClass.equals(String.class) && !StringUtils.hasText((String) value)) { putEmpty(map, key); } else { map.put(key, value); } } else { if (value instanceof String && !StringUtils.hasText((String) value)) { putEmpty(map, key); } else { if (CONVERSION_SERVICE.canConvert(value.getClass(), typeClass)) { map.put(key, CONVERSION_SERVICE.convert(value, typeClass)); } else { throw new IllegalStateException("Cannot convert from " + value.getClass().getName() + " to " + typeName); } } } } private void putEmpty(Map map, String key) { if (this.emptyStringArguments.contains(key)) { map.put(key, ""); } else { map.put(key, null); } } private boolean resolveExpressionAsBoolean(String value) { Object resolved = resolveExpression(value); if (resolved instanceof Boolean) { return (Boolean) resolved; } else if (resolved instanceof String) { return Boolean.valueOf((String) resolved); } else { return false; } } private String resolveExpressionAsString(String value, String attribute) { Object resolved = resolveExpression(value); if (resolved instanceof String) { return (String) resolved; } else { throw new IllegalStateException("The [" + attribute + "] must resolve to a String. " + "Resolved to [" + resolved.getClass() + "] for [" + value + "]"); } } private Object resolveExpression(String value) { String resolvedValue = resolve(value); if (!(resolvedValue.startsWith("#{") && value.endsWith("}"))) { return resolvedValue; } return this.resolver.evaluate(resolvedValue, this.expressionContext); } /** * Resolve the specified value if possible. * * @see ConfigurableBeanFactory#resolveEmbeddedValue */ private String resolve(String value) { if (this.beanFactory != null && this.beanFactory instanceof ConfigurableBeanFactory) { return ((ConfigurableBeanFactory) this.beanFactory).resolveEmbeddedValue(value); } return value; } /** * An {@link MessageHandlerMethodFactory} adapter that offers a configurable underlying * instance to use. Useful if the factory to use is determined once the endpoints * have been registered but not created yet. * @see RabbitListenerEndpointRegistrar#setMessageHandlerMethodFactory */ private class RabbitHandlerMethodFactoryAdapter implements MessageHandlerMethodFactory { private MessageHandlerMethodFactory messageHandlerMethodFactory; public void setMessageHandlerMethodFactory(MessageHandlerMethodFactory rabbitHandlerMethodFactory1) { this.messageHandlerMethodFactory = rabbitHandlerMethodFactory1; } @Override public InvocableHandlerMethod createInvocableHandlerMethod(Object bean, Method method) { return getMessageHandlerMethodFactory().createInvocableHandlerMethod(bean, method); } private MessageHandlerMethodFactory getMessageHandlerMethodFactory() { if (this.messageHandlerMethodFactory == null) { this.messageHandlerMethodFactory = createDefaultMessageHandlerMethodFactory(); } return this.messageHandlerMethodFactory; } private MessageHandlerMethodFactory createDefaultMessageHandlerMethodFactory() { DefaultMessageHandlerMethodFactory defaultFactory = new DefaultMessageHandlerMethodFactory(); defaultFactory.setBeanFactory(RabbitListenerAnnotationBeanPostProcessor.this.beanFactory); defaultFactory.afterPropertiesSet(); return defaultFactory; } } /** * The metadata holder of the class with {@link RabbitListener} * and {@link RabbitHandler} annotations. */ private static class TypeMetadata { /** * Methods annotated with {@link RabbitListener}. */ final ListenerMethod[] listenerMethods; /** * Methods annotated with {@link RabbitHandler}. */ final Method[] handlerMethods; /** * Class level {@link RabbitListener} annotations. */ final RabbitListener[] classAnnotations; static final TypeMetadata EMPTY = new TypeMetadata(); private TypeMetadata() { this.listenerMethods = new ListenerMethod[0]; this.handlerMethods = new Method[0]; this.classAnnotations = new RabbitListener[0]; } TypeMetadata(ListenerMethod[] methods, Method[] multiMethods, RabbitListener[] classLevelListeners) { this.listenerMethods = methods; this.handlerMethods = multiMethods; this.classAnnotations = classLevelListeners; } } /** * A method annotated with {@link RabbitListener}, together with the annotations. */ private static class ListenerMethod { final Method method; final RabbitListener[] annotations; ListenerMethod(Method method, RabbitListener[] annotations) { this.method = method; this.annotations = annotations; } } }