001/* 002 * Copyright (c) 2022-2023, 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 018 019import org.apache.ibatis.javassist.util.proxy.ProxyObject; 020 021import java.lang.reflect.Constructor; 022import java.lang.reflect.Field; 023import java.lang.reflect.Method; 024import java.lang.reflect.Modifier; 025import java.lang.reflect.Proxy; 026import java.util.ArrayList; 027import java.util.Arrays; 028import java.util.List; 029import java.util.function.Predicate; 030 031/** 032 * 类实例创建者创建者 033 * 034 * @author michael 035 * @date 17/3/21 036 */ 037@SuppressWarnings("unchecked") 038public class ClassUtil { 039 040 private ClassUtil() { 041 } 042 043 private static final String[] OBJECT_METHODS = new String[]{ 044 "toString", 045 "getClass", 046 "equals", 047 "hashCode", 048 "wait", 049 "notify", 050 "notifyAll", 051 "clone", 052 "finalize" 053 }; 054 055 //proxy frameworks 056 private static final List<String> PROXY_CLASS_NAMES = Arrays.asList("net.sf.cglib.proxy.Factory" 057 // cglib 058 , "org.springframework.cglib.proxy.Factory" 059 060 // javassist 061 , "javassist.util.proxy.ProxyObject" 062 , "org.apache.ibatis.javassist.util.proxy.ProxyObject"); 063 private static final String ENHANCER_BY = "$$EnhancerBy"; 064 private static final String JAVASSIST_BY = "_$$_"; 065 066 public static boolean isProxy(Class<?> clazz) { 067 for (Class<?> cls : clazz.getInterfaces()) { 068 if (PROXY_CLASS_NAMES.contains(cls.getName())) { 069 return true; 070 } 071 } 072 //java proxy 073 return Proxy.isProxyClass(clazz); 074 } 075 076 public static <T> Class<T> getUsefulClass(Class<T> clazz) { 077 078 if (ProxyObject.class.isAssignableFrom(clazz)) { 079 return (Class<T>) clazz.getSuperclass(); 080 } 081 082 if (isProxy(clazz)) { 083 return getJdkProxySuperClass(clazz); 084 } 085 086 //ControllerTest$ServiceTest$$EnhancerByGuice$$40471411#hello -------> Guice 087 //com.demo.blog.Blog$$EnhancerByCGLIB$$69a17158 ----> CGLIB 088 //io.jboot.test.app.TestAppListener_$$_jvstb9f_0 ------> javassist 089 final String name = clazz.getName(); 090 if (name.contains(ENHANCER_BY) || name.contains(JAVASSIST_BY)) { 091 return (Class<T>) clazz.getSuperclass(); 092 } 093 094 return clazz; 095 } 096 097 098 public static Class<?> getWrapType(Class<?> clazz) { 099 if (clazz == null || !clazz.isPrimitive()) { 100 return clazz; 101 } 102 if (clazz == Integer.TYPE) { 103 return Integer.class; 104 } else if (clazz == Long.TYPE) { 105 return Long.class; 106 } else if (clazz == Boolean.TYPE) { 107 return Boolean.class; 108 } else if (clazz == Float.TYPE) { 109 return Float.class; 110 } else if (clazz == Double.TYPE) { 111 return Double.class; 112 } else if (clazz == Short.TYPE) { 113 return Short.class; 114 } else if (clazz == Character.TYPE) { 115 return Character.class; 116 } else if (clazz == Byte.TYPE) { 117 return Byte.class; 118 } else if (clazz == Void.TYPE) { 119 return Void.class; 120 } 121 return clazz; 122 } 123 124 125 public static boolean isArray(Class<?> clazz) { 126 return clazz.isArray() 127 || clazz == int[].class 128 || clazz == long[].class 129 || clazz == short[].class 130 || clazz == float[].class 131 || clazz == double[].class; 132 } 133 134 public static boolean canInstance(int mod) { 135 return !Modifier.isAbstract(mod) || !Modifier.isInterface(mod); 136 } 137 138 139 public static <T> T newInstance(Class<T> clazz) { 140 try { 141 Constructor<?> defaultConstructor = null; 142 Constructor<?> otherConstructor = null; 143 144 Constructor<?>[] declaredConstructors = clazz.getDeclaredConstructors(); 145 for (Constructor<?> constructor : declaredConstructors) { 146 if (constructor.getParameterCount() == 0 && Modifier.isPublic(constructor.getModifiers())) { 147 defaultConstructor = constructor; 148 } else if (Modifier.isPublic(constructor.getModifiers())) { 149 otherConstructor = constructor; 150 } 151 } 152 if (defaultConstructor != null) { 153 return (T) defaultConstructor.newInstance(); 154 } else if (otherConstructor != null) { 155 Class<?>[] parameterTypes = otherConstructor.getParameterTypes(); 156 Object[] parameters = new Object[parameterTypes.length]; 157 for (int i = 0; i < parameterTypes.length; i++) { 158 if (parameterTypes[i].isPrimitive()) { 159 parameters[i] = ConvertUtil.getPrimitiveDefaultValue(parameterTypes[i]); 160 } else { 161 parameters[i] = null; 162 } 163 } 164 return (T) otherConstructor.newInstance(parameters); 165 } 166 // 没有任何构造函数的情况下,去查找 static 工厂方法,满足 lombok 注解的需求 167 else { 168 Method factoryMethod = ClassUtil.getFirstMethod(clazz, m -> m.getParameterCount() == 0 169 && clazz == m.getReturnType() 170 && Modifier.isPublic(m.getModifiers()) 171 && Modifier.isStatic(m.getModifiers())); 172 if (factoryMethod != null) { 173 return (T) factoryMethod.invoke(null); 174 } 175 } 176 throw new IllegalArgumentException("the class \"" + clazz.getName() + "\" has no constructor."); 177 } catch (Exception e) { 178 throw new RuntimeException("Can not newInstance class: " + clazz.getName(), e); 179 } 180 } 181 182 183 public static <T> T newInstance(Class<T> clazz, Object... paras) { 184 try { 185 Constructor<?>[] constructors = clazz.getDeclaredConstructors(); 186 for (Constructor<?> constructor : constructors) { 187 if (isMatchedParas(constructor, paras)) { 188 Object ret = constructor.newInstance(paras); 189 return (T) ret; 190 } 191 } 192 throw new IllegalArgumentException("Can not find constructor by paras: \"" + Arrays.toString(paras) + "\" in class[" + clazz.getName() + "]"); 193 } catch (Exception e) { 194 e.printStackTrace(); 195 } 196 197 return null; 198 } 199 200 201 private static boolean isMatchedParas(Constructor<?> constructor, Object[] paras) { 202 if (constructor.getParameterCount() == 0) { 203 return paras == null || paras.length == 0; 204 } 205 206 if (constructor.getParameterCount() > 0 207 && (paras == null || paras.length != constructor.getParameterCount())) { 208 return false; 209 } 210 211 Class<?>[] parameterTypes = constructor.getParameterTypes(); 212 for (int i = 0; i < parameterTypes.length; i++) { 213 Class<?> parameterType = parameterTypes[i]; 214 Object paraObject = paras[i]; 215 if (paraObject != null && !parameterType.isAssignableFrom(paraObject.getClass())) { 216 return false; 217 } 218 } 219 220 return true; 221 } 222 223 224 public static List<Field> getAllFields(Class<?> clazz) { 225 List<Field> fields = new ArrayList<>(); 226 doGetFields(clazz, fields, null, false); 227 return fields; 228 } 229 230 public static List<Field> getAllFields(Class<?> clazz, Predicate<Field> predicate) { 231 List<Field> fields = new ArrayList<>(); 232 doGetFields(clazz, fields, predicate, false); 233 return fields; 234 } 235 236 public static Field getFirstField(Class<?> clazz, Predicate<Field> predicate) { 237 List<Field> fields = new ArrayList<>(); 238 doGetFields(clazz, fields, predicate, true); 239 return fields.isEmpty() ? null : fields.get(0); 240 } 241 242 private static void doGetFields(Class<?> clazz, List<Field> fields, Predicate<Field> predicate, boolean firstOnly) { 243 if (clazz == null || clazz == Object.class) { 244 return; 245 } 246 247 Field[] declaredFields = clazz.getDeclaredFields(); 248 for (Field declaredField : declaredFields) { 249 if (predicate == null || predicate.test(declaredField)) { 250 fields.add(declaredField); 251 if (firstOnly) { 252 break; 253 } 254 } 255 } 256 257 if (firstOnly && !fields.isEmpty()) { 258 return; 259 } 260 261 doGetFields(clazz.getSuperclass(), fields, predicate, firstOnly); 262 } 263 264 public static List<Method> getAllMethods(Class<?> clazz) { 265 List<Method> methods = new ArrayList<>(); 266 doGetMethods(clazz, methods, null, false); 267 return methods; 268 } 269 270 public static List<Method> getAllMethods(Class<?> clazz, Predicate<Method> predicate) { 271 List<Method> methods = new ArrayList<>(); 272 doGetMethods(clazz, methods, predicate, false); 273 return methods; 274 } 275 276 public static Method getAnyMethod(Class<?> clazz, String... methodNames) { 277 return getFirstMethod(clazz, method -> ArrayUtil.contains(methodNames, method.getName())); 278 } 279 280 public static Method getFirstMethod(Class<?> clazz, Predicate<Method> predicate) { 281 List<Method> methods = new ArrayList<>(); 282 doGetMethods(clazz, methods, predicate, true); 283 return methods.isEmpty() ? null : methods.get(0); 284 } 285 286 287 private static void doGetMethods(Class<?> clazz, List<Method> methods, Predicate<Method> predicate, boolean firstOnly) { 288 if (clazz == null || clazz == Object.class) { 289 return; 290 } 291 292 Method[] declaredMethods = clazz.getDeclaredMethods(); 293 if (clazz.isInterface()) { 294 for (Method method : declaredMethods) { 295 // 接口类只需要获取 default 方法 296 if (method.isDefault() && (predicate == null || predicate.test(method))) { 297 methods.add(method); 298 if (firstOnly) { 299 break; 300 } 301 } 302 } 303 } else { 304 for (Method method : declaredMethods) { 305 if (predicate == null || predicate.test(method)) { 306 methods.add(method); 307 if (firstOnly) { 308 break; 309 } 310 } 311 } 312 } 313 314 315 if (firstOnly && !methods.isEmpty()) { 316 return; 317 } 318 319 Class<?>[] interfaces = clazz.getInterfaces(); 320 for (Class<?> anInterface : interfaces) { 321 doGetMethods(anInterface, methods, predicate, firstOnly); 322 } 323 324 doGetMethods(clazz.getSuperclass(), methods, predicate, firstOnly); 325 } 326 327 328 private static <T> Class<T> getJdkProxySuperClass(Class<T> clazz) { 329 final Class<?> proxyClass = Proxy.getProxyClass(clazz.getClassLoader(), clazz.getInterfaces()); 330 return (Class<T>) proxyClass.getInterfaces()[0]; 331 } 332 333 334 public static boolean isGetterMethod(Method method, String property) { 335 String methodName = method.getName(); 336 if (methodName.startsWith("get") && methodName.length() > 3) { 337 return StringUtil.firstCharToUpperCase(property).equals(methodName.substring(3)); 338 } else if (methodName.startsWith("is") && methodName.length() > 2) { 339 return StringUtil.firstCharToUpperCase(property).equals(methodName.substring(2)); 340 } else { 341 return false; 342 } 343 } 344 345 public static boolean isObjectMethod(String methodName) { 346 return ArrayUtil.contains(OBJECT_METHODS, methodName); 347 } 348 349}