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