通过Arthas反编译Jdk动态代理
容易得知Jdk动态代理实际就是动态类生成,实现对应的类,对所有方法进行拦截
也可以通过设置System.setProperty(“sun.misc.ProxyGenerator”,“true”);保留动态代理生成的文件
知道这点后就可以手动编写动态代理了
先根据接口动态生成类字符串,然后进行编译,最后加载进内存,并创建对象
效果
代码
拥有拦截方法执行
public interface MyHandler {
Object invoke(Object proxy, Method method, Object[] args)throws Throwable;
}
对基本数据类型与其包装类做转换
public class TypeConversion {
private static Map<String,String> map;
/*
* boolean Boolean java.lang.Boolean java.lang.Object
* byte Byte java.lang.Byte java.lang.Number
* short Short java.lang.Short java.lang.Number
* int Integer java.lang.Integer java.lang.Number
* long Long java.lang.Float java.lang.Number
* float Float java.lang.Double java.lang.Number
* double Double java.lang.Double java.lang.Number
* char Character
*/
static {
map=new HashMap<>();
map.put("boolean","Boolean");
map.put("byte","Byte");
map.put("short","Short");
map.put("int","Integer");
map.put("long","Long");
map.put("float","Float");
map.put("double","Double");
map.put("char","Character");
}
public static String conversion(String returnType) {
return map.getOrDefault(returnType,returnType);
}
public static boolean contain(String name) {
return map.containsKey(name);
}
}
封装方法信息
public class MethodInfo {
private String clazzFullName;
private String methodName;
private String[] paramClassFullNames;
private String[] paramClassNames;
private String returnType;
private List<String> classes;
private static final String NO_RETURN="void";
public MethodInfo(Class clazz, Method method) {
classes=new ArrayList<>();
clazzFullName=clazz.getName();
methodName=method.getName();
Class<?>[] types = method.getParameterTypes();
paramClassFullNames=new String[types.length];
paramClassNames=new String[types.length];
for (int i = 0; i < types.length; i++) {
paramClassFullNames[i]=types[i].getName();
paramClassNames[i]=types[i].getSimpleName();
addClass(types[i]);
}
returnType=method.getReturnType().getSimpleName();
if(!NO_RETURN.equals(returnType)){
addClass(method.getReturnType());
}
}
private void addClass(Class<?> clazz) {
while (clazz.isArray()){
clazz=clazz.getComponentType();
}
String name = clazz.getName();
if(!TypeConversion.contain(name)){
classes.add(name);
}
}
public String[] getParamClassNames() {
return paramClassNames;
}
public String getReturnType() {
return returnType;
}
public String getClazzFullName() {
return clazzFullName;
}
public String getMethodName() {
return methodName;
}
public String[] getParamClassFullNames() {
return paramClassFullNames;
}
public List<String> getClasses() {
return classes;
}
}
自定义类加载器加载类文件
public class MyClassLoader extends ClassLoader{
private static final String BASE_PATH="src/files";
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
try {
byte[] bytes = Files.readAllBytes(Paths.get(BASE_PATH, name + ".class"));
return defineClass(name,bytes,0,bytes.length);
} catch (IOException e) {
return super.findClass(name);
}
}
}
用于拼接类字符串
public class ClassMaker {
private static final String CLASS_NAME="$Proxy";
private static final String BASE_PATH="src/files";
private static final String NO_RETURN="void";
public static Class make(Class[] interfaces, ClassLoader loader) throws IOException, ClassNotFoundException {
String classStr=buildClass(interfaces);
writeAndCompiler(classStr);
return loader.loadClass(CLASS_NAME);
}
private static void writeAndCompiler(String classStr) throws IOException {
Files.write(Paths.get(BASE_PATH,CLASS_NAME+".java"),classStr.getBytes());
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
int res = compiler.run(System.in, System.out, System.err, BASE_PATH+"/" + CLASS_NAME + ".java");
if(res!=0)throw new RuntimeException("编译失败");
}
private static String buildClass(Class[] interfaces) {
Map<String,MethodInfo> methodInfoMap=new HashMap<>();
String baseMethodName="method";
int index=0;
Set<String> imports=new HashSet<>(Arrays.asList("proxy.MyHandler","java.lang.reflect.Method",
"proxy.MyProxy"));
for (Class clazz:interfaces){
imports.add(clazz.getName());
for (Method method:clazz.getMethods()){
MethodInfo methodInfo = new MethodInfo(clazz, method);
methodInfoMap.put(baseMethodName+index++,methodInfo);
imports.addAll(methodInfo.getClasses());
}
}
StringBuilder builder=new StringBuilder();
buildImport(builder,imports);
buildStart(builder,interfaces);
buildFields(builder,methodInfoMap);
buildConstructor(builder);
buildStatic(builder,methodInfoMap);
buildMethods(builder,methodInfoMap);
builder.append("\n}");
return builder.toString();
}
private static void buildMethods(StringBuilder builder, Map<String, MethodInfo> methodInfoMap) {
for (String key:methodInfoMap.keySet()){
MethodInfo methodInfo = methodInfoMap.get(key);
buildMethod(builder,methodInfo,key);
}
}
private static void buildMethod(StringBuilder builder, MethodInfo methodInfo,String name) {
builder.append("\n\tpublic final ").append(methodInfo.getReturnType()).append(" ")
.append(methodInfo.getMethodName()).append("(");
String[] names = methodInfo.getParamClassNames();
String[] args=new String[names.length];
if(names.length>0){
String baseName="arg";
for (int i = 0; i < args.length; i++) {
args[i]=baseName+i;
builder.append(names[i]).append(" ").append(args[i]).append(',');
}
builder.setLength(builder.length()-1);
}
builder.append(") {\n\t\ttry {\n\t\t\tObject res=handler.invoke(this, ").append(name)
.append(", new Object[]{");
if(args.length>0){
for (String arg:args){
builder.append(arg).append(',');
}
builder.setLength(builder.length()-1);
}
builder.append("});\n");
if(!NO_RETURN.equals(methodInfo.getReturnType())){
builder.append("\t\t\treturn (").append(TypeConversion.conversion(methodInfo.getReturnType()))
.append(")res;\n");
}
builder.append("\t\t}catch (Throwable throwable) {\n\t\t\tthrow new RuntimeException(throwable);\n\t\t}\n\t}\n");
}
private static void buildStatic(StringBuilder builder, Map<String, MethodInfo> methodInfoMap) {
builder.append("\tstatic {\n\t\ttry {\n");
for (String key:methodInfoMap.keySet()){
MethodInfo methodInfo = methodInfoMap.get(key);
builder.append("\t\t\t").append(key).append(" = Class.forName(\"").append(methodInfo.getClazzFullName())
.append("\").getMethod(\"").append(methodInfo.getMethodName())
.append("\"");
for (String clazzName:methodInfo.getParamClassNames()){
builder.append(",").append(clazzName).append(".class");
}
builder.append(");\n");
}
builder.append("\t\t}catch (Exception exception) {\n\t\t\tthrow new RuntimeException(exception);\n\t\t}\n\t}\n\n");
}
private static void buildConstructor(StringBuilder builder) {
builder.append("\n\tpublic ").append(CLASS_NAME).append("(MyHandler handler) {\n\t\tsuper(handler);\n\t}\n\n");
}
private static void buildFields(StringBuilder builder, Map<String, MethodInfo> methodInfoMap) {
for (String methodName:methodInfoMap.keySet()){
builder.append("\tprivate static Method ").append(methodName).append(";\n");
}
}
private static void buildStart(StringBuilder builder,Class[] interfaces) {
builder.append("\npublic final class ").append(CLASS_NAME).append(" extends MyProxy implements ");
for (Class clazz:interfaces){
builder.append(clazz.getSimpleName()).append(',');
}
builder.setCharAt(builder.length()-1,'{');
builder.append("\n\n");
}
private static void buildImport(StringBuilder builder, Set<String> imports) {
for (String item:imports){
builder.append("import ").append(item).append(";\n");
}
}
}
主要使用代理类
public class MyProxy implements MyHandler{
protected MyHandler handler;
public MyProxy(MyHandler handler) {
this.handler = handler;
}
public static Object newProxyInstance(ClassLoader loader, Class[] interfaces, MyHandler handler) throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException, IOException, ClassNotFoundException {
if(loader==null)loader=new MyClassLoader();
Class clazz=ClassMaker.make(interfaces,loader);
Constructor constructor = clazz.getConstructor(MyHandler.class);
return constructor.newInstance(handler);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
return handler.invoke(proxy, method, args);
}
}
总体来说没有什么难度,不过注意,返回值基本数据类型与封装数据类型转换问题,以及导包问题