利用JDT 回分析java 源代码
利用JDT 来分析java 源代码
项目里需要扫代码的sql,主要是想找出所有用到的sql。 将相关的sql 提交给DBA 来分析,希望在最早的时间发现潜在的查询性能问题。
想想eclipse 里面用到的JDT 能分析java 源代码, 如果我们能分析项目里的源代码利用ASTParser 就可以找到相关的SQL 定义了。
其实已经有人想到这个 http://www.programcreek.com/2011/01/a-complete-standalone-example-of-astparser
,已经自己整理过所有会依赖的jar,机器重装本地的 repository给丢了。 所有就直接用这个链接里面给的一个zip 包, 但是在我现在用到的eclipse 还需要 一个 org.eclipse.text_3.5.0.jar 。
为了这个代码不要弄丢了,直接放一份 当然没有项目里面的信息
package com.bwang.jdt; import java.io.BufferedReader; import java.io.BufferedWriter; import java.io.File; import java.io.FileReader; import java.io.FileWriter; import java.io.IOException; import java.io.Writer; import java.util.ArrayList; import java.util.List; import org.eclipse.jdt.core.dom.AST; import org.eclipse.jdt.core.dom.ASTNode; import org.eclipse.jdt.core.dom.ASTParser; import org.eclipse.jdt.core.dom.AbstractTypeDeclaration; import org.eclipse.jdt.core.dom.BodyDeclaration; import org.eclipse.jdt.core.dom.CompilationUnit; import org.eclipse.jdt.core.dom.FieldDeclaration; public class SqlExtractor { private BufferedWriter writer; private long counter = 0; private final String fileRoot; private final String logFileName; public static void main (String[] args) { if (args.length < 2) { System.out.println("Please provide the parameters:"); System.out.println("SqlExtractor codeOfRoot logFileName"); System.exit(-1); } SqlExtractor sqlExtractor = new SqlExtractor(args); sqlExtractor.extractSql(); } SqlExtractor(String[] args) { fileRoot = args[0]; logFileName = args[1]; try { writer = new BufferedWriter(new FileWriter(new File(logFileName))); } catch(IOException e) { } } private void analyzeOneFile(String sourceFile, Writer writer) { String source = getFileContent(sourceFile); ASTParser parser = ASTParser.newParser(AST.JLS3); parser.setSource(source.toCharArray()); CompilationUnit unit = (CompilationUnit)parser.createAST(null); unit.recordModifications(); StringBuilder sb = new StringBuilder(); List<AbstractTypeDeclaration> types = unit.types(); for (AbstractTypeDeclaration type : types) { if (type.getNodeType() == ASTNode.TYPE_DECLARATION) { // Class def found List<BodyDeclaration> bodies = type.bodyDeclarations(); for (BodyDeclaration body : bodies) { if (body.getNodeType() == ASTNode.FIELD_DECLARATION /*&& ((FieldDeclaration)body).getType().equals(Type.S) */ ) { FieldDeclaration field = (FieldDeclaration)body; if(field.toString().toLowerCase().indexOf("select") >=0) { sb.append(field.toString()); counter++; } } } } } if (sb.length() > 0) { try { if (counter > 0) { writer.append("\n\n"); } sourceFile = sourceFile.replace(fileRoot, ""); sourceFile = sourceFile.replace("\\", "/"); writer.append(sourceFile + "\n"); String content = sb.toString(); content = content.replace("\" +", "\"\n\t\t+ "); content = content.replace("\"+", "\"\n\t\t+ "); writer.append(content); }catch(IOException ioe) { } } } private void extractSql() { File folder = new File(fileRoot); List<File> poms = new ArrayList<File>(); handleOneFolder(folder, poms); System.out.println("begin to analyze files of :" + poms.size()); try { int index = 1; for(File f : poms) { System.out.println("begin to analyze file : " + index + " " + f.getName()); analyzeOneFile(f.getPath(), writer); index++; } } finally { try { writer.flush(); writer.close(); } catch(IOException e) { } } System.out.println("successfully find sql count:" + counter); } private void handleOneFolder(File folder, List<File> poms) { if(!folder.isDirectory()) { return; } String[] files = folder.list(); for(String oneFile : files) { String fullFileName = folder.getPath() + "/" + oneFile; if (fullFileName.endsWith(".java") && fullFileName.indexOf("src\\test\\java\\") < 0) { poms.add(new File(fullFileName)); continue; } File f = new File(fullFileName); if (f.isDirectory()) { handleOneFolder(f, poms); } } } private String getFileContent(String fName) { try { File file = new File(fName); BufferedReader in = new BufferedReader(new FileReader(file)); StringBuffer buffer = new StringBuffer(); String line = null; while (null != (line = in.readLine())) { buffer.append("\t" + line); buffer.append("\n"); } return buffer.toString(); } catch (IOException e) { throw new RuntimeException(e); } } }
思路也简单就是遍历 codeOfRoot 下面的所有java 文件, 找到 所有的field 定义的值里面包含 select 关键字的 field, 然后将所有这些满足条件的都写到一个log 文件。
到时候将log 文件发给DBA 就行了。 当然还可以更进一步来个CI, 可以定期去扫项目的代码, 然后比较产生的log 文件的变化。 然后DBA 定期看 变化的SQL 就好了, 是不是很酷。