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.expression.spel.support; 018 019import java.lang.reflect.Constructor; 020import java.util.ArrayList; 021import java.util.Arrays; 022import java.util.List; 023 024import org.springframework.core.MethodParameter; 025import org.springframework.core.convert.TypeDescriptor; 026import org.springframework.expression.AccessException; 027import org.springframework.expression.ConstructorExecutor; 028import org.springframework.expression.ConstructorResolver; 029import org.springframework.expression.EvaluationContext; 030import org.springframework.expression.EvaluationException; 031import org.springframework.expression.TypeConverter; 032import org.springframework.lang.Nullable; 033 034/** 035 * A constructor resolver that uses reflection to locate the constructor that should be invoked. 036 * 037 * @author Andy Clement 038 * @author Juergen Hoeller 039 * @since 3.0 040 */ 041public class ReflectiveConstructorResolver implements ConstructorResolver { 042 043 /** 044 * Locate a constructor on the type. There are three kinds of match that might occur: 045 * <ol> 046 * <li>An exact match where the types of the arguments match the types of the constructor 047 * <li>An in-exact match where the types we are looking for are subtypes of those defined on the constructor 048 * <li>A match where we are able to convert the arguments into those expected by the constructor, according to the 049 * registered type converter. 050 * </ol> 051 */ 052 @Override 053 @Nullable 054 public ConstructorExecutor resolve(EvaluationContext context, String typeName, List<TypeDescriptor> argumentTypes) 055 throws AccessException { 056 057 try { 058 TypeConverter typeConverter = context.getTypeConverter(); 059 Class<?> type = context.getTypeLocator().findType(typeName); 060 Constructor<?>[] ctors = type.getConstructors(); 061 062 Arrays.sort(ctors, (c1, c2) -> { 063 int c1pl = c1.getParameterCount(); 064 int c2pl = c2.getParameterCount(); 065 return Integer.compare(c1pl, c2pl); 066 }); 067 068 Constructor<?> closeMatch = null; 069 Constructor<?> matchRequiringConversion = null; 070 071 for (Constructor<?> ctor : ctors) { 072 int paramCount = ctor.getParameterCount(); 073 List<TypeDescriptor> paramDescriptors = new ArrayList<>(paramCount); 074 for (int i = 0; i < paramCount; i++) { 075 paramDescriptors.add(new TypeDescriptor(new MethodParameter(ctor, i))); 076 } 077 ReflectionHelper.ArgumentsMatchInfo matchInfo = null; 078 if (ctor.isVarArgs() && argumentTypes.size() >= paramCount - 1) { 079 // *sigh* complicated 080 // Basically.. we have to have all parameters match up until the varargs one, then the rest of what is 081 // being provided should be 082 // the same type whilst the final argument to the method must be an array of that (oh, how easy...not) - 083 // or the final parameter 084 // we are supplied does match exactly (it is an array already). 085 matchInfo = ReflectionHelper.compareArgumentsVarargs(paramDescriptors, argumentTypes, typeConverter); 086 } 087 else if (paramCount == argumentTypes.size()) { 088 // worth a closer look 089 matchInfo = ReflectionHelper.compareArguments(paramDescriptors, argumentTypes, typeConverter); 090 } 091 if (matchInfo != null) { 092 if (matchInfo.isExactMatch()) { 093 return new ReflectiveConstructorExecutor(ctor); 094 } 095 else if (matchInfo.isCloseMatch()) { 096 closeMatch = ctor; 097 } 098 else if (matchInfo.isMatchRequiringConversion()) { 099 matchRequiringConversion = ctor; 100 } 101 } 102 } 103 104 if (closeMatch != null) { 105 return new ReflectiveConstructorExecutor(closeMatch); 106 } 107 else if (matchRequiringConversion != null) { 108 return new ReflectiveConstructorExecutor(matchRequiringConversion); 109 } 110 else { 111 return null; 112 } 113 } 114 catch (EvaluationException ex) { 115 throw new AccessException("Failed to resolve constructor", ex); 116 } 117 } 118 119}