├── .gitignore ├── README.md ├── pom.xml └── src └── main ├── java ├── com │ ├── KillDemo.java │ └── SpringDependencyTest.java └── org │ └── springframework │ └── core │ └── ResolvableType.java └── resources └── interview.sql /.gitignore: -------------------------------------------------------------------------------- 1 | .* 2 | target/ 3 | demo.iml 4 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ### 说明 2 | 极海 演示代码 3 | -------------------------------------------------------------------------------- /pom.xml: -------------------------------------------------------------------------------- 1 | 2 | 5 | 4.0.0 6 | 7 | org.example 8 | demo 9 | 1.0-SNAPSHOT 10 | 11 | 12 | 8 13 | 8 14 | 15 | 16 | 17 | 18 | 19 | org.springframework.boot 20 | spring-boot-starter 21 | 2.2.6.RELEASE 22 | 23 | 24 | 25 | com.google.guava 26 | guava 27 | 22.0 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /src/main/java/com/KillDemo.java: -------------------------------------------------------------------------------- 1 | package com; 2 | 3 | import com.google.common.collect.Lists; 4 | 5 | import java.util.*; 6 | import java.util.concurrent.*; 7 | import java.util.stream.Collectors; 8 | 9 | /** 10 | * @Author:jihai 11 | * @Date:2022/5/26 12 | * @Description: 13 | */ 14 | public class KillDemo { 15 | /** 16 | * 启动10个用户线程 17 | * 库存6个 18 | * 生成一个合并队列,3个用户一批次 19 | * 每个用户能拿到自己的请求响应 20 | */ 21 | public static void main(String[] args) throws InterruptedException { 22 | ExecutorService executorService = Executors.newCachedThreadPool(); 23 | KillDemo killDemo = new KillDemo(); 24 | killDemo.mergeJob(); 25 | Thread.sleep(2000); 26 | 27 | CountDownLatch countDownLatch = new CountDownLatch(10); 28 | 29 | System.out.println("-------- 库存 --------"); 30 | System.out.println("库存初始数量 :" + killDemo.stock); 31 | 32 | Map> requestFutureMap = new HashMap<>(); 33 | for (int i = 0; i < 10; i++) { 34 | final Long orderId = i + 100L; 35 | final Long userId = Long.valueOf(i); 36 | UserRequest userRequest = new UserRequest(orderId, userId, 1); 37 | Future future = executorService.submit(() -> { 38 | countDownLatch.countDown(); 39 | countDownLatch.await(1, TimeUnit.SECONDS); 40 | return killDemo.operate(userRequest); 41 | }); 42 | 43 | requestFutureMap.put(userRequest, future); 44 | } 45 | 46 | System.out.println("------- 客户端响应 -------"); 47 | Thread.sleep(1000); 48 | requestFutureMap.entrySet().forEach(entry -> { 49 | try { 50 | Result result = entry.getValue().get(300, TimeUnit.MILLISECONDS); 51 | System.out.println(Thread.currentThread().getName() + ":客户端请求响应:" + result); 52 | 53 | if (! result.isSuccess() && result.getMsg().equals("等待超时")) { 54 | // 超时,发送请求回滚 55 | System.out.println(entry.getKey() + " 发起回滚操作"); 56 | killDemo.rollback(entry.getKey()); 57 | } 58 | } catch (Exception e) { 59 | e.printStackTrace(); 60 | } 61 | }); 62 | 63 | System.out.println("------- 库存操作日志 -------"); 64 | System.out.println("扣减成功条数: " + killDemo.operateChangeLogList.stream().filter(e -> e.getOperateType().equals(1)).count()); 65 | killDemo.operateChangeLogList.forEach(e -> { 66 | if (e.getOperateType().equals(1)) { 67 | System.out.println(e); 68 | } 69 | }); 70 | 71 | System.out.println("扣减回滚条数: " + killDemo.operateChangeLogList.stream().filter(e -> e.getOperateType().equals(2)).count()); 72 | killDemo.operateChangeLogList.forEach(e -> { 73 | if (e.getOperateType().equals(2)) { 74 | System.out.println(e); 75 | } 76 | }); 77 | 78 | System.out.println("-------- 库存 --------"); 79 | System.out.println("库存初始数量 :" + killDemo.stock); 80 | 81 | } 82 | 83 | private void rollback(UserRequest userRequest) { 84 | if (operateChangeLogList.stream().anyMatch(operateChangeLog -> operateChangeLog.getOrderId().equals(userRequest.getOrderId()))) { 85 | // 回滚 86 | boolean hasRollback = operateChangeLogList.stream().anyMatch(operateChangeLog -> operateChangeLog.getOrderId().equals(userRequest.getOrderId()) && operateChangeLog.getOperateType().equals(2)); 87 | if (hasRollback) return ; 88 | System.out.println(" 最终回滚"); 89 | stock += userRequest.getCount(); 90 | saveChangeLog(Lists.newArrayList(userRequest), 2); 91 | } 92 | // 忽略 93 | } 94 | 95 | // 模拟数据库行 96 | private Integer stock = 6; 97 | 98 | private BlockingQueue queue = new LinkedBlockingQueue<>(10); 99 | /** 100 | * 用户库存扣减 101 | * @param userRequest 102 | * @return 103 | */ 104 | public Result operate(UserRequest userRequest) throws InterruptedException { 105 | // TODO 阈值判断 106 | // TODO 队列的创建 107 | RequestPromise requestPromise = new RequestPromise(userRequest); 108 | synchronized (requestPromise) { 109 | boolean enqueueSuccess = queue.offer(requestPromise, 100, TimeUnit.MILLISECONDS); 110 | if (! enqueueSuccess) { 111 | return new Result(false, "系统繁忙"); 112 | } 113 | try { 114 | requestPromise.wait(200); 115 | if (requestPromise.getResult() == null) { 116 | return new Result(false, "等待超时"); 117 | } 118 | } catch (InterruptedException e) { 119 | return new Result(false, "被中断"); 120 | } 121 | } 122 | return requestPromise.getResult(); 123 | } 124 | 125 | public void mergeJob() { 126 | new Thread(() -> { 127 | List list = new ArrayList<>(); 128 | while (true) { 129 | if (queue.isEmpty()) { 130 | try { 131 | Thread.sleep(10); 132 | continue; 133 | } catch (InterruptedException e) { 134 | e.printStackTrace(); 135 | } 136 | } 137 | 138 | int batchSize = 3; 139 | for (int i = 0; i < batchSize; i++) { 140 | try { 141 | list.add(queue.take()); 142 | } catch (InterruptedException e) { 143 | e.printStackTrace(); 144 | } 145 | } 146 | 147 | // 用户ID=5的批次和之后的批次,请求都会超时 148 | if (list.stream().anyMatch(e -> e.getUserRequest().getUserId().equals(5L))) { 149 | try { 150 | Thread.sleep(200); 151 | } catch (InterruptedException e) { 152 | e.printStackTrace(); 153 | } 154 | } 155 | 156 | System.out.println(Thread.currentThread().getName() + ":合并扣减库存:" + list); 157 | 158 | int sum = list.stream().mapToInt(e -> e.getUserRequest().getCount()).sum(); 159 | // 两种情况 160 | if (sum <= stock) { 161 | // 开始事务 162 | stock -= sum; 163 | saveChangeLog(list.stream().map(RequestPromise::getUserRequest).collect(Collectors.toList()), 1); 164 | // 关闭事务 165 | // notify user 166 | list.forEach(requestPromise -> { 167 | requestPromise.setResult(new Result(true, "ok")); 168 | synchronized (requestPromise) { 169 | requestPromise.notify(); 170 | } 171 | }); 172 | list.clear(); 173 | continue; 174 | } 175 | for (RequestPromise requestPromise : list) { 176 | int count = requestPromise.getUserRequest().getCount(); 177 | if (count <= stock) { 178 | // 开启事务 179 | stock -= count; 180 | saveChangeLog(Lists.newArrayList(requestPromise.getUserRequest()), 1); 181 | // 关闭事务 182 | requestPromise.setResult(new Result(true, "ok")); 183 | } else { 184 | requestPromise.setResult(new Result(false, "库存不足")); 185 | } 186 | synchronized (requestPromise) { 187 | requestPromise.notify(); 188 | } 189 | } 190 | list.clear(); 191 | } 192 | }, "mergeThread").start(); 193 | } 194 | 195 | // 模拟数据库操作日志表 196 | // order_id_operate_type uk 197 | private List operateChangeLogList = new ArrayList<>(); 198 | 199 | /** 200 | * 写库存流水 201 | * @param list 202 | * @param operateType 203 | */ 204 | private void saveChangeLog(List list, int operateType) { 205 | List collect = list.stream().map(userRequest -> new OperateChangeLog(userRequest.getOrderId(), 206 | userRequest.getCount(), operateType)).collect(Collectors.toList()); 207 | operateChangeLogList.addAll(collect); 208 | } 209 | } 210 | class OperateChangeLog { 211 | private Long orderId; 212 | private Integer count; 213 | // 1-扣减,2-回滚 214 | private Integer operateType; 215 | 216 | public OperateChangeLog(Long orderId, Integer count, Integer operateType) { 217 | this.orderId = orderId; 218 | this.count = count; 219 | this.operateType = operateType; 220 | } 221 | 222 | public Long getOrderId() { 223 | return orderId; 224 | } 225 | 226 | public void setOrderId(Long orderId) { 227 | this.orderId = orderId; 228 | } 229 | 230 | public Integer getCount() { 231 | return count; 232 | } 233 | 234 | public void setCount(Integer count) { 235 | this.count = count; 236 | } 237 | 238 | public Integer getOperateType() { 239 | return operateType; 240 | } 241 | 242 | public void setOperateType(Integer operateType) { 243 | this.operateType = operateType; 244 | } 245 | 246 | @Override 247 | public String toString() { 248 | return "OperateChangeLog{" + 249 | "orderId=" + orderId + 250 | ", count=" + count + 251 | ", operateType=" + operateType + 252 | '}'; 253 | } 254 | } 255 | class RequestPromise { 256 | private UserRequest userRequest; 257 | private Result result; 258 | 259 | public RequestPromise(UserRequest userRequest) { 260 | this.userRequest = userRequest; 261 | } 262 | 263 | public RequestPromise(UserRequest userRequest, Result result) { 264 | this.userRequest = userRequest; 265 | this.result = result; 266 | } 267 | 268 | public UserRequest getUserRequest() { 269 | return userRequest; 270 | } 271 | 272 | public void setUserRequest(UserRequest userRequest) { 273 | this.userRequest = userRequest; 274 | } 275 | 276 | public Result getResult() { 277 | return result; 278 | } 279 | 280 | public void setResult(Result result) { 281 | this.result = result; 282 | } 283 | 284 | @Override 285 | public String toString() { 286 | return "RequestPromise{" + 287 | "userRequest=" + userRequest + 288 | ", result=" + result + 289 | '}'; 290 | } 291 | } 292 | class Result { 293 | private Boolean success; 294 | private String msg; 295 | 296 | public Result(boolean success, String msg) { 297 | this.success = success; 298 | this.msg = msg; 299 | } 300 | 301 | public boolean isSuccess() { 302 | return success; 303 | } 304 | 305 | public void setSuccess(boolean success) { 306 | this.success = success; 307 | } 308 | 309 | public String getMsg() { 310 | return msg; 311 | } 312 | 313 | public void setMsg(String msg) { 314 | this.msg = msg; 315 | } 316 | 317 | @Override 318 | public String toString() { 319 | return "Result{" + 320 | "success=" + success + 321 | ", msg='" + msg + '\'' + 322 | '}'; 323 | } 324 | } 325 | class UserRequest { 326 | private Long orderId; 327 | private Long userId; 328 | private Integer count; 329 | 330 | public UserRequest(Long orderId, Long userId, Integer count) { 331 | this.orderId = orderId; 332 | this.userId = userId; 333 | this.count = count; 334 | } 335 | 336 | public Long getOrderId() { 337 | return orderId; 338 | } 339 | 340 | public void setOrderId(Long orderId) { 341 | this.orderId = orderId; 342 | } 343 | 344 | public Long getUserId() { 345 | return userId; 346 | } 347 | 348 | public void setUserId(Long userId) { 349 | this.userId = userId; 350 | } 351 | 352 | public Integer getCount() { 353 | return count; 354 | } 355 | 356 | public void setCount(Integer count) { 357 | this.count = count; 358 | } 359 | 360 | @Override 361 | public String toString() { 362 | return "UserRequest{" + 363 | "orderId=" + orderId + 364 | ", userId=" + userId + 365 | ", count=" + count + 366 | '}'; 367 | } 368 | } 369 | -------------------------------------------------------------------------------- /src/main/java/com/SpringDependencyTest.java: -------------------------------------------------------------------------------- 1 | package com; 2 | 3 | import org.springframework.beans.factory.BeanFactory; 4 | import org.springframework.beans.factory.annotation.Autowired; 5 | import org.springframework.boot.SpringApplication; 6 | import org.springframework.boot.autoconfigure.SpringBootApplication; 7 | import org.springframework.context.annotation.Bean; 8 | import org.springframework.context.annotation.Configuration; 9 | import org.springframework.core.ResolvableType; 10 | import sun.misc.Launcher; 11 | 12 | /** 13 | * @Author:jihai 14 | * @Date:2022/6/15 15 | * @Description: 16 | */ 17 | @SpringBootApplication 18 | public class SpringDependencyTest { 19 | 20 | @Autowired 21 | private DistributedLock distributedLock; 22 | 23 | @Autowired 24 | private Lock lock; 25 | 26 | 27 | public static void main(String[] args) { 28 | // System.out.println(System.getenv()); 29 | 30 | System.getenv().entrySet().forEach(e -> { 31 | System.out.println(e); 32 | }); 33 | System.out.println(System.getProperties()); 34 | 35 | System.out.println("Spring app -> " + SpringDependencyTest.class.getClassLoader()); 36 | 37 | System.out.println("ResolvableType -> " + ResolvableType.class.getClassLoader()); 38 | 39 | System.out.println("BeanFactory -> " + BeanFactory.class.getClassLoader()); 40 | 41 | System.out.println("String -> " + String.class.getClassLoader()); 42 | 43 | ClassLoader classLoader = Launcher.getLauncher().getClassLoader(); 44 | // SpringApplication.run(SpringDependencyTest.class, args); 45 | } 46 | } 47 | @Configuration 48 | class Config { 49 | 50 | @Bean 51 | public Lock lock() { 52 | return new DistributedLock(); 53 | } 54 | 55 | } 56 | interface Lock {} 57 | 58 | class DistributedLock implements Lock {} 59 | -------------------------------------------------------------------------------- /src/main/java/org/springframework/core/ResolvableType.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2002-2019 the original author or authors. 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * https://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package org.springframework.core; 18 | 19 | import java.io.Serializable; 20 | import java.lang.reflect.Array; 21 | import java.lang.reflect.Constructor; 22 | import java.lang.reflect.Field; 23 | import java.lang.reflect.GenericArrayType; 24 | import java.lang.reflect.Method; 25 | import java.lang.reflect.ParameterizedType; 26 | import java.lang.reflect.Type; 27 | import java.lang.reflect.TypeVariable; 28 | import java.lang.reflect.WildcardType; 29 | import java.util.Arrays; 30 | import java.util.Collection; 31 | import java.util.IdentityHashMap; 32 | import java.util.Map; 33 | import java.util.StringJoiner; 34 | 35 | import org.springframework.core.SerializableTypeWrapper.FieldTypeProvider; 36 | import org.springframework.core.SerializableTypeWrapper.MethodParameterTypeProvider; 37 | import org.springframework.core.SerializableTypeWrapper.TypeProvider; 38 | import org.springframework.lang.Nullable; 39 | import org.springframework.util.Assert; 40 | import org.springframework.util.ClassUtils; 41 | import org.springframework.util.ConcurrentReferenceHashMap; 42 | import org.springframework.util.ObjectUtils; 43 | import org.springframework.util.StringUtils; 44 | 45 | /** 46 | * Encapsulates a Java {@link java.lang.reflect.Type}, providing access to 47 | * {@link #getSuperType() supertypes}, {@link #getInterfaces() interfaces}, and 48 | * {@link #getGeneric(int...) generic parameters} along with the ability to ultimately 49 | * {@link #resolve() resolve} to a {@link java.lang.Class}. 50 | * 51 | *

