001/* 002 * Copyright 2002-2020 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.invocation.reactive; 018 019import java.util.ArrayList; 020import java.util.Collections; 021import java.util.List; 022import java.util.Map; 023import java.util.concurrent.ConcurrentHashMap; 024 025import org.apache.commons.logging.Log; 026import org.apache.commons.logging.LogFactory; 027import reactor.core.publisher.Mono; 028 029import org.springframework.core.MethodParameter; 030import org.springframework.lang.Nullable; 031import org.springframework.messaging.Message; 032 033/** 034 * Resolves method parameters by delegating to a list of registered 035 * {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers}. 036 * Previously resolved method parameters are cached for faster lookups. 037 * 038 * @author Rossen Stoyanchev 039 * @since 5.2 040 */ 041public class HandlerMethodArgumentResolverComposite implements HandlerMethodArgumentResolver { 042 043 protected final Log logger = LogFactory.getLog(getClass()); 044 045 private final List<HandlerMethodArgumentResolver> argumentResolvers = new ArrayList<>(); 046 047 private final Map<MethodParameter, HandlerMethodArgumentResolver> argumentResolverCache = 048 new ConcurrentHashMap<>(256); 049 050 051 /** 052 * Add the given {@link HandlerMethodArgumentResolver}. 053 */ 054 public HandlerMethodArgumentResolverComposite addResolver(HandlerMethodArgumentResolver resolver) { 055 this.argumentResolvers.add(resolver); 056 return this; 057 } 058 059 /** 060 * Add the given {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers}. 061 */ 062 public HandlerMethodArgumentResolverComposite addResolvers(@Nullable HandlerMethodArgumentResolver... resolvers) { 063 if (resolvers != null) { 064 Collections.addAll(this.argumentResolvers, resolvers); 065 } 066 return this; 067 } 068 069 /** 070 * Add the given {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers}. 071 */ 072 public HandlerMethodArgumentResolverComposite addResolvers( 073 @Nullable List<? extends HandlerMethodArgumentResolver> resolvers) { 074 075 if (resolvers != null) { 076 this.argumentResolvers.addAll(resolvers); 077 } 078 return this; 079 } 080 081 /** 082 * Return a read-only list with the contained resolvers, or an empty list. 083 */ 084 public List<HandlerMethodArgumentResolver> getResolvers() { 085 return Collections.unmodifiableList(this.argumentResolvers); 086 } 087 088 /** 089 * Clear the list of configured resolvers. 090 */ 091 public void clear() { 092 this.argumentResolvers.clear(); 093 } 094 095 096 /** 097 * Whether the given {@linkplain MethodParameter method parameter} is 098 * supported by any registered {@link HandlerMethodArgumentResolver}. 099 */ 100 @Override 101 public boolean supportsParameter(MethodParameter parameter) { 102 return getArgumentResolver(parameter) != null; 103 } 104 105 /** 106 * Iterate over registered 107 * {@link HandlerMethodArgumentResolver HandlerMethodArgumentResolvers} and 108 * invoke the one that supports it. 109 * @throws IllegalStateException if no suitable 110 * {@link HandlerMethodArgumentResolver} is found. 111 */ 112 @Override 113 public Mono<Object> resolveArgument(MethodParameter parameter, Message<?> message) { 114 HandlerMethodArgumentResolver resolver = getArgumentResolver(parameter); 115 if (resolver == null) { 116 throw new IllegalArgumentException("Unsupported parameter type [" + 117 parameter.getParameterType().getName() + "]. supportsParameter should be called first."); 118 } 119 return resolver.resolveArgument(parameter, message); 120 } 121 122 /** 123 * Find a registered {@link HandlerMethodArgumentResolver} that supports 124 * the given method parameter. 125 */ 126 @Nullable 127 public HandlerMethodArgumentResolver getArgumentResolver(MethodParameter parameter) { 128 HandlerMethodArgumentResolver result = this.argumentResolverCache.get(parameter); 129 if (result == null) { 130 for (HandlerMethodArgumentResolver methodArgumentResolver : this.argumentResolvers) { 131 if (methodArgumentResolver.supportsParameter(parameter)) { 132 result = methodArgumentResolver; 133 this.argumentResolverCache.put(parameter, result); 134 break; 135 } 136 } 137 } 138 return result; 139 } 140 141}