001/**
002 * Copyright (c) 2015-2022, Michael Yang 杨福海 (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 io.jboot.utils;
017
018import com.jfinal.aop.Aop;
019import com.jfinal.log.Log;
020import io.jboot.aop.annotation.StaticConstruct;
021import io.jboot.exception.JbootException;
022
023import java.lang.reflect.*;
024import java.util.Arrays;
025import java.util.Map;
026import java.util.concurrent.ConcurrentHashMap;
027
028/**
029 * 类实例创建者创建者
030 * Created by michael on 17/3/21.
031 */
032public class ClassUtil {
033
034    private static Log LOG = Log.getLog(ClassUtil.class);
035
036    private static final Map<Class<?>, Object> singletons = new ConcurrentHashMap<>();
037
038
039    /**
040     * 获取单例
041     *
042     * @param clazz
043     * @param <T>
044     * @return
045     */
046    public static <T> T singleton(Class<T> clazz) {
047        return singleton(clazz, true);
048    }
049
050
051    /**
052     * 获取单利
053     *
054     * @param clazz
055     * @param createByAop
056     * @param <T>
057     * @return
058     */
059    public static synchronized <T> T singleton(Class<T> clazz, boolean createByAop) {
060        Object ret = singletons.get(clazz);
061        if (ret == null) {
062            ret = newInstance(clazz, createByAop);
063            if (ret != null) {
064                singletons.put(clazz, ret);
065            } else {
066                LOG.error("Can not new instance for class: " + clazz.getName());
067            }
068        }
069        return (T) ret;
070    }
071
072    public static synchronized <T> T singleton(Class<T> clazz, boolean createByAop, boolean inject) {
073        Object ret = singletons.get(clazz);
074        if (ret == null) {
075            ret = newInstance(clazz, createByAop);
076            if (ret != null) {
077                if (inject && !createByAop) {
078                    Aop.inject(ret);
079                }
080                singletons.put(clazz, ret);
081            } else {
082                LOG.error("Can not new instance for class: " + clazz.getName());
083            }
084        }
085        return (T) ret;
086    }
087
088    /**
089     * 创建新的实例
090     *
091     * @param <T>
092     * @param clazz
093     * @return
094     */
095    public static <T> T newInstance(Class<T> clazz) {
096        return newInstance(clazz, true);
097    }
098
099
100    /**
101     * 创建新的实例,并传入初始化参数
102     *
103     * @param clazz
104     * @param paras
105     * @param <T>
106     * @return
107     */
108    public static <T> T newInstance(Class<T> clazz, Object... paras) {
109        return newInstance(clazz, true, paras);
110    }
111
112
113    /**
114     * 是否通过 AOP 来实例化
115     *
116     * @param clazz
117     * @param createByAop
118     * @param <T>
119     * @return
120     */
121    public static <T> T newInstance(Class<T> clazz, boolean createByAop) {
122        if (createByAop) {
123            return Aop.get(clazz);
124        } else {
125            try {
126                Constructor constructor = clazz.getDeclaredConstructor();
127                constructor.setAccessible(true);
128                return (T) constructor.newInstance();
129            } catch (Exception e) {
130                LOG.error("Can not new instance for class:" + clazz.getName() + "\n" + e, e);
131            }
132
133            return null;
134        }
135    }
136
137
138    public static <T> T newInstance(Class<T> clazz, boolean createByAop, Object... paras) {
139        try {
140            Constructor<?>[] constructors = clazz.getDeclaredConstructors();
141            for (Constructor<?> constructor : constructors) {
142                if (isConstructorMatchedParas(constructor, paras)) {
143//                    constructor.setAccessible(true);
144                    Object ret = constructor.newInstance(paras);
145                    if (createByAop) {
146                        Aop.inject(ret);
147                    }
148                    return (T) ret;
149                }
150            }
151
152            throw new IllegalArgumentException("Can not matched constructor by paras" + Arrays.toString(paras) + " for class: " + clazz.getName());
153        } catch (Exception e) {
154            LOG.error("Can not new instance for class: " + clazz.getName() + "\n" + e, e);
155        }
156
157        return null;
158    }
159
160
161    private static boolean isConstructorMatchedParas(Constructor<?> constructor, Object[] paras) {
162        if (constructor.getParameterCount() == 0) {
163            return paras == null || paras.length == 0;
164        }
165
166        if (constructor.getParameterCount() > 0
167                && (paras == null || paras.length != constructor.getParameterCount())) {
168            return false;
169        }
170
171        Class<?>[] parameterTypes = constructor.getParameterTypes();
172        for (int i = 0; i < parameterTypes.length; i++) {
173            Class<?> parameterType = parameterTypes[i];
174            Object paraObject = paras[i];
175            if (paraObject != null && !parameterType.isAssignableFrom(paraObject.getClass())) {
176                return false;
177            }
178        }
179
180        return true;
181    }
182
183
184    public static <T> T newInstanceByStaticConstruct(Class<T> clazz) {
185        StaticConstruct staticConstruct = clazz.getAnnotation(StaticConstruct.class);
186        if (staticConstruct == null) {
187            return null;
188        }
189
190        return newInstanceByStaticConstruct(clazz, staticConstruct);
191    }
192
193
194    public static <T> T newInstanceByStaticConstruct(Class<T> clazz, StaticConstruct staticConstruct) {
195
196        Method method = getStaticConstruct(staticConstruct.value(), clazz);
197
198        if (method == null) {
199            throw new JbootException("Can not new instance by static construct for class: " + clazz.getName());
200        }
201
202        try {
203            return (T) method.invoke(null, null);
204        } catch (Exception e) {
205
206            LOG.error("Can not invoke method: " + method.getName()
207                    + " in class: " + clazz.getName() + "\n" + e, e);
208
209            if (e instanceof RuntimeException) {
210                throw (RuntimeException) e;
211            } else {
212                throw new RuntimeException(e);
213            }
214        }
215    }
216
217
218    private static Method getStaticConstruct(String name, Class<?> clazz) {
219        Method[] methods = clazz.getDeclaredMethods();
220        for (Method method : methods) {
221            if (Modifier.isStatic(method.getModifiers())
222                    && Modifier.isPublic(method.getModifiers())
223                    && method.getReturnType() == clazz) {
224                if (StrUtil.isBlank(name)) {
225                    return method;
226                } else if (name.equals(method.getName())) {
227                    return method;
228                }
229            }
230        }
231        return null;
232    }
233
234    /**
235     * 创建新的实例
236     *
237     * @param <T>
238     * @param clazzName
239     * @return
240     */
241    public static <T> T newInstance(String clazzName) {
242        return newInstance(clazzName, true);
243    }
244
245    /**
246     * 创建新的实例
247     *
248     * @param <T>
249     * @param clazzName
250     * @return
251     */
252    public static <T> T newInstance(String clazzName, boolean createByAop) {
253        return newInstance(clazzName, createByAop, Thread.currentThread().getContextClassLoader());
254    }
255
256
257    /**
258     * 创建新的实例
259     *
260     * @param clazzName
261     * @param createByAop
262     * @param classLoader
263     * @param <T>
264     * @return
265     */
266    public static <T> T newInstance(String clazzName, boolean createByAop, ClassLoader classLoader) {
267        try {
268            Class<T> clazz = (Class<T>) Class.forName(clazzName, false, classLoader);
269            return newInstance(clazz, createByAop);
270        } catch (Exception e) {
271            LOG.error("Can not new instance for class: " + clazzName + "\n" + e.toString(), e);
272        }
273
274        return null;
275    }
276
277
278    private static final String ENHANCER_BY = "$$EnhancerBy";
279    private static final String JAVASSIST_BY = "_$$_";
280
281    public static Class getUsefulClass(Class<?> clazz) {
282        //ControllerTest$ServiceTest$$EnhancerByGuice$$40471411#hello
283        //com.demo.blog.Blog$$EnhancerByCGLIB$$69a17158
284        //io.jboot.test.app.TestAppListener_$$_jvstb9f_0  by:javassist
285
286        final String name = clazz.getName();
287        if (name.contains(ENHANCER_BY) || name.contains(JAVASSIST_BY)) {
288            return clazz.getSuperclass();
289        }
290
291        return clazz;
292    }
293
294
295    public static ClassType getClassType(Type type, Class<?> runClass) {
296        if (type instanceof Class) {
297            return new ClassType((Class<?>) type);
298        }
299
300        // 泛型定义在参数里,例如 List<String>
301        else if (type instanceof ParameterizedType) {
302            ClassType classType = new ClassType((Class<?>) ((ParameterizedType) type).getRawType());
303
304            Type[] actualTypeArguments = ((ParameterizedType) type).getActualTypeArguments();
305            ClassType[] genericTypes = new ClassType[actualTypeArguments.length];
306            for (int i = 0; i < actualTypeArguments.length; i++) {
307                genericTypes[i] = getClassType(actualTypeArguments[i], runClass);
308            }
309
310            classType.setGenericTypes(genericTypes);
311            return classType;
312        }
313
314        //泛型定义在 class 里,例如 List<T>,其中 T 是在 class 里的参数
315        else if (type instanceof TypeVariable && runClass != null) {
316            Type variableRawType = getTypeInClassDefined(runClass, ((TypeVariable<?>) type));
317            if (variableRawType != null) {
318                return getClassType(variableRawType, runClass);
319            } else {
320                return null;
321            }
322        }
323
324        return null;
325    }
326
327
328    private static Type getTypeInClassDefined(Class<?> runClass, TypeVariable<?> typeVariable) {
329        Type type = runClass.getGenericSuperclass();
330        if (type instanceof ParameterizedType) {
331            Type[] typeArguments = ((ParameterizedType) type).getActualTypeArguments();
332            if (typeArguments.length == 1) {
333                return typeArguments[0];
334            } else if (typeArguments.length > 1) {
335                TypeVariable<?>[] typeVariables = typeVariable.getGenericDeclaration().getTypeParameters();
336                for (int i = 0; i < typeVariables.length; i++) {
337                    if (typeVariable.getName().equals(typeVariables[i].getName())) {
338                        return typeArguments[i];
339                    }
340                }
341            }
342        }
343        return null;
344    }
345
346
347    public static String buildMethodString(Method method) {
348        StringBuilder sb = new StringBuilder()
349                .append(method.getDeclaringClass().getName())
350                .append(".")
351                .append(method.getName())
352                .append("(");
353
354        Class<?>[] params = method.getParameterTypes();
355        int in = 0;
356        for (Class<?> clazz : params) {
357            sb.append(clazz.getName());
358            if (++in < params.length) {
359                sb.append(",");
360            }
361        }
362
363        return sb.append(")").toString();
364    }
365
366
367    public static boolean hasClass(String className) {
368        try {
369            Class.forName(className, false, getClassLoader());
370            return true;
371        } catch (ClassNotFoundException e) {
372            return false;
373        }
374    }
375
376
377    public static ClassLoader getClassLoader() {
378        ClassLoader ret = Thread.currentThread().getContextClassLoader();
379        return ret != null ? ret : ClassUtil.class.getClassLoader();
380    }
381
382}