{@code ResolvableTypes} may be obtained from {@link #forField(Field) fields}, 52 | * {@link #forMethodParameter(Method, int) method parameters}, 53 | * {@link #forMethodReturnType(Method) method returns} or 54 | * {@link #forClass(Class) classes}. Most methods on this class will themselves return 55 | * {@link ResolvableType ResolvableTypes}, allowing easy navigation. For example: 56 | *

  57 |  * private HashMap<Integer, List<String>> myMap;
  58 |  *
  59 |  * public void example() {
  60 |  *     ResolvableType t = ResolvableType.forField(getClass().getDeclaredField("myMap"));
  61 |  *     t.getSuperType(); // AbstractMap<Integer, List<String>>
  62 |  *     t.asMap(); // Map<Integer, List<String>>
  63 |  *     t.getGeneric(0).resolve(); // Integer
  64 |  *     t.getGeneric(1).resolve(); // List
  65 |  *     t.getGeneric(1); // List<String>
  66 |  *     t.resolveGeneric(1, 0); // String
  67 |  * }
  68 |  * 
69 | * 70 | * @author Phillip Webb 71 | * @author Juergen Hoeller 72 | * @author Stephane Nicoll 73 | * @since 4.0 74 | * @see #forField(Field) 75 | * @see #forMethodParameter(Method, int) 76 | * @see #forMethodReturnType(Method) 77 | * @see #forConstructorParameter(Constructor, int) 78 | * @see #forClass(Class) 79 | * @see #forType(Type) 80 | * @see #forInstance(Object) 81 | * @see ResolvableTypeProvider 82 | */ 83 | @SuppressWarnings("serial") 84 | public class ResolvableType implements Serializable { 85 | 86 | /** 87 | * {@code ResolvableType} returned when no value is available. {@code NONE} is used 88 | * in preference to {@code null} so that multiple method calls can be safely chained. 89 | */ 90 | public static final ResolvableType NONE = new ResolvableType(EmptyType.INSTANCE, null, null, 0); 91 | 92 | private static final ResolvableType[] EMPTY_TYPES_ARRAY = new ResolvableType[0]; 93 | 94 | private static final ConcurrentReferenceHashMap cache = 95 | new ConcurrentReferenceHashMap<>(256); 96 | 97 | 98 | /** 99 | * The underlying Java type being managed. 100 | */ 101 | private final Type type; 102 | 103 | /** 104 | * Optional provider for the type. 105 | */ 106 | @Nullable 107 | private final TypeProvider typeProvider; 108 | 109 | /** 110 | * The {@code VariableResolver} to use or {@code null} if no resolver is available. 111 | */ 112 | @Nullable 113 | private final VariableResolver variableResolver; 114 | 115 | /** 116 | * The component type for an array or {@code null} if the type should be deduced. 117 | */ 118 | @Nullable 119 | private final ResolvableType componentType; 120 | 121 | @Nullable 122 | private final Integer hash; 123 | 124 | @Nullable 125 | private Class resolved; 126 | 127 | @Nullable 128 | private volatile ResolvableType superType; 129 | 130 | @Nullable 131 | private volatile ResolvableType[] interfaces; 132 | 133 | @Nullable 134 | private volatile ResolvableType[] generics; 135 | 136 | 137 | /** 138 | * Private constructor used to create a new {@link ResolvableType} for cache key purposes, 139 | * with no upfront resolution. 140 | */ 141 | private ResolvableType( 142 | Type type, @Nullable TypeProvider typeProvider, @Nullable VariableResolver variableResolver) { 143 | 144 | this.type = type; 145 | this.typeProvider = typeProvider; 146 | this.variableResolver = variableResolver; 147 | this.componentType = null; 148 | this.hash = calculateHashCode(); 149 | this.resolved = null; 150 | } 151 | 152 | /** 153 | * Private constructor used to create a new {@link ResolvableType} for cache value purposes, 154 | * with upfront resolution and a pre-calculated hash. 155 | * @since 4.2 156 | */ 157 | private ResolvableType(Type type, @Nullable TypeProvider typeProvider, 158 | @Nullable VariableResolver variableResolver, @Nullable Integer hash) { 159 | 160 | this.type = type; 161 | this.typeProvider = typeProvider; 162 | this.variableResolver = variableResolver; 163 | this.componentType = null; 164 | this.hash = hash; 165 | this.resolved = resolveClass(); 166 | } 167 | 168 | /** 169 | * Private constructor used to create a new {@link ResolvableType} for uncached purposes, 170 | * with upfront resolution but lazily calculated hash. 171 | */ 172 | private ResolvableType(Type type, @Nullable TypeProvider typeProvider, 173 | @Nullable VariableResolver variableResolver, @Nullable ResolvableType componentType) { 174 | 175 | this.type = type; 176 | this.typeProvider = typeProvider; 177 | this.variableResolver = variableResolver; 178 | this.componentType = componentType; 179 | this.hash = null; 180 | this.resolved = resolveClass(); 181 | } 182 | 183 | /** 184 | * Private constructor used to create a new {@link ResolvableType} on a {@link Class} basis. 185 | * Avoids all {@code instanceof} checks in order to create a straight {@link Class} wrapper. 186 | * @since 4.2 187 | */ 188 | private ResolvableType(@Nullable Class clazz) { 189 | this.resolved = (clazz != null ? clazz : Object.class); 190 | this.type = this.resolved; 191 | this.typeProvider = null; 192 | this.variableResolver = null; 193 | this.componentType = null; 194 | this.hash = null; 195 | } 196 | 197 | 198 | /** 199 | * Return the underling Java {@link Type} being managed. 200 | */ 201 | public Type getType() { 202 | return SerializableTypeWrapper.unwrap(this.type); 203 | } 204 | 205 | /** 206 | * Return the underlying Java {@link Class} being managed, if available; 207 | * otherwise {@code null}. 208 | */ 209 | @Nullable 210 | public Class getRawClass() { 211 | if (this.type == this.resolved) { 212 | return this.resolved; 213 | } 214 | Type rawType = this.type; 215 | if (rawType instanceof ParameterizedType) { 216 | rawType = ((ParameterizedType) rawType).getRawType(); 217 | } 218 | return (rawType instanceof Class ? (Class) rawType : null); 219 | } 220 | 221 | /** 222 | * Return the underlying source of the resolvable type. Will return a {@link Field}, 223 | * {@link MethodParameter} or {@link Type} depending on how the {@link ResolvableType} 224 | * was constructed. With the exception of the {@link #NONE} constant, this method will 225 | * never return {@code null}. This method is primarily to provide access to additional 226 | * type information or meta-data that alternative JVM languages may provide. 227 | */ 228 | public Object getSource() { 229 | Object source = (this.typeProvider != null ? this.typeProvider.getSource() : null); 230 | return (source != null ? source : this.type); 231 | } 232 | 233 | /** 234 | * Return this type as a resolved {@code Class}, falling back to 235 | * {@link java.lang.Object} if no specific class can be resolved. 236 | * @return the resolved {@link Class} or the {@code Object} fallback 237 | * @since 5.1 238 | * @see #getRawClass() 239 | * @see #resolve(Class) 240 | */ 241 | public Class toClass() { 242 | return resolve(Object.class); 243 | } 244 | 245 | /** 246 | * Determine whether the given object is an instance of this {@code ResolvableType}. 247 | * @param obj the object to check 248 | * @since 4.2 249 | * @see #isAssignableFrom(Class) 250 | */ 251 | public boolean isInstance(@Nullable Object obj) { 252 | return (obj != null && isAssignableFrom(obj.getClass())); 253 | } 254 | 255 | /** 256 | * Determine whether this {@code ResolvableType} is assignable from the 257 | * specified other type. 258 | * @param other the type to be checked against (as a {@code Class}) 259 | * @since 4.2 260 | * @see #isAssignableFrom(ResolvableType) 261 | */ 262 | public boolean isAssignableFrom(Class other) { 263 | return isAssignableFrom(forClass(other), null); 264 | } 265 | 266 | /** 267 | * Determine whether this {@code ResolvableType} is assignable from the 268 | * specified other type. 269 | *

Attempts to follow the same rules as the Java compiler, considering 270 | * whether both the {@link #resolve() resolved} {@code Class} is 271 | * {@link Class#isAssignableFrom(Class) assignable from} the given type 272 | * as well as whether all {@link #getGenerics() generics} are assignable. 273 | * @param other the type to be checked against (as a {@code ResolvableType}) 274 | * @return {@code true} if the specified other type can be assigned to this 275 | * {@code ResolvableType}; {@code false} otherwise 276 | */ 277 | public boolean isAssignableFrom(ResolvableType other) { 278 | return isAssignableFrom(other, null); 279 | } 280 | 281 | private boolean isAssignableFrom(ResolvableType other, @Nullable Map matchedBefore) { 282 | Assert.notNull(other, "ResolvableType must not be null"); 283 | 284 | // If we cannot resolve types, we are not assignable 285 | if (this == NONE || other == NONE) { 286 | return false; 287 | } 288 | 289 | // Deal with array by delegating to the component type 290 | if (isArray()) { 291 | return (other.isArray() && getComponentType().isAssignableFrom(other.getComponentType())); 292 | } 293 | 294 | if (matchedBefore != null && matchedBefore.get(this.type) == other.type) { 295 | return true; 296 | } 297 | 298 | // Deal with wildcard bounds 299 | WildcardBounds ourBounds = WildcardBounds.get(this); 300 | WildcardBounds typeBounds = WildcardBounds.get(other); 301 | 302 | // In the form X is assignable to 303 | if (typeBounds != null) { 304 | return (ourBounds != null && ourBounds.isSameKind(typeBounds) && 305 | ourBounds.isAssignableFrom(typeBounds.getBounds())); 306 | } 307 | 308 | // In the form is assignable to X... 309 | if (ourBounds != null) { 310 | return ourBounds.isAssignableFrom(other); 311 | } 312 | 313 | // Main assignability check about to follow 314 | boolean exactMatch = (matchedBefore != null); // We're checking nested generic variables now... 315 | boolean checkGenerics = true; 316 | Class ourResolved = null; 317 | if (this.type instanceof TypeVariable) { 318 | TypeVariable variable = (TypeVariable) this.type; 319 | // Try default variable resolution 320 | if (this.variableResolver != null) { 321 | ResolvableType resolved = this.variableResolver.resolveVariable(variable); 322 | if (resolved != null) { 323 | ourResolved = resolved.resolve(); 324 | } 325 | } 326 | if (ourResolved == null) { 327 | // Try variable resolution against target type 328 | if (other.variableResolver != null) { 329 | ResolvableType resolved = other.variableResolver.resolveVariable(variable); 330 | if (resolved != null) { 331 | ourResolved = resolved.resolve(); 332 | checkGenerics = false; 333 | } 334 | } 335 | } 336 | if (ourResolved == null) { 337 | // Unresolved type variable, potentially nested -> never insist on exact match 338 | exactMatch = false; 339 | } 340 | } 341 | if (ourResolved == null) { 342 | ourResolved = resolve(Object.class); 343 | } 344 | Class otherResolved = other.toClass(); 345 | 346 | // We need an exact type match for generics 347 | // List is not assignable from List 348 | if (exactMatch ? !ourResolved.equals(otherResolved) : !ClassUtils.isAssignable(ourResolved, otherResolved)) { 349 | return false; 350 | } 351 | 352 | if (checkGenerics) { 353 | // Recursively check each generic 354 | ResolvableType[] ourGenerics = getGenerics(); 355 | ResolvableType[] typeGenerics = other.as(ourResolved).getGenerics(); 356 | if (ourGenerics.length != typeGenerics.length) { 357 | return false; 358 | } 359 | if (matchedBefore == null) { 360 | matchedBefore = new IdentityHashMap<>(1); 361 | } 362 | matchedBefore.put(this.type, other.type); 363 | for (int i = 0; i < ourGenerics.length; i++) { 364 | if (!ourGenerics[i].isAssignableFrom(typeGenerics[i], matchedBefore)) { 365 | return false; 366 | } 367 | } 368 | } 369 | 370 | return true; 371 | } 372 | 373 | /** 374 | * Return {@code true} if this type resolves to a Class that represents an array. 375 | * @see #getComponentType() 376 | */ 377 | public boolean isArray() { 378 | if (this == NONE) { 379 | return false; 380 | } 381 | return ((this.type instanceof Class && ((Class) this.type).isArray()) || 382 | this.type instanceof GenericArrayType || resolveType().isArray()); 383 | } 384 | 385 | /** 386 | * Return the ResolvableType representing the component type of the array or 387 | * {@link #NONE} if this type does not represent an array. 388 | * @see #isArray() 389 | */ 390 | public ResolvableType getComponentType() { 391 | if (this == NONE) { 392 | return NONE; 393 | } 394 | if (this.componentType != null) { 395 | return this.componentType; 396 | } 397 | if (this.type instanceof Class) { 398 | Class componentType = ((Class) this.type).getComponentType(); 399 | return forType(componentType, this.variableResolver); 400 | } 401 | if (this.type instanceof GenericArrayType) { 402 | return forType(((GenericArrayType) this.type).getGenericComponentType(), this.variableResolver); 403 | } 404 | return resolveType().getComponentType(); 405 | } 406 | 407 | /** 408 | * Convenience method to return this type as a resolvable {@link Collection} type. 409 | * Returns {@link #NONE} if this type does not implement or extend 410 | * {@link Collection}. 411 | * @see #as(Class) 412 | * @see #asMap() 413 | */ 414 | public ResolvableType asCollection() { 415 | return as(Collection.class); 416 | } 417 | 418 | /** 419 | * Convenience method to return this type as a resolvable {@link Map} type. 420 | * Returns {@link #NONE} if this type does not implement or extend 421 | * {@link Map}. 422 | * @see #as(Class) 423 | * @see #asCollection() 424 | */ 425 | public ResolvableType asMap() { 426 | return as(Map.class); 427 | } 428 | 429 | /** 430 | * Return this type as a {@link ResolvableType} of the specified class. Searches 431 | * {@link #getSuperType() supertype} and {@link #getInterfaces() interface} 432 | * hierarchies to find a match, returning {@link #NONE} if this type does not 433 | * implement or extend the specified class. 434 | * @param type the required type (typically narrowed) 435 | * @return a {@link ResolvableType} representing this object as the specified 436 | * type, or {@link #NONE} if not resolvable as that type 437 | * @see #asCollection() 438 | * @see #asMap() 439 | * @see #getSuperType() 440 | * @see #getInterfaces() 441 | */ 442 | public ResolvableType as(Class type) { 443 | if (this == NONE) { 444 | return NONE; 445 | } 446 | Class resolved = resolve(); 447 | if (resolved == null || resolved == type) { 448 | return this; 449 | } 450 | for (ResolvableType interfaceType : getInterfaces()) { 451 | ResolvableType interfaceAsType = interfaceType.as(type); 452 | if (interfaceAsType != NONE) { 453 | return interfaceAsType; 454 | } 455 | } 456 | return getSuperType().as(type); 457 | } 458 | 459 | /** 460 | * Return a {@link ResolvableType} representing the direct supertype of this type. 461 | * If no supertype is available this method returns {@link #NONE}. 462 | *

Note: The resulting {@link ResolvableType} instance may not be {@link Serializable}. 463 | * @see #getInterfaces() 464 | */ 465 | public ResolvableType getSuperType() { 466 | Class resolved = resolve(); 467 | if (resolved == null || resolved.getGenericSuperclass() == null) { 468 | return NONE; 469 | } 470 | ResolvableType superType = this.superType; 471 | if (superType == null) { 472 | superType = forType(resolved.getGenericSuperclass(), this); 473 | this.superType = superType; 474 | } 475 | return superType; 476 | } 477 | 478 | /** 479 | * Return a {@link ResolvableType} array representing the direct interfaces 480 | * implemented by this type. If this type does not implement any interfaces an 481 | * empty array is returned. 482 | *

Note: The resulting {@link ResolvableType} instances may not be {@link Serializable}. 483 | * @see #getSuperType() 484 | */ 485 | public ResolvableType[] getInterfaces() { 486 | Class resolved = resolve(); 487 | if (resolved == null) { 488 | return EMPTY_TYPES_ARRAY; 489 | } 490 | ResolvableType[] interfaces = this.interfaces; 491 | if (interfaces == null) { 492 | Type[] genericIfcs = resolved.getGenericInterfaces(); 493 | interfaces = new ResolvableType[genericIfcs.length]; 494 | for (int i = 0; i < genericIfcs.length; i++) { 495 | interfaces[i] = forType(genericIfcs[i], this); 496 | } 497 | this.interfaces = interfaces; 498 | } 499 | return interfaces; 500 | } 501 | 502 | /** 503 | * Return {@code true} if this type contains generic parameters. 504 | * @see #getGeneric(int...) 505 | * @see #getGenerics() 506 | */ 507 | public boolean hasGenerics() { 508 | return (getGenerics().length > 0); 509 | } 510 | 511 | /** 512 | * Return {@code true} if this type contains unresolvable generics only, 513 | * that is, no substitute for any of its declared type variables. 514 | */ 515 | boolean isEntirelyUnresolvable() { 516 | if (this == NONE) { 517 | return false; 518 | } 519 | ResolvableType[] generics = getGenerics(); 520 | for (ResolvableType generic : generics) { 521 | if (!generic.isUnresolvableTypeVariable() && !generic.isWildcardWithoutBounds()) { 522 | return false; 523 | } 524 | } 525 | return true; 526 | } 527 | 528 | /** 529 | * Determine whether the underlying type has any unresolvable generics: 530 | * either through an unresolvable type variable on the type itself 531 | * or through implementing a generic interface in a raw fashion, 532 | * i.e. without substituting that interface's type variables. 533 | * The result will be {@code true} only in those two scenarios. 534 | */ 535 | public boolean hasUnresolvableGenerics() { 536 | if (this == NONE) { 537 | return false; 538 | } 539 | ResolvableType[] generics = getGenerics(); 540 | for (ResolvableType generic : generics) { 541 | if (generic.isUnresolvableTypeVariable() || generic.isWildcardWithoutBounds()) { 542 | return true; 543 | } 544 | } 545 | Class resolved = resolve(); 546 | if (resolved != null) { 547 | for (Type genericInterface : resolved.getGenericInterfaces()) { 548 | if (genericInterface instanceof Class) { 549 | if (forClass((Class) genericInterface).hasGenerics()) { 550 | return true; 551 | } 552 | } 553 | } 554 | return getSuperType().hasUnresolvableGenerics(); 555 | } 556 | return false; 557 | } 558 | 559 | /** 560 | * Determine whether the underlying type is a type variable that 561 | * cannot be resolved through the associated variable resolver. 562 | */ 563 | private boolean isUnresolvableTypeVariable() { 564 | if (this.type instanceof TypeVariable) { 565 | if (this.variableResolver == null) { 566 | return true; 567 | } 568 | TypeVariable variable = (TypeVariable) this.type; 569 | ResolvableType resolved = this.variableResolver.resolveVariable(variable); 570 | if (resolved == null || resolved.isUnresolvableTypeVariable()) { 571 | return true; 572 | } 573 | } 574 | return false; 575 | } 576 | 577 | /** 578 | * Determine whether the underlying type represents a wildcard 579 | * without specific bounds (i.e., equal to {@code ? extends Object}). 580 | */ 581 | private boolean isWildcardWithoutBounds() { 582 | if (this.type instanceof WildcardType) { 583 | WildcardType wt = (WildcardType) this.type; 584 | if (wt.getLowerBounds().length == 0) { 585 | Type[] upperBounds = wt.getUpperBounds(); 586 | if (upperBounds.length == 0 || (upperBounds.length == 1 && Object.class == upperBounds[0])) { 587 | return true; 588 | } 589 | } 590 | } 591 | return false; 592 | } 593 | 594 | /** 595 | * Return a {@link ResolvableType} for the specified nesting level. 596 | * See {@link #getNested(int, Map)} for details. 597 | * @param nestingLevel the nesting level 598 | * @return the {@link ResolvableType} type, or {@code #NONE} 599 | */ 600 | public ResolvableType getNested(int nestingLevel) { 601 | return getNested(nestingLevel, null); 602 | } 603 | 604 | /** 605 | * Return a {@link ResolvableType} for the specified nesting level. 606 | *

The nesting level refers to the specific generic parameter that should be returned. 607 | * A nesting level of 1 indicates this type; 2 indicates the first nested generic; 608 | * 3 the second; and so on. For example, given {@code List>} level 1 refers 609 | * to the {@code List}, level 2 the {@code Set}, and level 3 the {@code Integer}. 610 | *

The {@code typeIndexesPerLevel} map can be used to reference a specific generic 611 | * for the given level. For example, an index of 0 would refer to a {@code Map} key; 612 | * whereas, 1 would refer to the value. If the map does not contain a value for a 613 | * specific level the last generic will be used (e.g. a {@code Map} value). 614 | *

Nesting levels may also apply to array types; for example given 615 | * {@code String[]}, a nesting level of 2 refers to {@code String}. 616 | *

If a type does not {@link #hasGenerics() contain} generics the 617 | * {@link #getSuperType() supertype} hierarchy will be considered. 618 | * @param nestingLevel the required nesting level, indexed from 1 for the 619 | * current type, 2 for the first nested generic, 3 for the second and so on 620 | * @param typeIndexesPerLevel a map containing the generic index for a given 621 | * nesting level (may be {@code null}) 622 | * @return a {@link ResolvableType} for the nested level, or {@link #NONE} 623 | */ 624 | public ResolvableType getNested(int nestingLevel, @Nullable Map typeIndexesPerLevel) { 625 | ResolvableType result = this; 626 | for (int i = 2; i <= nestingLevel; i++) { 627 | if (result.isArray()) { 628 | result = result.getComponentType(); 629 | } 630 | else { 631 | // Handle derived types 632 | while (result != ResolvableType.NONE && !result.hasGenerics()) { 633 | result = result.getSuperType(); 634 | } 635 | Integer index = (typeIndexesPerLevel != null ? typeIndexesPerLevel.get(i) : null); 636 | index = (index == null ? result.getGenerics().length - 1 : index); 637 | result = result.getGeneric(index); 638 | } 639 | } 640 | return result; 641 | } 642 | 643 | /** 644 | * Return a {@link ResolvableType} representing the generic parameter for the 645 | * given indexes. Indexes are zero based; for example given the type 646 | * {@code Map>}, {@code getGeneric(0)} will access the 647 | * {@code Integer}. Nested generics can be accessed by specifying multiple indexes; 648 | * for example {@code getGeneric(1, 0)} will access the {@code String} from the 649 | * nested {@code List}. For convenience, if no indexes are specified the first 650 | * generic is returned. 651 | *

If no generic is available at the specified indexes {@link #NONE} is returned. 652 | * @param indexes the indexes that refer to the generic parameter 653 | * (may be omitted to return the first generic) 654 | * @return a {@link ResolvableType} for the specified generic, or {@link #NONE} 655 | * @see #hasGenerics() 656 | * @see #getGenerics() 657 | * @see #resolveGeneric(int...) 658 | * @see #resolveGenerics() 659 | */ 660 | public ResolvableType getGeneric(@Nullable int... indexes) { 661 | ResolvableType[] generics = getGenerics(); 662 | if (indexes == null || indexes.length == 0) { 663 | return (generics.length == 0 ? NONE : generics[0]); 664 | } 665 | ResolvableType generic = this; 666 | for (int index : indexes) { 667 | generics = generic.getGenerics(); 668 | if (index < 0 || index >= generics.length) { 669 | return NONE; 670 | } 671 | generic = generics[index]; 672 | } 673 | return generic; 674 | } 675 | 676 | /** 677 | * Return an array of {@link ResolvableType ResolvableTypes} representing the generic parameters of 678 | * this type. If no generics are available an empty array is returned. If you need to 679 | * access a specific generic consider using the {@link #getGeneric(int...)} method as 680 | * it allows access to nested generics and protects against 681 | * {@code IndexOutOfBoundsExceptions}. 682 | * @return an array of {@link ResolvableType ResolvableTypes} representing the generic parameters 683 | * (never {@code null}) 684 | * @see #hasGenerics() 685 | * @see #getGeneric(int...) 686 | * @see #resolveGeneric(int...) 687 | * @see #resolveGenerics() 688 | */ 689 | public ResolvableType[] getGenerics() { 690 | if (this == NONE) { 691 | return EMPTY_TYPES_ARRAY; 692 | } 693 | ResolvableType[] generics = this.generics; 694 | if (generics == null) { 695 | if (this.type instanceof Class) { 696 | Type[] typeParams = ((Class) this.type).getTypeParameters(); 697 | generics = new ResolvableType[typeParams.length]; 698 | for (int i = 0; i < generics.length; i++) { 699 | generics[i] = ResolvableType.forType(typeParams[i], this); 700 | } 701 | } 702 | else if (this.type instanceof ParameterizedType) { 703 | Type[] actualTypeArguments = ((ParameterizedType) this.type).getActualTypeArguments(); 704 | generics = new ResolvableType[actualTypeArguments.length]; 705 | for (int i = 0; i < actualTypeArguments.length; i++) { 706 | generics[i] = forType(actualTypeArguments[i], this.variableResolver); 707 | } 708 | } 709 | else { 710 | generics = resolveType().getGenerics(); 711 | } 712 | this.generics = generics; 713 | } 714 | return generics; 715 | } 716 | 717 | /** 718 | * Convenience method that will {@link #getGenerics() get} and 719 | * {@link #resolve() resolve} generic parameters. 720 | * @return an array of resolved generic parameters (the resulting array 721 | * will never be {@code null}, but it may contain {@code null} elements}) 722 | * @see #getGenerics() 723 | * @see #resolve() 724 | */ 725 | public Class[] resolveGenerics() { 726 | ResolvableType[] generics = getGenerics(); 727 | Class[] resolvedGenerics = new Class[generics.length]; 728 | for (int i = 0; i < generics.length; i++) { 729 | resolvedGenerics[i] = generics[i].resolve(); 730 | } 731 | return resolvedGenerics; 732 | } 733 | 734 | /** 735 | * Convenience method that will {@link #getGenerics() get} and {@link #resolve() 736 | * resolve} generic parameters, using the specified {@code fallback} if any type 737 | * cannot be resolved. 738 | * @param fallback the fallback class to use if resolution fails 739 | * @return an array of resolved generic parameters 740 | * @see #getGenerics() 741 | * @see #resolve() 742 | */ 743 | public Class[] resolveGenerics(Class fallback) { 744 | ResolvableType[] generics = getGenerics(); 745 | Class[] resolvedGenerics = new Class[generics.length]; 746 | for (int i = 0; i < generics.length; i++) { 747 | resolvedGenerics[i] = generics[i].resolve(fallback); 748 | } 749 | return resolvedGenerics; 750 | } 751 | 752 | /** 753 | * Convenience method that will {@link #getGeneric(int...) get} and 754 | * {@link #resolve() resolve} a specific generic parameters. 755 | * @param indexes the indexes that refer to the generic parameter 756 | * (may be omitted to return the first generic) 757 | * @return a resolved {@link Class} or {@code null} 758 | * @see #getGeneric(int...) 759 | * @see #resolve() 760 | */ 761 | @Nullable 762 | public Class resolveGeneric(int... indexes) { 763 | return getGeneric(indexes).resolve(); 764 | } 765 | 766 | /** 767 | * Resolve this type to a {@link java.lang.Class}, returning {@code null} 768 | * if the type cannot be resolved. This method will consider bounds of 769 | * {@link TypeVariable TypeVariables} and {@link WildcardType WildcardTypes} if 770 | * direct resolution fails; however, bounds of {@code Object.class} will be ignored. 771 | *

If this method returns a non-null {@code Class} and {@link #hasGenerics()} 772 | * returns {@code false}, the given type effectively wraps a plain {@code Class}, 773 | * allowing for plain {@code Class} processing if desirable. 774 | * @return the resolved {@link Class}, or {@code null} if not resolvable 775 | * @see #resolve(Class) 776 | * @see #resolveGeneric(int...) 777 | * @see #resolveGenerics() 778 | */ 779 | @Nullable 780 | public Class resolve() { 781 | return this.resolved; 782 | } 783 | 784 | /** 785 | * Resolve this type to a {@link java.lang.Class}, returning the specified 786 | * {@code fallback} if the type cannot be resolved. This method will consider bounds 787 | * of {@link TypeVariable TypeVariables} and {@link WildcardType WildcardTypes} if 788 | * direct resolution fails; however, bounds of {@code Object.class} will be ignored. 789 | * @param fallback the fallback class to use if resolution fails 790 | * @return the resolved {@link Class} or the {@code fallback} 791 | * @see #resolve() 792 | * @see #resolveGeneric(int...) 793 | * @see #resolveGenerics() 794 | */ 795 | public Class resolve(Class fallback) { 796 | return (this.resolved != null ? this.resolved : fallback); 797 | } 798 | 799 | @Nullable 800 | private Class resolveClass() { 801 | if (this.type == EmptyType.INSTANCE) { 802 | return null; 803 | } 804 | if (this.type instanceof Class) { 805 | return (Class) this.type; 806 | } 807 | if (this.type instanceof GenericArrayType) { 808 | Class resolvedComponent = getComponentType().resolve(); 809 | return (resolvedComponent != null ? Array.newInstance(resolvedComponent, 0).getClass() : null); 810 | } 811 | return resolveType().resolve(); 812 | } 813 | 814 | /** 815 | * Resolve this type by a single level, returning the resolved value or {@link #NONE}. 816 | *

Note: The returned {@link ResolvableType} should only be used as an intermediary 817 | * as it cannot be serialized. 818 | */ 819 | ResolvableType resolveType() { 820 | if (this.type instanceof ParameterizedType) { 821 | return forType(((ParameterizedType) this.type).getRawType(), this.variableResolver); 822 | } 823 | if (this.type instanceof WildcardType) { 824 | Type resolved = resolveBounds(((WildcardType) this.type).getUpperBounds()); 825 | if (resolved == null) { 826 | resolved = resolveBounds(((WildcardType) this.type).getLowerBounds()); 827 | } 828 | return forType(resolved, this.variableResolver); 829 | } 830 | if (this.type instanceof TypeVariable) { 831 | TypeVariable variable = (TypeVariable) this.type; 832 | // Try default variable resolution 833 | if (this.variableResolver != null) { 834 | ResolvableType resolved = this.variableResolver.resolveVariable(variable); 835 | if (resolved != null) { 836 | return resolved; 837 | } 838 | } 839 | // Fallback to bounds 840 | return forType(resolveBounds(variable.getBounds()), this.variableResolver); 841 | } 842 | return NONE; 843 | } 844 | 845 | @Nullable 846 | private Type resolveBounds(Type[] bounds) { 847 | if (bounds.length == 0 || bounds[0] == Object.class) { 848 | return null; 849 | } 850 | return bounds[0]; 851 | } 852 | 853 | @Nullable 854 | private ResolvableType resolveVariable(TypeVariable variable) { 855 | if (this.type instanceof TypeVariable) { 856 | return resolveType().resolveVariable(variable); 857 | } 858 | if (this.type instanceof ParameterizedType) { 859 | ParameterizedType parameterizedType = (ParameterizedType) this.type; 860 | Class resolved = resolve(); 861 | if (resolved == null) { 862 | return null; 863 | } 864 | TypeVariable[] variables = resolved.getTypeParameters(); 865 | for (int i = 0; i < variables.length; i++) { 866 | if (ObjectUtils.nullSafeEquals(variables[i].getName(), variable.getName())) { 867 | Type actualType = parameterizedType.getActualTypeArguments()[i]; 868 | return forType(actualType, this.variableResolver); 869 | } 870 | } 871 | Type ownerType = parameterizedType.getOwnerType(); 872 | if (ownerType != null) { 873 | return forType(ownerType, this.variableResolver).resolveVariable(variable); 874 | } 875 | } 876 | if (this.type instanceof WildcardType) { 877 | ResolvableType resolved = resolveType().resolveVariable(variable); 878 | if (resolved != null) { 879 | return resolved; 880 | } 881 | } 882 | if (this.variableResolver != null) { 883 | return this.variableResolver.resolveVariable(variable); 884 | } 885 | return null; 886 | } 887 | 888 | 889 | @Override 890 | public boolean equals(@Nullable Object other) { 891 | if (this == other) { 892 | return true; 893 | } 894 | if (!(other instanceof ResolvableType)) { 895 | return false; 896 | } 897 | 898 | ResolvableType otherType = (ResolvableType) other; 899 | if (!ObjectUtils.nullSafeEquals(this.type, otherType.type)) { 900 | return false; 901 | } 902 | if (this.typeProvider != otherType.typeProvider && 903 | (this.typeProvider == null || otherType.typeProvider == null || 904 | !ObjectUtils.nullSafeEquals(this.typeProvider.getType(), otherType.typeProvider.getType()))) { 905 | return false; 906 | } 907 | if (this.variableResolver != otherType.variableResolver && 908 | (this.variableResolver == null || otherType.variableResolver == null || 909 | !ObjectUtils.nullSafeEquals(this.variableResolver.getSource(), otherType.variableResolver.getSource()))) { 910 | return false; 911 | } 912 | if (!ObjectUtils.nullSafeEquals(this.componentType, otherType.componentType)) { 913 | return false; 914 | } 915 | return true; 916 | } 917 | 918 | @Override 919 | public int hashCode() { 920 | return (this.hash != null ? this.hash : calculateHashCode()); 921 | } 922 | 923 | private int calculateHashCode() { 924 | int hashCode = ObjectUtils.nullSafeHashCode(this.type); 925 | if (this.typeProvider != null) { 926 | hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.typeProvider.getType()); 927 | } 928 | if (this.variableResolver != null) { 929 | hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.variableResolver.getSource()); 930 | } 931 | if (this.componentType != null) { 932 | hashCode = 31 * hashCode + ObjectUtils.nullSafeHashCode(this.componentType); 933 | } 934 | return hashCode; 935 | } 936 | 937 | /** 938 | * Adapts this {@link ResolvableType} to a {@link VariableResolver}. 939 | */ 940 | @Nullable 941 | VariableResolver asVariableResolver() { 942 | if (this == NONE) { 943 | return null; 944 | } 945 | return new DefaultVariableResolver(this); 946 | } 947 | 948 | /** 949 | * Custom serialization support for {@link #NONE}. 950 | */ 951 | private Object readResolve() { 952 | return (this.type == EmptyType.INSTANCE ? NONE : this); 953 | } 954 | 955 | /** 956 | * Return a String representation of this type in its fully resolved form 957 | * (including any generic parameters). 958 | */ 959 | @Override 960 | public String toString() { 961 | if (isArray()) { 962 | return getComponentType() + "[]"; 963 | } 964 | if (this.resolved == null) { 965 | return "?"; 966 | } 967 | if (this.type instanceof TypeVariable) { 968 | TypeVariable variable = (TypeVariable) this.type; 969 | if (this.variableResolver == null || this.variableResolver.resolveVariable(variable) == null) { 970 | // Don't bother with variable boundaries for toString()... 971 | // Can cause infinite recursions in case of self-references 972 | return "?"; 973 | } 974 | } 975 | if (hasGenerics()) { 976 | return this.resolved.getName() + '<' + StringUtils.arrayToDelimitedString(getGenerics(), ", ") + '>'; 977 | } 978 | return this.resolved.getName(); 979 | } 980 | 981 | 982 | // Factory methods 983 | 984 | /** 985 | * Return a {@link ResolvableType} for the specified {@link Class}, 986 | * using the full generic type information for assignability checks. 987 | * For example: {@code ResolvableType.forClass(MyArrayList.class)}. 988 | * @param clazz the class to introspect ({@code null} is semantically 989 | * equivalent to {@code Object.class} for typical use cases here) 990 | * @return a {@link ResolvableType} for the specified class 991 | * @see #forClass(Class, Class) 992 | * @see #forClassWithGenerics(Class, Class...) 993 | */ 994 | public static ResolvableType forClass(@Nullable Class clazz) { 995 | return new ResolvableType(clazz); 996 | } 997 | 998 | /** 999 | * Return a {@link ResolvableType} for the specified {@link Class}, 1000 | * doing assignability checks against the raw class only (analogous to 1001 | * {@link Class#isAssignableFrom}, which this serves as a wrapper for. 1002 | * For example: {@code ResolvableType.forRawClass(List.class)}. 1003 | * @param clazz the class to introspect ({@code null} is semantically 1004 | * equivalent to {@code Object.class} for typical use cases here) 1005 | * @return a {@link ResolvableType} for the specified class 1006 | * @since 4.2 1007 | * @see #forClass(Class) 1008 | * @see #getRawClass() 1009 | */ 1010 | public static ResolvableType forRawClass(@Nullable Class clazz) { 1011 | return new ResolvableType(clazz) { 1012 | @Override 1013 | public ResolvableType[] getGenerics() { 1014 | return EMPTY_TYPES_ARRAY; 1015 | } 1016 | @Override 1017 | public boolean isAssignableFrom(Class other) { 1018 | return (clazz == null || ClassUtils.isAssignable(clazz, other)); 1019 | } 1020 | @Override 1021 | public boolean isAssignableFrom(ResolvableType other) { 1022 | Class otherClass = other.resolve(); 1023 | return (otherClass != null && (clazz == null || ClassUtils.isAssignable(clazz, otherClass)) 1024 | || otherClass.isAssignableFrom(clazz)); 1025 | } 1026 | }; 1027 | } 1028 | 1029 | /** 1030 | * Return a {@link ResolvableType} for the specified base type 1031 | * (interface or base class) with a given implementation class. 1032 | * For example: {@code ResolvableType.forClass(List.class, MyArrayList.class)}. 1033 | * @param baseType the base type (must not be {@code null}) 1034 | * @param implementationClass the implementation class 1035 | * @return a {@link ResolvableType} for the specified base type backed by the 1036 | * given implementation class 1037 | * @see #forClass(Class) 1038 | * @see #forClassWithGenerics(Class, Class...) 1039 | */ 1040 | public static ResolvableType forClass(Class baseType, Class implementationClass) { 1041 | Assert.notNull(baseType, "Base type must not be null"); 1042 | ResolvableType asType = forType(implementationClass).as(baseType); 1043 | return (asType == NONE ? forType(baseType) : asType); 1044 | } 1045 | 1046 | /** 1047 | * Return a {@link ResolvableType} for the specified {@link Class} with pre-declared generics. 1048 | * @param clazz the class (or interface) to introspect 1049 | * @param generics the generics of the class 1050 | * @return a {@link ResolvableType} for the specific class and generics 1051 | * @see #forClassWithGenerics(Class, ResolvableType...) 1052 | */ 1053 | public static ResolvableType forClassWithGenerics(Class clazz, Class... generics) { 1054 | Assert.notNull(clazz, "Class must not be null"); 1055 | Assert.notNull(generics, "Generics array must not be null"); 1056 | ResolvableType[] resolvableGenerics = new ResolvableType[generics.length]; 1057 | for (int i = 0; i < generics.length; i++) { 1058 | resolvableGenerics[i] = forClass(generics[i]); 1059 | } 1060 | return forClassWithGenerics(clazz, resolvableGenerics); 1061 | } 1062 | 1063 | /** 1064 | * Return a {@link ResolvableType} for the specified {@link Class} with pre-declared generics. 1065 | * @param clazz the class (or interface) to introspect 1066 | * @param generics the generics of the class 1067 | * @return a {@link ResolvableType} for the specific class and generics 1068 | * @see #forClassWithGenerics(Class, Class...) 1069 | */ 1070 | public static ResolvableType forClassWithGenerics(Class clazz, ResolvableType... generics) { 1071 | Assert.notNull(clazz, "Class must not be null"); 1072 | Assert.notNull(generics, "Generics array must not be null"); 1073 | TypeVariable[] variables = clazz.getTypeParameters(); 1074 | Assert.isTrue(variables.length == generics.length, "Mismatched number of generics specified"); 1075 | 1076 | Type[] arguments = new Type[generics.length]; 1077 | for (int i = 0; i < generics.length; i++) { 1078 | ResolvableType generic = generics[i]; 1079 | Type argument = (generic != null ? generic.getType() : null); 1080 | arguments[i] = (argument != null && !(argument instanceof TypeVariable) ? argument : variables[i]); 1081 | } 1082 | 1083 | ParameterizedType syntheticType = new SyntheticParameterizedType(clazz, arguments); 1084 | return forType(syntheticType, new TypeVariablesVariableResolver(variables, generics)); 1085 | } 1086 | 1087 | /** 1088 | * Return a {@link ResolvableType} for the specified instance. The instance does not 1089 | * convey generic information but if it implements {@link ResolvableTypeProvider} a 1090 | * more precise {@link ResolvableType} can be used than the simple one based on 1091 | * the {@link #forClass(Class) Class instance}. 1092 | * @param instance the instance 1093 | * @return a {@link ResolvableType} for the specified instance 1094 | * @since 4.2 1095 | * @see ResolvableTypeProvider 1096 | */ 1097 | public static ResolvableType forInstance(Object instance) { 1098 | Assert.notNull(instance, "Instance must not be null"); 1099 | if (instance instanceof ResolvableTypeProvider) { 1100 | ResolvableType type = ((ResolvableTypeProvider) instance).getResolvableType(); 1101 | if (type != null) { 1102 | return type; 1103 | } 1104 | } 1105 | return ResolvableType.forClass(instance.getClass()); 1106 | } 1107 | 1108 | /** 1109 | * Return a {@link ResolvableType} for the specified {@link Field}. 1110 | * @param field the source field 1111 | * @return a {@link ResolvableType} for the specified field 1112 | * @see #forField(Field, Class) 1113 | */ 1114 | public static ResolvableType forField(Field field) { 1115 | Assert.notNull(field, "Field must not be null"); 1116 | return forType(null, new FieldTypeProvider(field), null); 1117 | } 1118 | 1119 | /** 1120 | * Return a {@link ResolvableType} for the specified {@link Field} with a given 1121 | * implementation. 1122 | *

Use this variant when the class that declares the field includes generic 1123 | * parameter variables that are satisfied by the implementation class. 1124 | * @param field the source field 1125 | * @param implementationClass the implementation class 1126 | * @return a {@link ResolvableType} for the specified field 1127 | * @see #forField(Field) 1128 | */ 1129 | public static ResolvableType forField(Field field, Class implementationClass) { 1130 | Assert.notNull(field, "Field must not be null"); 1131 | ResolvableType owner = forType(implementationClass).as(field.getDeclaringClass()); 1132 | return forType(null, new FieldTypeProvider(field), owner.asVariableResolver()); 1133 | } 1134 | 1135 | /** 1136 | * Return a {@link ResolvableType} for the specified {@link Field} with a given 1137 | * implementation. 1138 | *

Use this variant when the class that declares the field includes generic 1139 | * parameter variables that are satisfied by the implementation type. 1140 | * @param field the source field 1141 | * @param implementationType the implementation type 1142 | * @return a {@link ResolvableType} for the specified field 1143 | * @see #forField(Field) 1144 | */ 1145 | public static ResolvableType forField(Field field, @Nullable ResolvableType implementationType) { 1146 | Assert.notNull(field, "Field must not be null"); 1147 | ResolvableType owner = (implementationType != null ? implementationType : NONE); 1148 | owner = owner.as(field.getDeclaringClass()); 1149 | return forType(null, new FieldTypeProvider(field), owner.asVariableResolver()); 1150 | } 1151 | 1152 | /** 1153 | * Return a {@link ResolvableType} for the specified {@link Field} with the 1154 | * given nesting level. 1155 | * @param field the source field 1156 | * @param nestingLevel the nesting level (1 for the outer level; 2 for a nested 1157 | * generic type; etc) 1158 | * @see #forField(Field) 1159 | */ 1160 | public static ResolvableType forField(Field field, int nestingLevel) { 1161 | Assert.notNull(field, "Field must not be null"); 1162 | return forType(null, new FieldTypeProvider(field), null).getNested(nestingLevel); 1163 | } 1164 | 1165 | /** 1166 | * Return a {@link ResolvableType} for the specified {@link Field} with a given 1167 | * implementation and the given nesting level. 1168 | *

Use this variant when the class that declares the field includes generic 1169 | * parameter variables that are satisfied by the implementation class. 1170 | * @param field the source field 1171 | * @param nestingLevel the nesting level (1 for the outer level; 2 for a nested 1172 | * generic type; etc) 1173 | * @param implementationClass the implementation class 1174 | * @return a {@link ResolvableType} for the specified field 1175 | * @see #forField(Field) 1176 | */ 1177 | public static ResolvableType forField(Field field, int nestingLevel, @Nullable Class implementationClass) { 1178 | Assert.notNull(field, "Field must not be null"); 1179 | ResolvableType owner = forType(implementationClass).as(field.getDeclaringClass()); 1180 | return forType(null, new FieldTypeProvider(field), owner.asVariableResolver()).getNested(nestingLevel); 1181 | } 1182 | 1183 | /** 1184 | * Return a {@link ResolvableType} for the specified {@link Constructor} parameter. 1185 | * @param constructor the source constructor (must not be {@code null}) 1186 | * @param parameterIndex the parameter index 1187 | * @return a {@link ResolvableType} for the specified constructor parameter 1188 | * @see #forConstructorParameter(Constructor, int, Class) 1189 | */ 1190 | public static ResolvableType forConstructorParameter(Constructor constructor, int parameterIndex) { 1191 | Assert.notNull(constructor, "Constructor must not be null"); 1192 | return forMethodParameter(new MethodParameter(constructor, parameterIndex)); 1193 | } 1194 | 1195 | /** 1196 | * Return a {@link ResolvableType} for the specified {@link Constructor} parameter 1197 | * with a given implementation. Use this variant when the class that declares the 1198 | * constructor includes generic parameter variables that are satisfied by the 1199 | * implementation class. 1200 | * @param constructor the source constructor (must not be {@code null}) 1201 | * @param parameterIndex the parameter index 1202 | * @param implementationClass the implementation class 1203 | * @return a {@link ResolvableType} for the specified constructor parameter 1204 | * @see #forConstructorParameter(Constructor, int) 1205 | */ 1206 | public static ResolvableType forConstructorParameter(Constructor constructor, int parameterIndex, 1207 | Class implementationClass) { 1208 | 1209 | Assert.notNull(constructor, "Constructor must not be null"); 1210 | MethodParameter methodParameter = new MethodParameter(constructor, parameterIndex, implementationClass); 1211 | return forMethodParameter(methodParameter); 1212 | } 1213 | 1214 | /** 1215 | * Return a {@link ResolvableType} for the specified {@link Method} return type. 1216 | * @param method the source for the method return type 1217 | * @return a {@link ResolvableType} for the specified method return 1218 | * @see #forMethodReturnType(Method, Class) 1219 | */ 1220 | public static ResolvableType forMethodReturnType(Method method) { 1221 | Assert.notNull(method, "Method must not be null"); 1222 | return forMethodParameter(new MethodParameter(method, -1)); 1223 | } 1224 | 1225 | /** 1226 | * Return a {@link ResolvableType} for the specified {@link Method} return type. 1227 | * Use this variant when the class that declares the method includes generic 1228 | * parameter variables that are satisfied by the implementation class. 1229 | * @param method the source for the method return type 1230 | * @param implementationClass the implementation class 1231 | * @return a {@link ResolvableType} for the specified method return 1232 | * @see #forMethodReturnType(Method) 1233 | */ 1234 | public static ResolvableType forMethodReturnType(Method method, Class implementationClass) { 1235 | Assert.notNull(method, "Method must not be null"); 1236 | MethodParameter methodParameter = new MethodParameter(method, -1, implementationClass); 1237 | return forMethodParameter(methodParameter); 1238 | } 1239 | 1240 | /** 1241 | * Return a {@link ResolvableType} for the specified {@link Method} parameter. 1242 | * @param method the source method (must not be {@code null}) 1243 | * @param parameterIndex the parameter index 1244 | * @return a {@link ResolvableType} for the specified method parameter 1245 | * @see #forMethodParameter(Method, int, Class) 1246 | * @see #forMethodParameter(MethodParameter) 1247 | */ 1248 | public static ResolvableType forMethodParameter(Method method, int parameterIndex) { 1249 | Assert.notNull(method, "Method must not be null"); 1250 | return forMethodParameter(new MethodParameter(method, parameterIndex)); 1251 | } 1252 | 1253 | /** 1254 | * Return a {@link ResolvableType} for the specified {@link Method} parameter with a 1255 | * given implementation. Use this variant when the class that declares the method 1256 | * includes generic parameter variables that are satisfied by the implementation class. 1257 | * @param method the source method (must not be {@code null}) 1258 | * @param parameterIndex the parameter index 1259 | * @param implementationClass the implementation class 1260 | * @return a {@link ResolvableType} for the specified method parameter 1261 | * @see #forMethodParameter(Method, int, Class) 1262 | * @see #forMethodParameter(MethodParameter) 1263 | */ 1264 | public static ResolvableType forMethodParameter(Method method, int parameterIndex, Class implementationClass) { 1265 | Assert.notNull(method, "Method must not be null"); 1266 | MethodParameter methodParameter = new MethodParameter(method, parameterIndex, implementationClass); 1267 | return forMethodParameter(methodParameter); 1268 | } 1269 | 1270 | /** 1271 | * Return a {@link ResolvableType} for the specified {@link MethodParameter}. 1272 | * @param methodParameter the source method parameter (must not be {@code null}) 1273 | * @return a {@link ResolvableType} for the specified method parameter 1274 | * @see #forMethodParameter(Method, int) 1275 | */ 1276 | public static ResolvableType forMethodParameter(MethodParameter methodParameter) { 1277 | return forMethodParameter(methodParameter, (Type) null); 1278 | } 1279 | 1280 | /** 1281 | * Return a {@link ResolvableType} for the specified {@link MethodParameter} with a 1282 | * given implementation type. Use this variant when the class that declares the method 1283 | * includes generic parameter variables that are satisfied by the implementation type. 1284 | * @param methodParameter the source method parameter (must not be {@code null}) 1285 | * @param implementationType the implementation type 1286 | * @return a {@link ResolvableType} for the specified method parameter 1287 | * @see #forMethodParameter(MethodParameter) 1288 | */ 1289 | public static ResolvableType forMethodParameter(MethodParameter methodParameter, 1290 | @Nullable ResolvableType implementationType) { 1291 | 1292 | Assert.notNull(methodParameter, "MethodParameter must not be null"); 1293 | implementationType = (implementationType != null ? implementationType : 1294 | forType(methodParameter.getContainingClass())); 1295 | ResolvableType owner = implementationType.as(methodParameter.getDeclaringClass()); 1296 | return forType(null, new MethodParameterTypeProvider(methodParameter), owner.asVariableResolver()). 1297 | getNested(methodParameter.getNestingLevel(), methodParameter.typeIndexesPerLevel); 1298 | } 1299 | 1300 | /** 1301 | * Return a {@link ResolvableType} for the specified {@link MethodParameter}, 1302 | * overriding the target type to resolve with a specific given type. 1303 | * @param methodParameter the source method parameter (must not be {@code null}) 1304 | * @param targetType the type to resolve (a part of the method parameter's type) 1305 | * @return a {@link ResolvableType} for the specified method parameter 1306 | * @see #forMethodParameter(Method, int) 1307 | */ 1308 | public static ResolvableType forMethodParameter(MethodParameter methodParameter, @Nullable Type targetType) { 1309 | Assert.notNull(methodParameter, "MethodParameter must not be null"); 1310 | return forMethodParameter(methodParameter, targetType, methodParameter.getNestingLevel()); 1311 | } 1312 | 1313 | /** 1314 | * Return a {@link ResolvableType} for the specified {@link MethodParameter} at 1315 | * a specific nesting level, overriding the target type to resolve with a specific 1316 | * given type. 1317 | * @param methodParameter the source method parameter (must not be {@code null}) 1318 | * @param targetType the type to resolve (a part of the method parameter's type) 1319 | * @param nestingLevel the nesting level to use 1320 | * @return a {@link ResolvableType} for the specified method parameter 1321 | * @since 5.2 1322 | * @see #forMethodParameter(Method, int) 1323 | */ 1324 | static ResolvableType forMethodParameter( 1325 | MethodParameter methodParameter, @Nullable Type targetType, int nestingLevel) { 1326 | 1327 | ResolvableType owner = forType(methodParameter.getContainingClass()).as(methodParameter.getDeclaringClass()); 1328 | return forType(targetType, new MethodParameterTypeProvider(methodParameter), owner.asVariableResolver()). 1329 | getNested(nestingLevel, methodParameter.typeIndexesPerLevel); 1330 | } 1331 | 1332 | /** 1333 | * Return a {@link ResolvableType} as a array of the specified {@code componentType}. 1334 | * @param componentType the component type 1335 | * @return a {@link ResolvableType} as an array of the specified component type 1336 | */ 1337 | public static ResolvableType forArrayComponent(ResolvableType componentType) { 1338 | Assert.notNull(componentType, "Component type must not be null"); 1339 | Class arrayClass = Array.newInstance(componentType.resolve(), 0).getClass(); 1340 | return new ResolvableType(arrayClass, null, null, componentType); 1341 | } 1342 | 1343 | /** 1344 | * Return a {@link ResolvableType} for the specified {@link Type}. 1345 | *

Note: The resulting {@link ResolvableType} instance may not be {@link Serializable}. 1346 | * @param type the source type (potentially {@code null}) 1347 | * @return a {@link ResolvableType} for the specified {@link Type} 1348 | * @see #forType(Type, ResolvableType) 1349 | */ 1350 | public static ResolvableType forType(@Nullable Type type) { 1351 | return forType(type, null, null); 1352 | } 1353 | 1354 | /** 1355 | * Return a {@link ResolvableType} for the specified {@link Type} backed by the given 1356 | * owner type. 1357 | *

Note: The resulting {@link ResolvableType} instance may not be {@link Serializable}. 1358 | * @param type the source type or {@code null} 1359 | * @param owner the owner type used to resolve variables 1360 | * @return a {@link ResolvableType} for the specified {@link Type} and owner 1361 | * @see #forType(Type) 1362 | */ 1363 | public static ResolvableType forType(@Nullable Type type, @Nullable ResolvableType owner) { 1364 | VariableResolver variableResolver = null; 1365 | if (owner != null) { 1366 | variableResolver = owner.asVariableResolver(); 1367 | } 1368 | return forType(type, variableResolver); 1369 | } 1370 | 1371 | 1372 | /** 1373 | * Return a {@link ResolvableType} for the specified {@link ParameterizedTypeReference}. 1374 | *

Note: The resulting {@link ResolvableType} instance may not be {@link Serializable}. 1375 | * @param typeReference the reference to obtain the source type from 1376 | * @return a {@link ResolvableType} for the specified {@link ParameterizedTypeReference} 1377 | * @since 4.3.12 1378 | * @see #forType(Type) 1379 | */ 1380 | public static ResolvableType forType(ParameterizedTypeReference typeReference) { 1381 | return forType(typeReference.getType(), null, null); 1382 | } 1383 | 1384 | /** 1385 | * Return a {@link ResolvableType} for the specified {@link Type} backed by a given 1386 | * {@link VariableResolver}. 1387 | * @param type the source type or {@code null} 1388 | * @param variableResolver the variable resolver or {@code null} 1389 | * @return a {@link ResolvableType} for the specified {@link Type} and {@link VariableResolver} 1390 | */ 1391 | static ResolvableType forType(@Nullable Type type, @Nullable VariableResolver variableResolver) { 1392 | return forType(type, null, variableResolver); 1393 | } 1394 | 1395 | /** 1396 | * Return a {@link ResolvableType} for the specified {@link Type} backed by a given 1397 | * {@link VariableResolver}. 1398 | * @param type the source type or {@code null} 1399 | * @param typeProvider the type provider or {@code null} 1400 | * @param variableResolver the variable resolver or {@code null} 1401 | * @return a {@link ResolvableType} for the specified {@link Type} and {@link VariableResolver} 1402 | */ 1403 | static ResolvableType forType( 1404 | @Nullable Type type, @Nullable TypeProvider typeProvider, @Nullable VariableResolver variableResolver) { 1405 | 1406 | if (type == null && typeProvider != null) { 1407 | type = SerializableTypeWrapper.forTypeProvider(typeProvider); 1408 | } 1409 | if (type == null) { 1410 | return NONE; 1411 | } 1412 | 1413 | // For simple Class references, build the wrapper right away - 1414 | // no expensive resolution necessary, so not worth caching... 1415 | if (type instanceof Class) { 1416 | return new ResolvableType(type, typeProvider, variableResolver, (ResolvableType) null); 1417 | } 1418 | 1419 | // Purge empty entries on access since we don't have a clean-up thread or the like. 1420 | cache.purgeUnreferencedEntries(); 1421 | 1422 | // Check the cache - we may have a ResolvableType which has been resolved before... 1423 | ResolvableType resultType = new ResolvableType(type, typeProvider, variableResolver); 1424 | ResolvableType cachedType = cache.get(resultType); 1425 | if (cachedType == null) { 1426 | cachedType = new ResolvableType(type, typeProvider, variableResolver, resultType.hash); 1427 | cache.put(cachedType, cachedType); 1428 | } 1429 | resultType.resolved = cachedType.resolved; 1430 | return resultType; 1431 | } 1432 | 1433 | /** 1434 | * Clear the internal {@code ResolvableType}/{@code SerializableTypeWrapper} cache. 1435 | * @since 4.2 1436 | */ 1437 | public static void clearCache() { 1438 | cache.clear(); 1439 | SerializableTypeWrapper.cache.clear(); 1440 | } 1441 | 1442 | 1443 | /** 1444 | * Strategy interface used to resolve {@link TypeVariable TypeVariables}. 1445 | */ 1446 | interface VariableResolver extends Serializable { 1447 | 1448 | /** 1449 | * Return the source of the resolver (used for hashCode and equals). 1450 | */ 1451 | Object getSource(); 1452 | 1453 | /** 1454 | * Resolve the specified variable. 1455 | * @param variable the variable to resolve 1456 | * @return the resolved variable, or {@code null} if not found 1457 | */ 1458 | @Nullable 1459 | ResolvableType resolveVariable(TypeVariable variable); 1460 | } 1461 | 1462 | 1463 | @SuppressWarnings("serial") 1464 | private static class DefaultVariableResolver implements VariableResolver { 1465 | 1466 | private final ResolvableType source; 1467 | 1468 | DefaultVariableResolver(ResolvableType resolvableType) { 1469 | this.source = resolvableType; 1470 | } 1471 | 1472 | @Override 1473 | @Nullable 1474 | public ResolvableType resolveVariable(TypeVariable variable) { 1475 | return this.source.resolveVariable(variable); 1476 | } 1477 | 1478 | @Override 1479 | public Object getSource() { 1480 | return this.source; 1481 | } 1482 | } 1483 | 1484 | 1485 | @SuppressWarnings("serial") 1486 | private static class TypeVariablesVariableResolver implements VariableResolver { 1487 | 1488 | private final TypeVariable[] variables; 1489 | 1490 | private final ResolvableType[] generics; 1491 | 1492 | public TypeVariablesVariableResolver(TypeVariable[] variables, ResolvableType[] generics) { 1493 | this.variables = variables; 1494 | this.generics = generics; 1495 | } 1496 | 1497 | @Override 1498 | @Nullable 1499 | public ResolvableType resolveVariable(TypeVariable variable) { 1500 | TypeVariable variableToCompare = SerializableTypeWrapper.unwrap(variable); 1501 | for (int i = 0; i < this.variables.length; i++) { 1502 | TypeVariable resolvedVariable = SerializableTypeWrapper.unwrap(this.variables[i]); 1503 | if (ObjectUtils.nullSafeEquals(resolvedVariable, variableToCompare)) { 1504 | return this.generics[i]; 1505 | } 1506 | } 1507 | return null; 1508 | } 1509 | 1510 | @Override 1511 | public Object getSource() { 1512 | return this.generics; 1513 | } 1514 | } 1515 | 1516 | 1517 | private static final class SyntheticParameterizedType implements ParameterizedType, Serializable { 1518 | 1519 | private final Type rawType; 1520 | 1521 | private final Type[] typeArguments; 1522 | 1523 | public SyntheticParameterizedType(Type rawType, Type[] typeArguments) { 1524 | this.rawType = rawType; 1525 | this.typeArguments = typeArguments; 1526 | } 1527 | 1528 | @Override 1529 | public String getTypeName() { 1530 | String typeName = this.rawType.getTypeName(); 1531 | if (this.typeArguments.length > 0) { 1532 | StringJoiner stringJoiner = new StringJoiner(", ", "<", ">"); 1533 | for (Type argument : this.typeArguments) { 1534 | stringJoiner.add(argument.getTypeName()); 1535 | } 1536 | return typeName + stringJoiner; 1537 | } 1538 | return typeName; 1539 | } 1540 | 1541 | @Override 1542 | @Nullable 1543 | public Type getOwnerType() { 1544 | return null; 1545 | } 1546 | 1547 | @Override 1548 | public Type getRawType() { 1549 | return this.rawType; 1550 | } 1551 | 1552 | @Override 1553 | public Type[] getActualTypeArguments() { 1554 | return this.typeArguments; 1555 | } 1556 | 1557 | @Override 1558 | public boolean equals(@Nullable Object other) { 1559 | if (this == other) { 1560 | return true; 1561 | } 1562 | if (!(other instanceof ParameterizedType)) { 1563 | return false; 1564 | } 1565 | ParameterizedType otherType = (ParameterizedType) other; 1566 | return (otherType.getOwnerType() == null && this.rawType.equals(otherType.getRawType()) && 1567 | Arrays.equals(this.typeArguments, otherType.getActualTypeArguments())); 1568 | } 1569 | 1570 | @Override 1571 | public int hashCode() { 1572 | return (this.rawType.hashCode() * 31 + Arrays.hashCode(this.typeArguments)); 1573 | } 1574 | 1575 | @Override 1576 | public String toString() { 1577 | return getTypeName(); 1578 | } 1579 | } 1580 | 1581 | 1582 | /** 1583 | * Internal helper to handle bounds from {@link WildcardType WildcardTypes}. 1584 | */ 1585 | private static class WildcardBounds { 1586 | 1587 | private final Kind kind; 1588 | 1589 | private final ResolvableType[] bounds; 1590 | 1591 | /** 1592 | * Internal constructor to create a new {@link WildcardBounds} instance. 1593 | * @param kind the kind of bounds 1594 | * @param bounds the bounds 1595 | * @see #get(ResolvableType) 1596 | */ 1597 | public WildcardBounds(Kind kind, ResolvableType[] bounds) { 1598 | this.kind = kind; 1599 | this.bounds = bounds; 1600 | } 1601 | 1602 | /** 1603 | * Return {@code true} if this bounds is the same kind as the specified bounds. 1604 | */ 1605 | public boolean isSameKind(WildcardBounds bounds) { 1606 | return this.kind == bounds.kind; 1607 | } 1608 | 1609 | /** 1610 | * Return {@code true} if this bounds is assignable to all the specified types. 1611 | * @param types the types to test against 1612 | * @return {@code true} if this bounds is assignable to all types 1613 | */ 1614 | public boolean isAssignableFrom(ResolvableType... types) { 1615 | for (ResolvableType bound : this.bounds) { 1616 | for (ResolvableType type : types) { 1617 | if (!isAssignable(bound, type)) { 1618 | return false; 1619 | } 1620 | } 1621 | } 1622 | return true; 1623 | } 1624 | 1625 | private boolean isAssignable(ResolvableType source, ResolvableType from) { 1626 | return (this.kind == Kind.UPPER ? source.isAssignableFrom(from) : from.isAssignableFrom(source)); 1627 | } 1628 | 1629 | /** 1630 | * Return the underlying bounds. 1631 | */ 1632 | public ResolvableType[] getBounds() { 1633 | return this.bounds; 1634 | } 1635 | 1636 | /** 1637 | * Get a {@link WildcardBounds} instance for the specified type, returning 1638 | * {@code null} if the specified type cannot be resolved to a {@link WildcardType}. 1639 | * @param type the source type 1640 | * @return a {@link WildcardBounds} instance or {@code null} 1641 | */ 1642 | @Nullable 1643 | public static WildcardBounds get(ResolvableType type) { 1644 | ResolvableType resolveToWildcard = type; 1645 | while (!(resolveToWildcard.getType() instanceof WildcardType)) { 1646 | if (resolveToWildcard == NONE) { 1647 | return null; 1648 | } 1649 | resolveToWildcard = resolveToWildcard.resolveType(); 1650 | } 1651 | WildcardType wildcardType = (WildcardType) resolveToWildcard.type; 1652 | Kind boundsType = (wildcardType.getLowerBounds().length > 0 ? Kind.LOWER : Kind.UPPER); 1653 | Type[] bounds = (boundsType == Kind.UPPER ? wildcardType.getUpperBounds() : wildcardType.getLowerBounds()); 1654 | ResolvableType[] resolvableBounds = new ResolvableType[bounds.length]; 1655 | for (int i = 0; i < bounds.length; i++) { 1656 | resolvableBounds[i] = ResolvableType.forType(bounds[i], type.variableResolver); 1657 | } 1658 | return new WildcardBounds(boundsType, resolvableBounds); 1659 | } 1660 | 1661 | /** 1662 | * The various kinds of bounds. 1663 | */ 1664 | enum Kind {UPPER, LOWER} 1665 | } 1666 | 1667 | 1668 | /** 1669 | * Internal {@link Type} used to represent an empty value. 1670 | */ 1671 | @SuppressWarnings("serial") 1672 | static class EmptyType implements Type, Serializable { 1673 | 1674 | static final Type INSTANCE = new EmptyType(); 1675 | 1676 | Object readResolve() { 1677 | return INSTANCE; 1678 | } 1679 | } 1680 | 1681 | } 1682 | -------------------------------------------------------------------------------- /src/main/resources/interview.sql: -------------------------------------------------------------------------------- 1 | -- SQL execute link >> https://onecompiler.com/mysql/3y92m8sqt 2 | 3 | -- create 4 | CREATE TABLE course ( 5 | id BIGINT PRIMARY KEY, 6 | name VARCHAR(64) NOT NULL 7 | ); 8 | CREATE TABLE student ( 9 | id BIGINT PRIMARY KEY, 10 | name VARCHAR(64) NOT NULL 11 | ); 12 | CREATE TABLE student_score ( 13 | id BIGINT PRIMARY KEY, 14 | student_id BIGINT NOT NULL, 15 | course_id BIGINT NOT NULL, 16 | score FLOAT NOT NULL 17 | ); 18 | 19 | -- insert 20 | INSERT INTO course VALUES (1, '语文'); 21 | INSERT INTO course VALUES (2, '数学'); 22 | INSERT INTO course VALUES (3, '英语'); 23 | 24 | INSERT INTO student VALUES (1, '小李'); 25 | INSERT INTO student VALUES (2, '小马'); 26 | INSERT INTO student VALUES (3, '小王'); 27 | INSERT INTO student VALUES (4, '小张'); 28 | 29 | -- 小李 30 | INSERT INTO student_score VALUES (1, 1, 1, 80); 31 | INSERT INTO student_score VALUES (2, 1, 2, 90); 32 | INSERT INTO student_score VALUES (3, 1, 3, 70); 33 | 34 | -- 小马 35 | INSERT INTO student_score VALUES (4, 2, 1, 70); 36 | INSERT INTO student_score VALUES (5, 2, 2, 90); 37 | INSERT INTO student_score VALUES (6, 2, 3, 90); 38 | 39 | -- 小王 40 | INSERT INTO student_score VALUES (7, 3, 1, 80); 41 | INSERT INTO student_score VALUES (8, 3, 2, 90); 42 | INSERT INTO student_score VALUES (9, 3, 3, 90); 43 | 44 | -- 小张 45 | INSERT INTO student_score VALUES (10, 4, 1, 70); 46 | INSERT INTO student_score VALUES (11, 4, 2, 80); 47 | INSERT INTO student_score VALUES (12, 4, 3, 80); 48 | 49 | 50 | -- fetch 51 | -- SELECT * FROM student_score ORDER BY course_id DESC; 52 | -- 视频中的SQL 53 | SELECT c.name AS 课程,t.score AS 最高分分数,s.name AS 学生名 FROM 54 | (SELECT MAX(score) score, course_id FROM student_score ss 55 | GROUP BY course_id) t 56 | RIGHT JOIN 57 | student_score sc 58 | ON sc.score = t.score AND sc.course_id = t.course_id 59 | LEFT JOIN 60 | student s 61 | ON s.id = sc.student_id 62 | LEFT JOIN 63 | course c 64 | ON c.id = sc.course_id 65 | ORDER BY c.id DESC; 66 | 67 | 68 | -- 下面是正确答案 69 | SELECT c.name AS 课程,t.score AS 最高分分数,s.name AS 学生名 FROM 70 | (SELECT MAX(score) score, course_id FROM student_score ss 71 | GROUP BY course_id) t 72 | LEFT JOIN -- 这里换成LEFT即可 73 | student_score sc 74 | ON sc.score = t.score AND sc.course_id = t.course_id 75 | LEFT JOIN 76 | student s 77 | ON s.id = sc.student_id 78 | LEFT JOIN 79 | course c 80 | ON c.id = sc.course_id 81 | ORDER BY c.id DESC; --------------------------------------------------------------------------------