難得最近項目結束了,可以休息一下,但是領導突然給了我一個有點奇葩的任務。
我們項目是分成多個模塊的,我當前做的是其中一個模塊,領導讓我找出我這個模塊中用到其他模塊哪些接口,這些接口中哪些方法。
例如說:
我們service層經常會注入很多的service層接口,這些接口可能是自己模塊的,也有其他模塊的。
而我的任務就是找到我這個項目中引用的A模塊和B模塊的接口,以及調用了他們哪些方法。
這個任務着實是讓人頭疼啊,我的這個項目就有100多個文件,我總不能一個個文件翻吧。
雖然說我剛開始還真是這樣的,但是后面實在是不想繼續了。於是乎在摸魚中想到寫個小應用讓程序幫我找
想了下整體思路,其實也就四步。
- 遍歷所有java文件
- 獲取import springcloud_producer.dao.DeptMapper;紅色這串玩意
- 獲取private DeptMapper deptMapper;綠色這串玩意
- int resultNum = deptMapper.count(query);藍色這串玩意
然后返回不就ok了么,我真聰明。
至於怎么獲取,正則表達式不是很牛逼么。
全部都准備好了,那就直接開干。
附上代碼:
package springcloud_producer_8001; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.OutputStream; import java.lang.reflect.Method; import java.nio.charset.Charset; import java.nio.file.FileAlreadyExistsException; import java.util.Arrays; import java.util.HashSet; import java.util.List; import java.util.Set; import java.util.regex.Matcher; import java.util.regex.Pattern; import java.util.stream.Collectors; public class AppTest { /** 用來保存引入的類全限定名 */ private Set<String> classResult = new HashSet<String>(); /** 用來保存引用的類的方法,包括普通方法和靜態方法名 */ private Set<String> methodResult = new HashSet<String>(); /** * 我們要找的目標包 * */ private List<String> searchPackages; /** * 構造函數 * @param searchPackages 我們要找的目標包 * */ public AppTest(List<String> searchPackages) { if(searchPackages == null || searchPackages.isEmpty()) { throw new IllegalArgumentException("searchPackages不能為空"); } this.searchPackages = searchPackages; } /** * 獲取目標文件中引用的指定包內的類和使用的方法 * @param f 目標文件,如果是文件夾,則會遞歸處理 * @throws Exception * */ public void parseAllUsedImportClassAndMethods(String path) throws Exception { File file = new File(path); // 獲取類和方法 this.getAllUsedImportClassAndMethods(file); // 獲取方法的參數類型 this.getMethodParams(); } /** * 遞歸獲取java文件內容 * */ private void getAllUsedImportClassAndMethods(File f) throws Exception { if(f.isDirectory()) { // 如果是目錄,那就遞歸獲取java文件 File[] listFiles = f.listFiles(); for(File file :listFiles) { this.getAllUsedImportClassAndMethods(file); } }else { // 如果是.java結尾的文件就讀取 if(f.getName().endsWith(".java")) { this.getFileText(f); } } } /** * 讀取文件內容 * */ private void getFileText(File file) throws Exception{ FileReader reader = new FileReader(file); char[] buf = new char[1024]; StringBuilder sb = new StringBuilder(); while(reader.read(buf ) != -1) { sb.append(buf); } // 獲取引入的類 Set<String> importClass = this.getImport(sb.toString()); // 獲取引入的方法 this.getMethods(sb.toString(), importClass); reader.close(); } /** * 獲取import的類 * */ private Set<String> getImport(String fileText) { StringBuilder sb = new StringBuilder(); sb.append("import +?("); for(String packageStr :this.searchPackages) { sb.append(packageStr).append(".*?").append("|"); } if(sb.charAt(sb.length() - 1) == '|') { sb.deleteCharAt(sb.length() - 1); } sb.append(") *;"); Pattern p = Pattern.compile(sb.toString()); Matcher m = p.matcher(fileText); Set<String> thisResult = new HashSet<String>(); while(m.find()) { for(int i = 1; i <= m.groupCount(); i++) { String group = m.group(i); classResult.add(group); thisResult.add(group); } } return thisResult; } /** * 獲取調用的方法 * */ private void getMethods(String fileText,Set<String> importClass) { // 普通方法 for(String clasz: importClass) { String simpleClass = clasz.substring(clasz.lastIndexOf(".")+1); Pattern p = Pattern.compile("private +"+simpleClass+" +(.*?);"); Matcher m = p.matcher(fileText); if(m.find()) { String fieldName = m.group(1); Pattern p2 = Pattern.compile(fieldName+"\\.([a-zA-Z]+?)\\(.*?\\)"); Matcher m2 = p2.matcher(fileText); while(m2.find()) { for(int i = 1; i <= m2.groupCount(); i++) { String group = m2.group(i); methodResult.add(clasz + "." + group); } } } } // 靜態方法 for(String clasz: importClass) { String simpleClass = clasz.substring(clasz.lastIndexOf(".")+1); Pattern p2 = Pattern.compile(simpleClass+"\\.([a-zA-Z]+?)\\(.*?\\)"); Matcher m2 = p2.matcher(fileText); while(m2.find()) { for(int i = 1; i <= m2.groupCount(); i++) { String group = m2.group(i); methodResult.add(clasz + "." + group); } } } } /** * 獲取類方法的參數類型 * */ @SuppressWarnings("rawtypes") private void getMethodParams() { this.methodResult = this.methodResult.stream().sorted().map((methodStr)->{ // 獲取方法的參數 StringBuilder sb = new StringBuilder(methodStr); sb.append("("); int pointLastIndex = methodStr.lastIndexOf("."); String methodName = methodStr.substring(pointLastIndex+1); String className = methodStr.substring(0,pointLastIndex); try { Class<?> clasz = Class.forName(className); Method[] declaredMethods = clasz.getDeclaredMethods(); for(Method method: declaredMethods) { if(method.getName().equals(methodName)) { Class<?>[] parameterTypes = method.getParameterTypes(); for(Class p: parameterTypes) { sb.append(p.getSimpleName()).append(" ,"); } break; } } } catch (ClassNotFoundException e) { e.printStackTrace(); return methodStr; } if(sb.charAt(sb.length()-1) == ',') { sb.deleteCharAt(sb.length()-1); } sb.append(")"); return sb.toString(); }).collect(Collectors.toSet()); } /** * 導出 * @throws Exception * */ public void export(String path) throws Exception { File file = new File(path); if(file.exists()) { throw new FileAlreadyExistsException(path); } // 創建父目錄 file.getParentFile().mkdirs(); FileWriter fw = new FileWriter(file); fw.write("引用的類如下:\n"); for(String clasz: this.classResult) { fw.write(clasz); fw.write("\n"); } fw.write("\n使用的引用類的方法如下:\n"); for(String method: this.methodResult) { fw.write(method); fw.write("\n"); } fw.flush(); fw.close(); } /** * 導出 * @throws Exception * */ public void export(OutputStream out) throws Exception { out.write("引用的類如下:\n".getBytes(Charset.forName("utf-8"))); for(String clasz: this.classResult) { out.write(clasz.getBytes(Charset.forName("utf-8"))); out.write("\n".getBytes(Charset.forName("utf-8"))); } out.write("\n使用的引用類的方法如下:\n".getBytes(Charset.forName("utf-8"))); for(String method: this.methodResult) { out.write(method.getBytes(Charset.forName("utf-8"))); out.write("\n".getBytes(Charset.forName("utf-8"))); } out.flush(); out.close(); } /** * 測試 * */ public static void main(String[] args) throws Exception { AppTest test = new AppTest(Arrays.asList("springcloud_producer.service")); test.parseAllUsedImportClassAndMethods("D:\\workspace\\java_web\\springcloud-parent\\springcloud_producer\\src\\main\\java\\springcloud_producer"); test.export(System.out); test.export("C:\\Users\\HongCheng\\Desktop\\2.txt"); } }
下面這個是我的運行效果:
不過要注意一下,因為會用到反射,所以這代碼最好放在你要搜索的項目里面,不然會導致反射失敗
不過idea應該有類似的功能,沒必要像我一樣做