删除未使用的Java类文件

package io.github.linwancen.code.modify;

import java.io.File;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.*;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;

/**
 * 删除未使用的类
 * 
仅依赖 JDK */ public class DeleteNotUsage { /** * 多线程电脑会很卡,约快 3 倍 */ private static final boolean PARALLEL = true; private static final boolean DELETE = false; private static final String CLASS_PATH = ClassLoader.getSystemResource("").getPath(); /** * 扫描根目录 *
根据需要添加.getParentFile() */ private static final File ROOT; static { if (CLASS_PATH.endsWith("test-classes")) { // maven ROOT= new File(CLASS_PATH) // target/test-classes .getParentFile() // target .getParentFile(); // } else if (CLASS_PATH.endsWith("main")) { // gradle ROOT = new File(CLASS_PATH) // main .getParentFile() // java .getParentFile() // classes .getParentFile() // build .getParentFile(); } else { ROOT = new File(""); } } private static final int ROOT_LEN = ROOT.getPath().length(); private static final Pattern EXT_PATTERN = Pattern.compile("\\.(?:java|xml|properties|conf|yml)|services$"); private static final Pattern EXCLUDE_PATTERN = Pattern.compile("target|.git"); /** * 添加自行定义的会被调用到的注解或关键字 */ private static final Pattern USED_PATTERN = Pattern.compile( "@(?:Service|Repository|Component|Test|Controller|Configuration)|main\\(String"); private static final AtomicInteger N = new AtomicInteger(); private static final class Data { final String pathString; final Set usages = new ConcurrentSkipListSet<>(); final File file; public Data(String pathString, File file) { this.pathString = pathString; this.file = file; } } public static void main(String[] args) throws Exception { List paths = Files.walk(ROOT.toPath()) .filter(path -> path.toFile().isFile()) .filter(path -> { String s = path.toString(); return EXT_PATTERN.matcher(s).find() && !EXCLUDE_PATTERN.matcher(s).find(); }) .collect(Collectors.toList()); int fileCount = paths.size(); System.out.println("walk complete:" + fileCount); Map usageMap = new HashMap<>(); StringBuilder patternStr = new StringBuilder("\\b(?:"); for (Path path : paths) { filterClassSimpleName(usageMap, patternStr, path); } System.out.println("filter complete:" + usageMap.size()); int len = patternStr.length(); if (len == 0) { return; } patternStr.delete(len - 1, len); patternStr.append(")\\b"); Pattern pattern = Pattern.compile(patternStr.toString()); System.out.println("pattern compile complete:" + patternStr.length()); long start = System.currentTimeMillis(); if (PARALLEL) { paths.parallelStream().forEach(path -> check(fileCount, usageMap, pattern, start, N.incrementAndGet(), path)); } else { for (int i = 0; i < fileCount; i++) { Path path = paths.get(i); check(fileCount, usageMap, pattern, start, i, path); } } long useTime = (System.currentTimeMillis() - start) / 1000; System.out.println("check complete, size:{}" + fileCount + " use " + useTime / 60 + ":" + useTime % 60); long count = usageMap.entrySet().stream() .filter(entry -> { if (!entry.getValue().usages.isEmpty()) { return false; } String symbol = "-"; if (DELETE) { symbol = entry.getValue().file.delete() ? "√" : "×"; } String path = entry.getValue().pathString.substring(ROOT_LEN); System.out.println(symbol + "\t.(" + entry.getKey() + ".java:0)\t" + path); return true; }) .count(); System.out.println("check print complete, " + count + "/" + usageMap.size()); } private static void filterClassSimpleName(Map usageMap, StringBuilder patternStr, Path path) { String s = path.toString(); if (!s.endsWith(".java")) { return; } try { byte[] bytes = Files.readAllBytes(path); String code = new String(bytes, StandardCharsets.UTF_8); if (USED_PATTERN.matcher(code).find()) { return; } } catch (IOException e) { e.printStackTrace(); return; } String classSimpleName = s.substring(s.lastIndexOf(File.separator) + 1, s.length() - 5); usageMap.put(classSimpleName, new Data(s, path.toFile())); patternStr.append(classSimpleName).append("|"); } private static void check(int fileCount, Map usageMap, Pattern pattern, long start, int i, Path p) { if (i != 0 && (i % 1000) == 0) { long use = System.currentTimeMillis() - start; long remain = use / i * (fileCount - i); use = use / 1000; remain = remain / 1000; System.out.println("check " + i + "/" + fileCount + ", use " + use / 60 + ":" + use % 60 + ", remain " + remain / 60 + ":" + remain % 60); } String pathStr = p.toString(); try { byte[] bytes = Files.readAllBytes(p); String code = new String(bytes, StandardCharsets.UTF_8); Matcher m = pattern.matcher(code); while (m.find()) { String s = m.group(); Data data = usageMap.get(s); if (!data.pathString.equals(pathStr)) { data.usages.add(pathStr); } } } catch (Exception e) { e.printStackTrace(); } } }

你可能感兴趣的:(删除未使用的Java类文件)