难得最近项目结束了,可以休息一下,但是领导突然给了我一个有点奇葩的任务。
我们项目是分成多个模块的,我当前做的是其中一个模块,领导让我找出我这个模块中用到其他模块哪些接口,这些接口中哪些方法。
例如说:
我们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应该有类似的功能,没必要像我一样做