使用方法:
需要用到 jdk 里的 tools.jar,
我的电脑里的路径是:C:\Program Files\Java\jdk1.8.0\lib\tools.jar
在 Windows 中:
java -cp ".;your_jdk_path\lib\tools.jar" CreateProxyClass TargetType.class > TargetTypeProxy.java
在 Linux中:
java -cp ".:your_jdk_path\lib\tools.jar" CreateProxyClass TargetType.class > TargetTypeProxy.java
若制作成 jar 包,MANIFEST.MF 清单如下
Manifest-Version: 1.0
Created-By: 1.8.0_151 (Oracle Corporation)
Main-Class: CreateProxyClass
执行命令如下:
java -Xms512m -Xmx1024m -Xbootclasspath/a:tools.jar; -jar createproxyclass.jar TargetType.class > TargetTypeProxy.java
(参考)运行jar应用程序引用其他jar包的四种方法 http://www.iteye.com/topic/332580
以下是源码:
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.IOException;
import java.io.PrintWriter;
import java.io.StringReader;
import java.io.StringWriter;
import java.util.Arrays;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.ListIterator;
import java.util.Map;
import java.util.Set;
import java.util.TreeSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
/**
* Created by ganmin.he on 2018/3/15 0015.
*/
public class CreateProxyClass {
private static final String COMMA_REGEX = "\\s*,\\s*";
private static final String ACCESS_MODIFIERS = "public|protected|private";
private static final String REMOVE_MODIFIERS = ACCESS_MODIFIERS + "|abstract|static|final|synchronized|native";
private static final String METHOD_MODIFIERS = REMOVE_MODIFIERS + "|strictfp";
private static final String MODIFIERS_REGEX = "((?:(?:" + METHOD_MODIFIERS + ")\\s+)*)"; // the last character is a space or spaces
private static final String REMOVE_MODIFIERS_REGEX = "\\s+(" + REMOVE_MODIFIERS + ")|(" + REMOVE_MODIFIERS + ")\\s*";
private static final String GENERIC_SINGLE_REGEX = "\\w+(?:\\s+extends\\s+[\\w.$]+(?:<.*>)?)?";
private static final String GENERIC_DECLARE_REGEX = "<\\s*(?:" + GENERIC_SINGLE_REGEX + COMMA_REGEX + ")*" + GENERIC_SINGLE_REGEX + "\\s*>";
private static final String RAW_TYPE_REGEX = "(?:\\w+\\.)+(\\w+)";
private static final String RAW_GENERIC_ARRAY_TYPE_REGEX = "[\\w.$]+(?:<.*>)?(?:\\[])?";
private static final String RETURN_TYPE_REGEX = "(" + RAW_GENERIC_ARRAY_TYPE_REGEX + ")";
private static final String METHOD_REGEX = "(\\S+)";
private static final String ARGS_REGEX = "(.*)";
private static final String EXCEPTIONS_REGEX = "(.*)";
private static final Pattern FUN_PATTERN = Pattern.compile(
"\\s*" + MODIFIERS_REGEX + "(?:(" + GENERIC_DECLARE_REGEX + ")\\s+)?" + RETURN_TYPE_REGEX + "\\s+" + METHOD_REGEX
+ "\\s*\\(\\s*" + ARGS_REGEX + "\\s*\\)\\s*" + "(?:throws\\s+" + EXCEPTIONS_REGEX + "\\s*)?" + ";" + "\\s*");
private static final int MODIFIERS_GROUP = 1;
private static final int GENERIC_DECLARE_GROUP = 2;
private static final int RETURN_TYPE_GROUP = 3;
private static final int METHOD_GROUP = 4;
private static final int ARGS_GROUP = 5;
private static final int EXCEPTIONS_GROUP = 6;
private static final Pattern MODIFIERS_PATTERN = Pattern.compile(MODIFIERS_REGEX);
private static final Pattern REMOVE_MODIFIERS_PATTERN = Pattern.compile(REMOVE_MODIFIERS_REGEX);
// import xxx.yyy.Type;
private static final Pattern RAW_TYPE_PATTERN = Pattern.compile(RAW_TYPE_REGEX);
// no need to import java.lang.Xxx;
private static final Pattern LANG_PATTERN = Pattern.compile("\\s*java\\.lang\\.\\w+\\s*");
private static Matcher sMethodMatcher;
private static Matcher sRemoveModifiersMatcher;
private static Matcher sRawTypeMatcher;
private static Matcher sLangMatcher;
private static final char REPLACE_MARK = '#'; // pound
private static Set<String> sImportsSet;
private static boolean sHasClassGeneric;
private static String sClassGenericDeclare;
private static String sClassGenericType;
private static Map<String, String> sClassGenericBoundMap;
public static void main(String[] args) throws IOException {
// java CreateProxyClass "D:\framework_intermediates\classes\android\telephony\TelephonyManager.class"
// args = new String[]{"D:\\framework_intermediates\\classes\\android\\telephony\\TelephonyManager.class"};
boolean isLinuxOS = System.getProperty("os.name").toLowerCase().contains("linux");
if (args.length == 0) {
if (isLinuxOS) {
System.out.println(
"Usage: java -cp \".:jdk_path/lib/tools.jar\" CreateProxyClass \"path/to/TargetType.class\" > \"path/to/TargetTypeProxy.java\"");
} else {
System.out.println(
"Usage: java -cp \".;jdk_path\\lib\\tools.jar\" CreateProxyClass \"path\\to\\TargetType.class\" > \"path\\to\\TargetTypeProxy.java\"");
}
System.out.println();
System.out.println("where possible options include:");
System.out.println(" -p -private include protected and private methods");
System.out.println();
return;
}
boolean includePrivate = false;
for (String arg : args) {
if (arg.equals("-p") || arg.equals("-private")) {
includePrivate = true;
break;
}
}
String className = args[0];
StringWriter sw = new StringWriter();
PrintWriter pw = new PrintWriter(new BufferedWriter(sw), true);
if (includePrivate) {
com.sun.tools.javap.Main.run(new String[]{
"-p", className}, pw);
} else {
com.sun.tools.javap.Main.run(new String[]{className}, pw);
}
pw.close();
//sw.write("private static native java.lang.String[] test$(int, boolean[],java.lang.String[], HashMap<Integer<String, Thread>, Map>[])"
// + "throws Exception, java.lang.RuntimeException;");
//sw.write("public static <T extends Number & Comparable> T transform(T);");
BufferedReader br = new BufferedReader(new StringReader(sw.toString()));
if (className.endsWith(".class")) {
className = className.substring(0, className.length() - 6);
}
int lastIndexOfSlash = className.lastIndexOf('/');
int lastIndexOfBackSlash = className.lastIndexOf('\\');
int maxIndex = Math.max(lastIndexOfSlash, lastIndexOfBackSlash);
if (maxIndex > 0) {
className = className.substring(maxIndex + 1);
}
StringBuilder importsSb = new StringBuilder();
StringBuilder headSb = new StringBuilder();
StringBuilder methodSb = new StringBuilder();
sImportsSet = new TreeSet<>();
sImportsSet.add("java.lang.reflect.InvocationTargetException");
sImportsSet.add("java.lang.reflect.Method");
String line = null;
//String enclosingClass = null;
// first line: source file
line = br.readLine();
/*if (line != null) {
String sourceFile = line.substring(line.indexOf('\"') + 1, line.lastIndexOf('\"'));
enclosingClass = sourceFile;
if (sourceFile.contains(".")) {
enclosingClass = sourceFile.substring(0, sourceFile.indexOf('.'));
}
}*/
// second line: class define
line = br.readLine();
int leftAngle = line.indexOf('<');
int extIndex = line.indexOf("extends");
int impIndex = line.indexOf("implements");
sHasClassGeneric = (leftAngle != -1) && (extIndex == -1 || leftAngle < extIndex) && (impIndex == -1 || leftAngle < impIndex);
if (line != null) {
String[] strs = line.trim().split("\\s+");
for (String str : strs) {
if (str.contains(className)) {
if (sHasClassGeneric) {
str = str.substring(0, str.indexOf('<'));
}
if (str.contains("$")) {
// java.util.Map$Entry --> import java.util.Map; import java.util.Map.Entry
String[] paths = str.split("\\$");
sImportsSet.add(paths[0]);
for (int i = 1; i < paths.length; i++) {
sImportsSet.add(paths[0] + "." + paths[i]);
}
} else {
sImportsSet.add(str);
}
break;
}
}
if (sHasClassGeneric) {
char[] chars = line.toCharArray();
int leftIndex = -1;
int rightIndex = -1;
int balance = 0;
for (int i = 0; i < chars.length; i++) {
if (chars[i] == '<') {
if (leftIndex == -1) {
leftIndex = i;
}
++balance;
} else if (chars[i] == '>') {
if (--balance == 0) {
rightIndex = i;
break;
}
}
}
sClassGenericDeclare = new String(chars, leftIndex, rightIndex - leftIndex + 1);
sClassGenericDeclare = replace$(sClassGenericDeclare);
sClassGenericDeclare = movePackagePathToImports(sClassGenericDeclare);
sClassGenericBoundMap = new LinkedHashMap<>();
setGenericBound(sClassGenericBoundMap, sClassGenericDeclare.substring(1, sClassGenericDeclare.length() - 1));
Iterator<String> it = sClassGenericBoundMap.keySet().iterator();
StringBuilder sb = new StringBuilder("<");
while (it.hasNext()) {
sb.append(it.next()).append(", ");
}
sb.delete(sb.length() - 2, sb.length());
sb.append(">");
sClassGenericType = sb.toString();
}
}
String proxyName = className + "Proxy";
className = replace$(className);
headSb.append("public final class ").append(proxyName);
if (sHasClassGeneric) {
headSb.append(sClassGenericDeclare);
}
headSb.append(" {\n");
headSb.append(" private static final Class<?> clazz = ").append(className).append(".class;\n");
headSb.append("\n");
headSb.append(" private ").append(className);
if (sHasClassGeneric) {
headSb.append(sClassGenericType);
}
headSb.append(" proxied;\n");
headSb.append("\n");
headSb.append(" private ").append(proxyName).append("(").append(className);
if (sHasClassGeneric) {
headSb.append(sClassGenericType);
}
headSb.append(" proxiedInstance) {\n");
headSb.append(" proxied = proxiedInstance;\n");
headSb.append(" }\n");
headSb.append("\n");
headSb.append(" public static ");
if (sHasClassGeneric) {
headSb.append(sClassGenericDeclare).append(" ");
}
headSb.append(proxyName);
if (sHasClassGeneric) {
headSb.append(sClassGenericType);
}
headSb.append(" getProxyInstance(").append(className);
if (sHasClassGeneric) {
headSb.append(sClassGenericType);
}
headSb.append(" proxiedInstance) {\n");
//headSb.append(" if (proxiedInstance == null) {\n");
//headSb.append(" throw new IllegalArgumentException(\"proxiedInstance must not be null\");\n");
//headSb.append(" }\n");
headSb.append(" return new ").append(proxyName);
if (sHasClassGeneric) {
headSb.append(sClassGenericType);
}
headSb.append("(proxiedInstance);\n");
headSb.append(" }\n");
headSb.append("\n");
headSb.append(" private Object invokeProxiedMethod(String methodName, Class<?>[] classes, Object... objs) {\n");
headSb.append(" try {\n");
headSb.append(" Method method = clazz.getDeclaredMethod(methodName, classes);\n");
headSb.append(" method.setAccessible(true);\n");
headSb.append(" return method.invoke(proxied, objs);\n");
headSb.append(" } catch (NoSuchMethodException | IllegalAccessException e) {\n");
headSb.append(" e.printStackTrace();\n");
headSb.append(" } catch (SecurityException | IllegalArgumentException e) {\n");
headSb.append(" throw e;\n");
headSb.append(" } catch (InvocationTargetException e) {\n");
headSb.append(" Throwable t = e.getCause();\n");
headSb.append(" if (t instanceof RuntimeException) {\n");
headSb.append(" throw (RuntimeException) t;\n");
headSb.append(" } else {\n");
headSb.append(" e.printStackTrace();\n");
headSb.append(" }\n");
headSb.append(" }\n");
headSb.append(" return null;\n");
headSb.append(" }\n");
headSb.append("\n");
headSb.append(" private Object invokeProxiedMethod(String methodName, Object... objs) {\n");
headSb.append(" Class<?>[] classes = new Class[objs.length];\n");
headSb.append(" for (int i = 0; i < objs.length; i++) {\n");
headSb.append(" classes[i] = objs[i].getClass();\n");
headSb.append(" }\n");
headSb.append(" return invokeProxiedMethod(methodName, classes, objs);\n");
headSb.append(" }\n");
while ((line = br.readLine()) != null) {
line = line.trim();
sMethodMatcher = FUN_PATTERN.matcher(line);
if (sMethodMatcher.matches()) {
/*for (int i = 1, count = sMethodMatcher.groupCount(); i <= count; i++) {
System.out.print(sMethodMatcher.group(i) + " ");
}
System.out.println();*/
// Modifiers
String modifiers = sMethodMatcher.group(MODIFIERS_GROUP);
if (modifiers != null) {
modifiers = modifiers.trim();
sRemoveModifiersMatcher = REMOVE_MODIFIERS_PATTERN.matcher(modifiers);
modifiers = sRemoveModifiersMatcher.replaceAll("").trim();
if (!modifiers.equals("")) {
modifiers = modifiers + " ";
}
}
// Generic declare
String genericDeclare = sMethodMatcher.group(GENERIC_DECLARE_GROUP);
if (genericDeclare != null) {
genericDeclare = genericDeclare.trim();
genericDeclare = replace$(genericDeclare);
genericDeclare = movePackagePathToImports(genericDeclare);
}
// Return type
String retType = sMethodMatcher.group(RETURN_TYPE_GROUP).trim();
retType = retType.trim();
if (MODIFIERS_PATTERN.matcher(retType + " ").matches()) {
// if the retType string is a modifier string, this method is a constructor
continue;
}
retType = replace$(retType);
retType = movePackagePathToImports(retType);
// Method name
String method = sMethodMatcher.group(METHOD_GROUP).trim();
if (method.startsWith("lambda$") || method.endsWith("Lambda$")) {
continue;
}
// Argument list
String arguments = sMethodMatcher.group(ARGS_GROUP).trim();
arguments = replace$(arguments);
arguments = movePackagePathToImports(arguments);
arguments = replaceCommaWithMark(arguments);
// Exception list
String exceptions = sMethodMatcher.group(EXCEPTIONS_GROUP);
boolean hasException = (exceptions != null);
if (hasException) {
exceptions = exceptions.trim();
}
boolean hasMethodGeneric = (genericDeclare != null);
boolean isNoRet = retType.equals("void");
boolean hasArgs = !arguments.equals("");
String[] argTypes = arguments.trim().split(COMMA_REGEX);
int argCount = argTypes.length;
String[] argParas = new String[argCount];
StringBuilder argListSb = new StringBuilder();
StringBuilder parListSb = new StringBuilder();
StringBuilder classArrSb = new StringBuilder("new Class<?>[]{");
// boolean hasVarArgs = false;
// T extends Number & Comparable --> T is key, Number is value
Map<String, String> methodGenericBoundMap = new LinkedHashMap<>();
if (hasMethodGeneric) {
setGenericBound(methodGenericBoundMap, genericDeclare.substring(genericDeclare.indexOf('<') + 1, genericDeclare.lastIndexOf('>')));
}
if (hasArgs) {
for (int i = 0; i < argCount; i++) {
argTypes[i] = restoreComma(argTypes[i]);
argParas[i] = "arg" + i;
// String arg0, UncaughtExceptionHandler arg1, int arg2...
argListSb.append(argTypes[i]).append(" ").append(argParas[i]);
// String.class, UncaughtExceptionHandler.class, int.class...
if (argTypes[i].contains("<")) {
// List<String> --> List
argTypes[i] = argTypes[i].substring(0, argTypes[i].indexOf('<'));
} else if (argTypes[i].contains("...")) {
// hasVarArgs = true;
// Object... --> Object[]
argTypes[i] = argTypes[i].substring(0, argTypes[i].indexOf("...")) + "[]";
}
boolean processed = false;
if (hasMethodGeneric) {
if (methodGenericBoundMap.containsKey(argTypes[i])) {
// T.class --> Object.class
classArrSb.append(methodGenericBoundMap.get(argTypes[i])).append(".class");
processed = true;
} else if (argTypes[i].contains("[]")) {
String elementType = argTypes[i].substring(0, argTypes[i].indexOf('['));
if (methodGenericBoundMap.containsKey(elementType)) {
// T[][].class --> Object[][].class
String genericArr = argTypes[i].replace(elementType, methodGenericBoundMap.get(elementType));
classArrSb.append(genericArr).append(".class");
processed = true;
}
}
}
if (!processed && sHasClassGeneric) {
if (sClassGenericBoundMap.containsKey(argTypes[i])) {
// T.class --> Object.class
classArrSb.append(sClassGenericBoundMap.get(argTypes[i])).append(".class");
processed = true;
} else if (argTypes[i].contains("[]")) {
String elementType = argTypes[i].substring(0, argTypes[i].indexOf('['));
if (sClassGenericBoundMap.containsKey(elementType)) {
// T[][].class --> Object[][].class
String genericArr = argTypes[i].replace(elementType, sClassGenericBoundMap.get(elementType));
classArrSb.append(genericArr).append(".class");
processed = true;
}
}
}
if (!processed) {
// String.class, String[].class
classArrSb.append(argTypes[i]).append(".class");
}
/*
* if (hasVarArgs) { parListSb.append("(Object) "); }
*/
// arg0, arg1, arg2...
parListSb.append(argParas[i]);
if (i < argCount - 1) {
argListSb.append(", ");
classArrSb.append(", ");
parListSb.append(", ");
}
}
}
classArrSb.append("}");
String argList = argListSb.toString();
String clazzArr = classArrSb.toString();
String parList = parListSb.toString();
if (!hasException) {
methodSb.append(" public ").append(modifiers);
if (hasMethodGeneric) {
methodSb.append(genericDeclare).append(" ");
}
methodSb.append(retType).append(" ").append(method).append("(").append(argList).append(") {\n");
methodSb.append(isNoRet ? " " : (" return (" + retType + ") ")).append("invokeProxiedMethod(\"").append(method).append("\"");
if (hasArgs) {
methodSb.append(", ").append(clazzArr).append(", ").append(parList);
}
methodSb.append(");\n");
methodSb.append(" }\n\n");
} else {
exceptions = replace$(exceptions);
exceptions = movePackagePathToImports(exceptions);
List<String> list = new LinkedList<>(Arrays.asList(exceptions.trim().split(COMMA_REGEX)));
ListIterator<String> listIter = list.listIterator();
boolean containsThrowable = false;
boolean containsException = false;
StringBuilder excListSb = new StringBuilder();
while (listIter.hasNext()) {
String tmp = listIter.next();
switch (tmp) {
case "Throwable":
containsThrowable = true;
listIter.remove();
break;
case "Exception":
containsException = true;
listIter.remove();
break;
case "RuntimeException":
listIter.remove();
break;
}
// NullPointerException, InterruptedException...
excListSb.append(tmp).append(", ");
}
String excList = excListSb.substring(0, excListSb.length() - 2);
StringBuilder methodExcSb = new StringBuilder();
methodExcSb.append(" public ").append(modifiers);
if (hasMethodGeneric) {
methodExcSb.append(genericDeclare).append(" ");
}
methodExcSb.append(retType).append(" ").append(method).append("(").append(argList).append(") throws ").append(excList).append(" {\n");
methodExcSb.append(" try {\n");
methodExcSb.append(" Method method = clazz.getDeclaredMethod(\"").append(method).append("\"");
if (hasArgs) {
methodExcSb.append(", ").append(clazzArr);
}
methodExcSb.append(");\n");
methodExcSb.append(" method.setAccessible(true);\n");
methodExcSb.append(" ");
if (!isNoRet) {
methodExcSb.append("return (").append(retType).append(") ");
}
methodExcSb.append("method.invoke(proxied");
if (hasArgs) {
methodExcSb.append(", ").append(parList);
}
methodExcSb.append(");\n");
methodExcSb.append(" } catch (NoSuchMethodException | IllegalAccessException e) {\n");
methodExcSb.append(" e.printStackTrace();\n");
methodExcSb.append(" } catch (SecurityException | IllegalArgumentException e) {\n");
methodExcSb.append(" throw e;\n");
methodExcSb.append(" } catch (InvocationTargetException e) {\n");
methodExcSb.append(" Throwable t = e.getCause();\n");
int size = list.size();
for (int i = 0; i < size; i++) {
if (i == 0) {
methodExcSb.append(" if (t instanceof ");
} else {
methodExcSb.append(" } else if (t instanceof ");
}
String tmp = list.get(i);
methodExcSb.append(tmp).append(") {\n");
methodExcSb.append(" throw (").append(tmp).append(") t;\n");
}
methodExcSb.append(" ");
if (size != 0) {
methodExcSb.append("} else ");
}
methodExcSb.append("if (t instanceof RuntimeException) {\n");
methodExcSb.append(" throw (RuntimeException) t;\n");
if (containsException) {
methodExcSb.append(" } else if (t instanceof Exception) {\n");
methodExcSb.append(" throw (Exception) t;\n");
}
if (containsThrowable) {
methodExcSb.append(" } else if (t instanceof Throwable) {\n");
methodExcSb.append(" throw (Throwable) t;\n");
}
methodExcSb.append(" } else {\n");
methodExcSb.append(" e.printStackTrace();\n");
methodExcSb.append(" }\n");
methodExcSb.append(" }\n");
boolean isBoolRet = retType.equals("boolean");
boolean isCharRet = retType.equals("char");
boolean isPrimitiveRet = retType.equals("byte") || retType.equals("short") || retType.equals("int") || retType.equals("long")
|| retType.equals("float") || retType.equals("double");
if (isNoRet) {
//methodExcSb.append(" return;\n");
} else if (isBoolRet) {
methodExcSb.append(" return false;\n");
} else if (isCharRet) {
methodExcSb.append(" return 0;\n");
} else if (isPrimitiveRet) {
methodExcSb.append(" return -1;\n");
} else {
methodExcSb.append(" return null;\n");
}
methodExcSb.append(" }\n\n");
methodSb.append(methodExcSb);
}
}
// methodSb.append("---------------------------------------------------");
}
for (String imp : sImportsSet) {
sLangMatcher = LANG_PATTERN.matcher(imp);
if (!sLangMatcher.matches()) {
importsSb.append("import " + imp + ";\n");
}
}
System.out.println(importsSb);
System.out.println(headSb);
System.out.print(methodSb);
System.out.println("}");
}
private static String replace$(String input) {
if (input.contains("$")) {
// java.util.Map$Entry --> java.util.Map.Entry
return input.replace('$', '.');
} else {
return input;
}
}
private static final int RAW_TYPE_GROUP = 1;
private static String movePackagePathToImports(String input) {
if (input.contains(".")) {
sRawTypeMatcher = RAW_TYPE_PATTERN.matcher(input);
StringBuffer rawSb = new StringBuffer();
while (sRawTypeMatcher.find()) {
sImportsSet.add(sRawTypeMatcher.group());
sRawTypeMatcher.appendReplacement(rawSb, sRawTypeMatcher.group(RAW_TYPE_GROUP));
}
sRawTypeMatcher.appendTail(rawSb);
return rawSb.toString();
} else {
return input;
}
}
private static void setGenericBound(Map<String, String> genericBoundMap, String input) {
String[] genericTypesArr = input.trim().split(COMMA_REGEX);
for (String genericType : genericTypesArr) {
boolean hasBound = genericType.contains("extends");
if (hasBound) {
// T extends Number & Comparable --> Number is the bound
String[] splitArr = genericType.split("\\s+");
genericBoundMap.put(splitArr[0], splitArr[2]);
} else {
// T --> Object is the Bound
genericBoundMap.put(genericType, "Object");
}
}
}
private static String replaceCommaWithMark(String input) {
if (input.contains("<")) {
int balance = 0;
char[] chArr = input.toCharArray();
for (int i = 0; i < chArr.length; i++) {
if (chArr[i] == '<') {
++balance;
} else if (chArr[i] == '>') {
--balance;
} else if (chArr[i] == ',') {
if (balance != 0) {
chArr[i] = REPLACE_MARK;
}
}
}
return new String(chArr);
} else {
return input;
}
}
private static String restoreComma(String input) {
return input.replace(REPLACE_MARK, ',');
}
}