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