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