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