001/*
002 * Copyright 2002-2019 the original author or authors.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License");
005 * you may not use this file except in compliance with the License.
006 * You may obtain a copy of the License at
007 *
008 *      https://www.apache.org/licenses/LICENSE-2.0
009 *
010 * Unless required by applicable law or agreed to in writing, software
011 * distributed under the License is distributed on an "AS IS" BASIS,
012 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013 * See the License for the specific language governing permissions and
014 * limitations under the License.
015 */
016
017package org.springframework.messaging.handler.annotation.support;
018
019import java.lang.reflect.Method;
020import java.util.ArrayList;
021import java.util.List;
022
023import org.springframework.beans.factory.BeanFactory;
024import org.springframework.beans.factory.BeanFactoryAware;
025import org.springframework.beans.factory.InitializingBean;
026import org.springframework.beans.factory.config.ConfigurableBeanFactory;
027import org.springframework.core.convert.ConversionService;
028import org.springframework.format.support.DefaultFormattingConversionService;
029import org.springframework.lang.Nullable;
030import org.springframework.messaging.converter.GenericMessageConverter;
031import org.springframework.messaging.converter.MessageConverter;
032import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolver;
033import org.springframework.messaging.handler.invocation.HandlerMethodArgumentResolverComposite;
034import org.springframework.messaging.handler.invocation.InvocableHandlerMethod;
035import org.springframework.util.Assert;
036import org.springframework.validation.Validator;
037
038/**
039 * The default {@link MessageHandlerMethodFactory} implementation creating an
040 * {@link InvocableHandlerMethod} with the necessary
041 * {@link HandlerMethodArgumentResolver} instances to detect and process
042 * most of the use cases defined by
043 * {@link org.springframework.messaging.handler.annotation.MessageMapping MessageMapping}.
044 *
045 * <p>Extra method argument resolvers can be added to customize the method
046 * signature that can be handled.
047 *
048 * <p>By default, the validation process redirects to a no-op implementation, see
049 * {@link #setValidator(Validator)} to customize it. The {@link ConversionService}
050 * can be customized in a similar manner to tune how the message payload
051 * can be converted
052 *
053 * @author Stephane Nicoll
054 * @author Juergen Hoeller
055 * @since 4.1
056 * @see #setConversionService
057 * @see #setValidator
058 * @see #setCustomArgumentResolvers
059 */
060public class DefaultMessageHandlerMethodFactory
061                implements MessageHandlerMethodFactory, BeanFactoryAware, InitializingBean {
062
063        private ConversionService conversionService = new DefaultFormattingConversionService();
064
065        @Nullable
066        private MessageConverter messageConverter;
067
068        @Nullable
069        private Validator validator;
070
071        @Nullable
072        private List<HandlerMethodArgumentResolver> customArgumentResolvers;
073
074        private final HandlerMethodArgumentResolverComposite argumentResolvers =
075                        new HandlerMethodArgumentResolverComposite();
076
077        @Nullable
078        private BeanFactory beanFactory;
079
080
081        /**
082         * Set the {@link ConversionService} to use to convert the original
083         * message payload or headers.
084         * @see HeaderMethodArgumentResolver
085         * @see GenericMessageConverter
086         */
087        public void setConversionService(ConversionService conversionService) {
088                this.conversionService = conversionService;
089        }
090
091        /**
092         * Set the {@link MessageConverter} to use. By default a {@link GenericMessageConverter}
093         * is used.
094         * @see GenericMessageConverter
095         */
096        public void setMessageConverter(MessageConverter messageConverter) {
097                this.messageConverter = messageConverter;
098        }
099
100        /**
101         * Set the Validator instance used for validating {@code @Payload} arguments.
102         * @see org.springframework.validation.annotation.Validated
103         * @see PayloadMethodArgumentResolver
104         */
105        public void setValidator(Validator validator) {
106                this.validator = validator;
107        }
108
109        /**
110         * Set the list of custom {@code HandlerMethodArgumentResolver}s that will be used
111         * after resolvers for supported argument type.
112         * @param customArgumentResolvers the list of resolvers (never {@code null})
113         */
114        public void setCustomArgumentResolvers(List<HandlerMethodArgumentResolver> customArgumentResolvers) {
115                this.customArgumentResolvers = customArgumentResolvers;
116        }
117
118        /**
119         * Configure the complete list of supported argument types effectively overriding
120         * the ones configured by default. This is an advanced option. For most use cases
121         * it should be sufficient to use {@link #setCustomArgumentResolvers(java.util.List)}.
122         */
123        @SuppressWarnings("ConstantConditions")
124        public void setArgumentResolvers(List<HandlerMethodArgumentResolver> argumentResolvers) {
125                if (argumentResolvers == null) {
126                        this.argumentResolvers.clear();
127                        return;
128                }
129                this.argumentResolvers.addResolvers(argumentResolvers);
130        }
131
132        /**
133         * A {@link BeanFactory} only needs to be available for placeholder resolution
134         * in handler method arguments; it's optional otherwise.
135         */
136        @Override
137        public void setBeanFactory(BeanFactory beanFactory) {
138                this.beanFactory = beanFactory;
139        }
140
141        @Override
142        public void afterPropertiesSet() {
143                if (this.messageConverter == null) {
144                        this.messageConverter = new GenericMessageConverter(this.conversionService);
145                }
146                if (this.argumentResolvers.getResolvers().isEmpty()) {
147                        this.argumentResolvers.addResolvers(initArgumentResolvers());
148                }
149        }
150
151
152        @Override
153        public InvocableHandlerMethod createInvocableHandlerMethod(Object bean, Method method) {
154                InvocableHandlerMethod handlerMethod = new InvocableHandlerMethod(bean, method);
155                handlerMethod.setMessageMethodArgumentResolvers(this.argumentResolvers);
156                return handlerMethod;
157        }
158
159        protected List<HandlerMethodArgumentResolver> initArgumentResolvers() {
160                List<HandlerMethodArgumentResolver> resolvers = new ArrayList<>();
161                ConfigurableBeanFactory beanFactory = (this.beanFactory instanceof ConfigurableBeanFactory ?
162                                (ConfigurableBeanFactory) this.beanFactory : null);
163
164                // Annotation-based argument resolution
165                resolvers.add(new HeaderMethodArgumentResolver(this.conversionService, beanFactory));
166                resolvers.add(new HeadersMethodArgumentResolver());
167
168                // Type-based argument resolution
169                resolvers.add(new MessageMethodArgumentResolver(this.messageConverter));
170
171                if (this.customArgumentResolvers != null) {
172                        resolvers.addAll(this.customArgumentResolvers);
173                }
174
175                Assert.notNull(this.messageConverter, "MessageConverter not configured");
176                resolvers.add(new PayloadMethodArgumentResolver(this.messageConverter, this.validator));
177
178                return resolvers;
179        }
180
181}