Java获取指定包名下的所有类的全类名的解决方案

package com.component.msg.redis.util;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONArray;
import cn.hutool.json.JSONObject;
import cn.hutool.json.JSONUtil;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.boot.orm.jpa.hibernate.SpringPhysicalNamingStrategy;
import org.springframework.stereotype.Component;

import javax.persistence.Column;
import javax.persistence.Query;
import javax.persistence.Table;
import javax.persistence.Transient;
import java.io.BufferedInputStream;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.net.JarURLConnection;
import java.net.URL;
import java.net.URLClassLoader;
import java.nio.file.FileSystem;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.jar.JarEntry;
import java.util.jar.JarFile;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @description 将所有mysql对应的entity中所有属性转为mysql对应字段,并入redis缓存,用于简化sql查询代码
 */
@Component
public class SqlHelper implements ApplicationRunner {

    @Autowired
    private RedisUtil redisUtil;

    private static final String BASIC_PACKAGES = "com.modules.system.domain";

    public static final String REDIS_MYSQL_ENTITY_KEY = "mysql_entity";

    private final Pattern underLinePattern = Pattern.compile("_(\\w)");

    public static final List<String> defaultIgnoreFieldList;

    public static final Map<String, String> defaultAppendFieldMap;

    private static Map<String, JSONObject> localMap = new HashMap<>();

