001/* 002 * Copyright (c) 2022-2025, Mybatis-Flex (fuhai999@gmail.com). 003 * <p> 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 * <p> 008 * http://www.apache.org/licenses/LICENSE-2.0 009 * <p> 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 */ 016package com.mybatisflex.core.util; 017 018import com.mybatisflex.annotation.Column; 019import org.apache.ibatis.reflection.Reflector; 020 021import java.lang.reflect.*; 022import java.util.Collection; 023import java.util.Map; 024import java.util.concurrent.ConcurrentHashMap; 025 026public class FieldWrapper { 027 028 public static Map<Class<?>, Map<String, FieldWrapper>> cache = new ConcurrentHashMap<>(); 029 030 private Field field; 031 private boolean isIgnore = false; 032 private Class<?> fieldType; 033 private Class<?> mappingType; 034 private Class<?> keyType; 035 private Method getterMethod; 036 private Method setterMethod; 037 038 public static FieldWrapper of(Class<?> clazz, String fieldName) { 039 Map<String, FieldWrapper> wrapperMap = cache.get(clazz); 040 if (wrapperMap == null) { 041 synchronized (clazz) { 042 wrapperMap = cache.get(clazz); 043 if (wrapperMap == null) { 044 wrapperMap = new ConcurrentHashMap<>(); 045 cache.put(clazz, wrapperMap); 046 } 047 } 048 } 049 050 FieldWrapper fieldWrapper = wrapperMap.get(fieldName); 051 if (fieldWrapper == null) { 052 synchronized (clazz) { 053 fieldWrapper = wrapperMap.get(fieldName); 054 if (fieldWrapper == null) { 055 Field findField = ClassUtil.getFirstField(clazz, field -> field.getName().equalsIgnoreCase(fieldName)); 056 if (findField == null) { 057 throw new IllegalStateException("Can not find field \"" + fieldName + "\" in class: " + clazz.getName()); 058 } 059 060 String setterName = "set" + StringUtil.firstCharToUpperCase(findField.getName()); 061 Method setter = ClassUtil.getFirstMethod(clazz, method -> 062 method.getParameterCount() == 1 063 && Modifier.isPublic(method.getModifiers()) 064 && method.getName().equals(setterName)); 065 066 fieldWrapper = new FieldWrapper(); 067 fieldWrapper.field = findField; 068 fieldWrapper.fieldType = findField.getType(); 069 initMappingTypeAndKeyType(clazz, findField, fieldWrapper); 070 071 Column column = findField.getAnnotation(Column.class); 072 if (column != null && column.ignore()) { 073 fieldWrapper.isIgnore = true; 074 } 075 076 fieldWrapper.setterMethod = setter; 077 078 String[] getterNames = new String[]{"get" + StringUtil.firstCharToUpperCase(findField.getName()), "is" + StringUtil.firstCharToUpperCase(findField.getName())}; 079 fieldWrapper.getterMethod = ClassUtil.getFirstMethod(clazz, method -> method.getParameterCount() == 0 080 && Modifier.isPublic(method.getModifiers()) 081 && ArrayUtil.contains(getterNames, method.getName())); 082 083 wrapperMap.put(fieldName, fieldWrapper); 084 } 085 } 086 } 087 088 return fieldWrapper; 089 } 090 091 private static void initMappingTypeAndKeyType(Class<?> clazz, Field field, FieldWrapper fieldWrapper) { 092 Reflector reflector = Reflectors.of(clazz); 093 Class<?> fieldType = reflector.getGetterType(field.getName()); 094 095 if (Collection.class.isAssignableFrom(fieldType)) { 096 Type genericType = field.getGenericType(); 097 try{ 098 //使用Kotlin时,若Collection泛型类型为协变类型时(如 List<out E>),并且传入的具体泛型类型为open,genericType的具体类型为WildcardType(? extends ExampleClass),该类型不可转为Class(无具体类型),使用instanceOf会抛出转换异常。 099 //该错误目前只在以上情况中发现 100 if (genericType instanceof ParameterizedType) { 101 Type actualTypeArgument = ((ParameterizedType) genericType).getActualTypeArguments()[0]; 102 fieldWrapper.mappingType = (Class<?>) actualTypeArgument; 103 } 104 }catch (ClassCastException e){ 105 throw new UnsupportedOperationException(String.format("不支持使用[%s]作为集合类型。请使用MutableList",fieldType.getName())); 106 } 107 } else if (Map.class.isAssignableFrom(fieldType)) { 108 Type genericType = field.getGenericType(); 109 if (genericType instanceof ParameterizedType) { 110 fieldWrapper.keyType = (Class<?>) ((ParameterizedType) genericType).getActualTypeArguments()[0]; 111 Type actualTypeArgument = ((ParameterizedType) genericType).getActualTypeArguments()[1]; 112 if (actualTypeArgument instanceof ParameterizedType) { 113 fieldWrapper.mappingType = (Class<?>) ((ParameterizedType) actualTypeArgument).getRawType(); 114 } else { 115 fieldWrapper.mappingType = (Class<?>) actualTypeArgument; 116 } 117 } 118 } else { 119 fieldWrapper.mappingType = fieldType; 120 } 121 } 122 123 124 public void set(Object value, Object to) { 125 try { 126 if (setterMethod == null) { 127 throw new IllegalStateException("Can not find method \"set" + StringUtil.firstCharToUpperCase(field.getName()) + "\" in class: " + to.getClass().getName()); 128 } 129 setterMethod.invoke(to, value); 130 } catch (Exception e) { 131 throw new RuntimeException(e); 132 } 133 } 134 135 public Object get(Object target) { 136 try { 137 if (getterMethod == null) { 138 throw new IllegalStateException("Can not find method \"get" + StringUtil.firstCharToUpperCase(field.getName()) + ", is" 139 + StringUtil.firstCharToUpperCase(field.getName()) + "\" in class: " + target.getClass().getName()); 140 } 141 return getterMethod.invoke(target); 142 } catch (Exception e) { 143 throw new RuntimeException(e); 144 } 145 } 146 147 public Class<?> getFieldType() { 148 return fieldType; 149 } 150 151 public Class<?> getMappingType() { 152 return mappingType; 153 } 154 155 public Class<?> getKeyType() { 156 return keyType; 157 } 158 159 public Field getField() { 160 return field; 161 } 162 163 public boolean isIgnore() { 164 return isIgnore; 165 } 166}