[Proxy] 深入理解jdk動態代理
阿新 • • 發佈:2018-12-13
[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()方法下,可以自定義代理邏輯。