版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/qq_22271479/article/details/84721486
[Proxy] 深入理解jdk动态代理
分析
jdk的proxy主要有三个类,Proxy,Proxy.ProxyClassFactory和ProxyGenerator。Proxy是一个面向用户的Client,主要是管理proxy class cache,jdk的proxy必须是interface,且必须传入一个InvocationHandler接口的实例,规约比较强。Proxy.ProxyClassFactory主要是生产proxy class实例。ProxyGenerator是生产class实例的执行者。最终将生成proxy class 字节码,ProxyGenerator的saveGeneratedFiles属性决定了是否保存class文件。文件路径是BaseDir+/com/sun/proxy/$xx.class。最终ClassLoader将会load此class字节码到JVM中。返回interface的动态代理实例。
自定义proxy
- 创建java文件,保存至工作路径。
- 通过JavaCompiler将java文件编译成class文件,保存在工作路径。
- 通过ClassLoader加载此class文件
- 返回interface实例
Proxy
package com.yzz.study.proxy.custom;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.util.Map;
import java.util.WeakHashMap;
/**
* author:yzz
* date:2018/12/1
* E-mail:[email protected]
* com.yzz.study.proxy.custom
* {@link java.lang.reflect.Proxy}
*/
public class Proxy {
/**
* 缓存 {@link java.lang.reflect.Proxy}
*/
private static Map<String, Class> cachedProxyClassInstaceMap = new WeakHashMap<String, Class>();
protected InvocationHandler h;
private Proxy(){
}
protected Proxy(InvocationHandler h){
this.h = h;
}
/**
* {@link java.lang.reflect.Proxy}
* 这里实现比较简单的 Class<T>[] interfaces --> Class<T> ince
* @param loader
* @param ince
* @param h
* @param <T>
* @return
*/
public static <T> T newInstance(MyClassLoader loader,
Class<T> ince,
InvocationHandler h) throws Exception {
String className = "$proxy"+cachedProxyClassInstaceMap.size()+1;
//1.从cache里查找
Class targetClass = cachedProxyClassInstaceMap.get(ince.getName());
if (null != targetClass){
return (T) targetClass;
}
//2.创建java file
String javaFilePth = ProxyGenerator.createProxyJavaFile(loader,className,ince);
//3.编译 java file
CompileJavaCode.compile(javaFilePth);
//4.load class 文件
Class<T> _class = (Class<T>) loader.findClass(className);
//这里加入InvocationHandler实例
Constructor constructor = _class.getConstructor(InvocationHandler.class);
return (T) constructor.newInstance(h);
}
}
ProxyGenerator(负责创建java文件)
package com.yzz.study.proxy.custom;
import javax.sound.sampled.Line;
import java.io.*;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
/**
* author:yzz
* date:2018/12/1
* E-mail:[email protected]
* com.yzz.study.proxy.custom
* 注释:创建proxy class 文件
*/
class ProxyGenerator {
private static final String LINE = "\r\n";
public static final String LEFT_CLOSE = "}";
public static final String RIGHT_CLOSE = "{";
public static final String INVOCATIONHANDLER = "InvocationHandler";
public static final String PARAMATER = "var";
public static String createProxyJavaFile(ClassLoader classLoader, String className, Class ince) {
String filePath = classLoader.getClass().getResource("").getPath() + "/" + className + ".java";
String packgeNmae = classLoader.getClass().getPackage().getName();
StringBuffer sb = new StringBuffer();
sb.append("package ").append(packgeNmae).append(";").append(LINE);
sb.append("import ").append("java.lang.reflect.InvocationHandler;").append(LINE);
sb.append("import java.lang.reflect.Method;").append(LINE);
sb.append("import com.yzz.study.proxy.custom.Proxy;").append(LINE);
createHead(sb, className,ince.getName());
Map<String, String> methodNames = createField(sb, ince);
createConstruct(sb, className);
createMethods(sb, ince, methodNames);
createStaticBlock(ince,methodNames,sb);
sb.append(LEFT_CLOSE);
try {
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream(filePath)));
writer.write(sb.toString());
writer.flush();
writer.close();
} catch (Exception e) {
e.printStackTrace();
}
return filePath;
}
public static void createHead(StringBuffer sb, String className,String inceName) {
sb.append("public class ")
.append(className)
.append(" extends Proxy implements ")
.append(inceName)
.append(" ")
.append(RIGHT_CLOSE)
.append(" ")
.append(LINE);
}
public static Map<String, String> createField(StringBuffer sb, Class ince) {
Method[] ms = ince.getMethods();
Map<String, String> map = new HashMap<String, String>();
for (int i = 0; i < ms.length; i++) {
Method method = ms[i];
sb.append("private static Method ")
.append("m")
.append(i + 1)
.append(";").append(LINE);
map.put(method.getName(), "m" + (i + 1));
}
return map;
}
public static void createConstruct(StringBuffer sb, String className) {
sb.append("public ")
.append(className)
.append(" (")
.append(INVOCATIONHANDLER)
.append(" h )")
.append(RIGHT_CLOSE)
.append("super(h);")
.append(LEFT_CLOSE)
.append(LINE);
}
public static void createMethods(StringBuffer sb, Class ince, Map<String, String> methodNmaes) {
Method[] ms = ince.getMethods();
for (Method m : ms) {
String methodName = m.getName();
Class returnType = m.getReturnType();
Class[] pts = m.getParameterTypes();
createMethod(methodName, returnType, pts, sb, ince, methodNmaes.get(m.getName()));
}
}
public static void createMethod(String methodName, Class returnType, Class[] pts, StringBuffer sb, Class ince, String mName) {
sb.append(" public ")
.append(returnType)
.append(" ")
.append(methodName)
.append("(");
String[] args = new String[pts.length];
for (int i = 0; i < pts.length; i++) {
Class c = pts[i];
sb.append(c.getName())
.append(" ")
.append(PARAMATER)
.append(i);
if (i != pts.length - 1) {
sb.append(",");
}
args[i] = PARAMATER + i;
}
sb.append(")");
sb.append(RIGHT_CLOSE);
createMethodBody(mName, args, sb);
sb.append(LEFT_CLOSE)
.append(LINE);
}
/***
* public Object invoke(Object proxy, Method method, Object[] args)
* throws Throwable;
* @return
*/
public static void createMethodBody(String methodName, String[] args, StringBuffer sb) {
sb.append("try ")
.append(LINE)
.append(RIGHT_CLOSE)
.append(LINE)
.append("super.h.invoke(this,")
.append(methodName)
.append(",")
.append("new Object[]{");
//这里需要构造参数
for (int i = 0; i < args.length; i++) {
sb.append(args[i]);
if (i != args.length - 1) {
sb.append(",");
}
}
sb.append("});")
.append(LINE)
.append("}catch(Throwable e){")
.append(LINE)
.append("e.printStackTrace();")
.append(LINE)
.append(LEFT_CLOSE)
.append(LINE);
}
private static void createStaticBlock(Class ince,Map<String,String> classMethodMap,StringBuffer sb){
sb.append("static ")
.append(RIGHT_CLOSE)
.append("try ")
.append(RIGHT_CLOSE);
Method[] ms = ince.getMethods();
for (Method method:ms){
Class[] ps = method.getParameterTypes();
String pcs = "";
for (int i = 0; i < ps.length; i++) {
pcs += "Class.forName(\""+ps[i].getName() + "\")";
if (i != ps.length-1){
pcs += ",";
}
}
sb.append(classMethodMap.get(method.getName()))
.append("=")
.append("Class.forName(\"")
.append(ince.getName())
.append("\")")
.append(".getMethod(")
.append("\""+method.getName()+"\"")
.append(",")
.append("new Class[]{")
.append(pcs)
.append("});")
.append(LINE);
}
sb.append("}catch(Exception e)")
.append(RIGHT_CLOSE)
.append(LINE)
.append("e.printStackTrace();")
.append(LINE)
.append(LEFT_CLOSE);
sb.append(LEFT_CLOSE);
sb.append(LINE);
}
}
CompileJavaCode(负责编译java文件)
编译工作是依赖javax.tools下的资源包
package com.yzz.study.proxy.custom;
import javax.tools.*;
import java.nio.charset.Charset;
/**
* author:yzz
* date:2018/12/1
* E-mail:[email protected]
* com.yzz.study.proxy.custom
* 注释:
*/
public class CompileJavaCode {
public static void compile(String javaFileName){
try {
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager manager = compiler.getStandardFileManager(null, null, Charset.defaultCharset());
Iterable iterable = manager.getJavaFileObjects(javaFileName);
JavaCompiler.CompilationTask compilationTask = compiler.getTask(null, manager, null, null, null, iterable);
compilationTask.call();
manager.close();
}catch (Exception e){
e.printStackTrace();
}
}
}
ClassLoader(负责装载指定目录下的class文件)
package com.yzz.study.proxy.custom;
import java.io.*;
/**
* author:yzz
* date:2018/12/1
* E-mail:[email protected]
* com.yzz.study.proxy.custom
* 注释: 加载class文件
*/
public class MyClassLoader extends ClassLoader{
private static String baseDir;
public MyClassLoader(){
baseDir = MyClassLoader.class.getResource("").getPath();
}
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
String className = MyClassLoader.class.getPackage().getName()+"."+name;
BufferedInputStream bufferedInputStream = null;
ByteArrayOutputStream byteArrayOutputStream = null;
try {
File classFile = new File(baseDir,name.replaceAll("\\.","/") + ".class");
bufferedInputStream = new BufferedInputStream(new FileInputStream(classFile));
byteArrayOutputStream = new ByteArrayOutputStream();
byte[] temp = new byte[1024];
int len;
while((len = bufferedInputStream.read(temp)) != -1){
byteArrayOutputStream.write(temp,0,len);
}
} catch (Exception e) {
e.printStackTrace();
}finally {
if (null != bufferedInputStream){
try {
byteArrayOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if (null != byteArrayOutputStream){
try {
byteArrayOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return defineClass(className,byteArrayOutputStream.toByteArray(),0,byteArrayOutputStream.size());
}
}
interface
package com.yzz.study.proxy.custom;
/**
* author:yzz
* date:2018/12/1
* E-mail:[email protected]
* com.yzz.study.proxy.jdk
* 注释:
*/
public interface ILog {
void printLog(String a,Object b);
void printLog1(String a,Object b);
void printLog2(String a,Object b);
}
测试
package com.yzz.study.proxy.custom;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
/**
* author:yzz
* date:2018/12/2
* E-mail:[email protected]
* com.yzz.study.proxy.custom
* 注释:
*/
public class Test {
public static void main(String[] args) {
try {
ILog log = Proxy.newInstance(new MyClassLoader(), ILog.class, new InvocationHandler() {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("==================");
System.out.println(method.getName());
return null;
}
});
System.out.println(log);
log.printLog("1","2");
} catch (Exception e) {
e.printStackTrace();
}
}
}
展示
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by Fernflower decompiler)
//
package com.yzz.study.proxy.custom;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class $proxy01 extends Proxy implements ILog {
private static Method m1;
private static Method m2;
private static Method m3;
public $proxy01(InvocationHandler var1) {
super(var1);
}
public void printLog1(String var1, Object var2) {
try {
super.h.invoke(this, m1, new Object[]{var1, var2});
} catch (Throwable var4) {
var4.printStackTrace();
}
}
public void printLog2(String var1, Object var2) {
try {
super.h.invoke(this, m2, new Object[]{var1, var2});
} catch (Throwable var4) {
var4.printStackTrace();
}
}
public void printLog(String var1, Object var2) {
try {
super.h.invoke(this, m3, new Object[]{var1, var2});
} catch (Throwable var4) {
var4.printStackTrace();
}
}
static {
try {
m1 = Class.forName("com.yzz.study.proxy.custom.ILog").getMethod("printLog1", new Class[]{Class.forName("java.lang.String"), Class.forName("java.lang.Object")});
m2 = Class.forName("com.yzz.study.proxy.custom.ILog").getMethod("printLog2", new Class[]{Class.forName("java.lang.String"), Class.forName("java.lang.Object")});
m3 = Class.forName("com.yzz.study.proxy.custom.ILog").getMethod("printLog", new Class[]{Class.forName("java.lang.String"), Class.forName("java.lang.Object")});
} catch (Exception var1) {
var1.printStackTrace();
}
}
}
总结
jdk的proxy严重依赖InvocationHandler接口,代理实例的方法都会回调该接口的invoke()方法,在invoke()方法下,可以自定义代理逻辑。