    private static final ThreadLocal<SimpleDateFormat> sdf = ThreadLocal.withInitial(() ->
            new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));

    static {
        defaultIgnoreFieldList = new ArrayList<String>() {{
            add("createDate");
            add("updateDate");
            add("createBy");
            add("updateBy");
        }};
        defaultAppendFieldMap = new TreeMap<String, String>() {{
            put("creatTime", "create_date");
            put("updateTime", "update_date");
        }};
    }

    @Override
    public void run(ApplicationArguments args) throws Exception {
        redisUtil.del(REDIS_MYSQL_ENTITY_KEY);
        scanMysqlEntity(args);
    }

    //扫描mysql实体类属性,入redis缓存
    private void scanMysqlEntity(ApplicationArguments args) {
        ClassLoader cl = Thread.currentThread().getContextClassLoader();
        List<String> list = new ArrayList<>();
        for (String packagePath : BASIC_PACKAGES.split(",")) {
            list.addAll(getClazzName(packagePath, true));
        }
        try {
            for (String clazz : list) {
                getFieldsByClass(cl.loadClass(clazz));
            }
        } catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
         /*try {
           for (String packagePath : BASIC_PACKAGES.replace(".", "/").split(",")) {
                Enumeration<URL> resources = cl.getResources(packagePath);
                while (resources.hasMoreElements()) {
                    URL url = resources.nextElement();
                    File[] files = new File(url.getPath()).listFiles();
                    if (files != null) {
                        for (File classFile : files) {
                            cacheEntityFields(packagePath, classFile);
                        }
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }*/
    }

    private void cacheEntityFields(String packagePath, File classFile) throws ClassNotFoundException {
        // 如果是java类文件 去掉后面的.class 只留下类名
        String pureClassName = classFile.getName().substring(0, classFile.getName().lastIndexOf(".class"));
        String path = packagePath.replace("/", ".") + "." + pureClassName;
        //使用appclassloader,不使用restartclassloader
        Class<?> clazz = ClassLoader.getSystemClassLoader().loadClass(path);
        getFieldsByClass(clazz);
    }

    private JSONObject getFieldsByClass(Class clazz) {
        String clazzName = clazz.getName();
        if (localMap.containsKey(clazzName)) {
            return localMap.get(clazzName);
        }
        if (!clazz.isAnnotationPresent(Table.class)) return null;
        Field[] fields = clazz.getDeclaredFields();
        //使用有序的tree
        JSONObject columnMap = new JSONObject(16, true);
        for (Field field : fields) {
            if (!field.isAccessible()) field.setAccessible(true);
            if (field.isAnnotationPresent(Transient.class)
                    || defaultIgnoreFieldList.contains(field.getName()))
                continue;
            columnMap.put(field.getName(), getColumnName(field));
        }
        if (defaultAppendFieldMap.size() > 0) {
            for (String key : defaultAppendFieldMap.keySet()) {
                columnMap.put(key, defaultAppendFieldMap.get(key));
            }
        }
        localMap.put(clazz.getName(), columnMap);
        redisUtil.hashPut(REDIS_MYSQL_ENTITY_KEY, clazz.getName(), columnMap.toString());
        return columnMap;
    }

    private String getColumnName(Field field) {
        String columnName = null;
        if (field.isAnnotationPresent(Column.class)) {
            Column column = field.getAnnotation(Column.class);
            columnName = column.name();
        }
        if (columnName != null && columnName.length() > 0)
            return columnName;
        return getDbColumnNameByFieldName(field.getName());
    }

    private String getDbColumnNameByFieldName(String fieldName) {
        return apply(fieldName);
    }

    private String apply(String name) {
        //参考 SpringPhysicalNamingStrategy
        if (name == null) {
            return null;
        }
        StringBuilder builder = new StringBuilder(name.replace('.', '_'));
        for (int i = 1; i < builder.length() - 1; i++) {
            if (isUnderscoreRequired(builder.charAt(i - 1), builder.charAt(i),
                    builder.charAt(i + 1))) {
                builder.insert(i++, '_');
            }
        }
        return builder.toString().toLowerCase(Locale.ROOT);
    }

    private static final String CLASS_SUFFIX = ".class";
    private static final String CLASS_FILE_PREFIX = File.separator + "classes" + File.separator;
    private static final String PACKAGE_SEPARATOR = ".";

    public static List<String> getClazzName(String packageName, boolean showChildPackageFlag) {
        List<String> result = new ArrayList<>();
        String suffixPath = packageName.replaceAll("\\.", "/");
        ClassLoader loader = Thread.currentThread().getContextClassLoader();
        try {
            Enumeration<URL> urls = loader.getResources(suffixPath);
            while (urls.hasMoreElements()) {
                URL url = urls.nextElement();
                if (url != null) {
                    String protocol = url.getProtocol();
                    if ("file".equals(protocol)) {
                        String path = url.getPath();
                        result.addAll(getAllClassNameByFile(new File(path), showChildPackageFlag));
                    } else if ("jar".equals(protocol)) {
                        JarFile jarFile = null;
                        try {
                            jarFile = ((JarURLConnection) url.openConnection()).getJarFile();
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                        if (jarFile != null) {
                            result.addAll(getAllClassNameByJar(jarFile, packageName, showChildPackageFlag));
                        }
                    }
                }
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
        return result;
    }

    private static List<String> getAllClassNameByFile(File file, boolean flag) {
        List<String> result = new ArrayList<>();
        if (!file.exists()) {
            return result;
        }
        if (file.isFile()) {
            String path = file.getPath();
            // 注意:这里替换文件分割符要用replace。因为replaceAll里面的参数是正则表达式,而windows环境中File.separator="\\"的,因此会有问题
            if (path.endsWith(CLASS_SUFFIX)) {
                path = path.replace(CLASS_SUFFIX, "");
                String clazzName = path.substring(path.indexOf(CLASS_FILE_PREFIX) + CLASS_FILE_PREFIX.length())
                        .replace(File.separator, PACKAGE_SEPARATOR);
                if (-1 == clazzName.indexOf("$")) {
                    result.add(clazzName);
                }
            }
            return result;
        } else {
            File[] listFiles = file.listFiles();
            if (listFiles != null && listFiles.length > 0) {
                for (File f : listFiles) {
                    if (flag) {
                        result.addAll(getAllClassNameByFile(f, flag));
                    } else {
                        if (f.isFile()) {
                            String path = f.getPath();
                            if (path.endsWith(CLASS_SUFFIX)) {
                                path = path.replace(CLASS_SUFFIX, "");
                                // 从"/classes/"后面开始截取
                                String clazzName = path.substring(path.indexOf(CLASS_FILE_PREFIX) + CLASS_FILE_PREFIX.length())
                                        .replace(File.separator, PACKAGE_SEPARATOR);
                                if (-1 == clazzName.indexOf("$")) {
                                    result.add(clazzName);
                                }
                            }
                        }
                    }
                }
            }
            return result;
        }
    }

    private static List<String> getAllClassNameByJar(JarFile jarFile, String packageName, boolean flag) {
        List<String> result = new ArrayList<>();
        Enumeration<JarEntry> entries = jarFile.entries();
        while (entries.hasMoreElements()) {
            JarEntry jarEntry = entries.nextElement();
            String name = jarEntry.getName();
            // 判断是不是class文件
            if (name.endsWith(CLASS_SUFFIX)) {
                name = name.replace(CLASS_SUFFIX, "").replace("/", ".");
                if (flag) {
                    // 如果要子包的文件,那么就只要开头相同且不是内部类就ok
                    if (name.startsWith(packageName) && -1 == name.indexOf("$")) {
                        result.add(name);
                    }
                } else {
                    // 如果不要子包的文件,那么就必须保证最后一个"."之前的字符串和包名一样且不是内部类
                    if (packageName.equals(name.substring(0, name.lastIndexOf("."))) && -1 == name.indexOf("$")) {
                        result.add(name);
                    }
                }
            }
        }
        return result;
    }

    private boolean isUnderscoreRequired(char before, char current, char after) {
        return Character.isLowerCase(before) && Character.isUpperCase(current)
                && Character.isLowerCase(after);
    }

    /**
     * 把app_id转换成appId
     *
     * @param value 要转换的下划线字符串
     * @return 驼峰体字符串
     */
    private String underLineToCamel(final String value) {
        StringBuffer sb = new StringBuffer();
        Matcher matcher = underLinePattern.matcher(value);
        while (matcher.find()) {
            matcher.appendReplacement(sb, matcher.group(1).toUpperCase());
        }
        matcher.appendTail(sb);
        return sb.toString();
    }

    public JSONObject getEntityColumns(Class clazz) {
        return getFieldsByClass(clazz);
        /*Object value = redisUtil.hashGet(REDIS_MYSQL_ENTITY_KEY, clazz.getName());
        return JSONUtil.parseObj(value, false, true);*/
    }

    public static String getFieldNamesByClazz(JSONObject entityColumns, String... ignoreFields) {
        Set<String> fieldNames = entityColumns.keySet();
        List<String> ignoreFieldList = null;
        if (ignoreFields != null && ignoreFields.length > 0) {
            ignoreFieldList = new ArrayList<>(Arrays.asList(ignoreFields));
        }
        List<String> list = new ArrayList<>();
        for (String fieldName : fieldNames) {
            if (ignoreFieldList == null || !ignoreFieldList.contains(fieldName)) {
                list.add(fieldName);
            }
        }
        return CollUtil.join(list, ",");
    }

    public static Query query(Query query, List<Object> paramList) {
        if (paramList.size() > 0) {
            int index = 0;
            for (Object param : paramList) {
                query.setParameter(++index, param);
            }
        }
        return query;
    }

    public static String castToSql(JSONObject fields, String tableAlias, String... ignoreFields) {
        List<String> ignoreFieldList = null;
        if (ignoreFields != null && ignoreFields.length > 0) {
            ignoreFieldList = new ArrayList<>(Arrays.asList(ignoreFields));
        }
        StringBuilder sql = new StringBuilder();
        if (tableAlias != null && tableAlias.length() > 0) {
            tableAlias = tableAlias.trim() + ".";
        } else {
            tableAlias = "";
        }
        for (String fieldName : fields.keySet()) {
            if (ignoreFieldList == null || !ignoreFieldList.contains(fieldName)) {
                String columnName = fields.getStr(fieldName);
                sql.append(tableAlias).append(columnName);
                if (!columnName.equals(fieldName)) {
                    sql.append(" as ").append(fieldName).append(",");
                } else {
                    sql.append(",");
                }
            }
        }
        if (sql.length() > 0) {
            sql.setLength(sql.length() - 1);
        }
        return sql.toString();
    }

    public static JSONArray objToArray(List<Object[]> list, String columns) {
        JSONArray result = new JSONArray();
        if (list != null && columns != null) {
            String[] columnArr = columns.split(",");
            int columnLen = columnArr.length;
            for (Object[] row : list) {
                JSONObject job = new JSONObject();
                for (int index = 0; index < columnLen; index++) {
                    job.put(columnArr[index], row[index] == null ? "" : row[index]);
                }
                result.add(job);
            }
        }
        return result;
    }

    public static JSONArray formatTimeDefault(JSONArray jsonArray) {
        return formatTime(jsonArray, "creatTime", "updateTime");
    }

    public static JSONArray formatTime(JSONArray jsonArray, String... formatFields) {
        if (jsonArray != null && formatFields != null && formatFields.length > 0) {
            Iterator<Object> iterator = jsonArray.iterator();
            while (iterator.hasNext()) {
                JSONObject jsonObject = (JSONObject) iterator.next();
                for (String formatField : formatFields) {
                    Date date = jsonObject.getDate(formatField);
                    if (date != null) {
                        jsonObject.put(formatField, sdf.get().format(date));
                    }
                }
            }
        }
        return jsonArray;
    }
}

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值