代理对象的方法到目标对象的方法过程:
自定义类加载器:
package myjdkproxy;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
public class MyClassLoader extends ClassLoader{
private File classPathFile;
public MyClassLoader(){
//获得classFile路径
String classPath = MyClassLoader.class.getResource("").getPath();
this.classPathFile = new File(classPath);
}
protected Class<?> findClass(String name) throws ClassNotFoundException {
//String className = MyClassLoader.class.getPackage().getName() + "." + name;
if(classPathFile == null){
return null;
}
/*
* classFile="class文件绝对路径"
* eg:D:\java_space\Test2\bin\myjdkproxy\$MyProxy0.class
*/
File classFile = new File(classPathFile,name.replaceAll("\\.","/") + ".class");
byte[] classData=getClassData(classFile);
//name:类名
return defineClass(name,classData,0,classData.length);
}
//获取class文件的byte流
private byte[] getClassData(File classFile) {
FileInputStream in = null;
ByteArrayOutputStream out = null;
try{
in = new FileInputStream(classFile);
out = new ByteArrayOutputStream();
byte [] buff = new byte[1024];
int len;
while ((len = in.read(buff)) != -1){
out.write(buff,0,len);
}
return out.toByteArray();
}catch (Exception e){
e.printStackTrace();
}finally {
if(null != in){
try {
in.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(out != null){
try {
out.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
return null;
}
}
自定义InvocationHandler接口及其实现:
package myjdkproxy;
import java.lang.reflect.Method;
public interface MyInvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable;
}
package myjdkproxy;
import java.lang.reflect.Method;
class MyInvoke implements MyInvocationHandler {
//被代理的对象,把引用保存下来
private Object target;
public Object getInstance(Object target) throws Exception{
this.target = target;
Class<?> cls = target.getClass();
return MyProxy.newProxyInstance(new MyClassLoader(),cls.getInterfaces(),this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("before---");
method.invoke(this.target,args);//执行目标对象的方法
System.out.println("after---");
//此处的返回值可以执行设置,不过类型要与目标对象方法返回值一致,代理对象的方法返回值就是这个
return null;
}
}
代理类:
package myjdkproxy;
import java.io.File;
import java.io.FileWriter;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
import javax.tools.JavaCompiler;
import javax.tools.JavaFileObject;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
//动态产生的代理类示例:
//import myjdkproxy.MyProxy;
//import myjdkproxy.Human;
//import myjdkproxy.MyInvocationHandler;
//import java.lang.reflect.*;
//public class $MyProxy0 implements myjdkproxy.IHuman{
//MyInvocationHandler h;
//public $MyProxy0(MyInvocationHandler h) {
//this.h = h;}
//public void talk() {
//try{
//Method m = myjdkproxy.IHuman.class.getMethod ("talk",new Class[]{});
//this.h.invoke(this,m,new Object[]{});
//}catch(Error _ex) { }catch(Throwable e){
//throw new UndeclaredThrowableException(e);
//}}}
public class MyProxy {
public static final String ln = "\r\n";
//产生代理对象
public static Object newProxyInstance(MyClassLoader classLoader, Class<?> [] interfaces, MyInvocationHandler h){
try {
/*
* 动态生成源代码.java文件
* 这个java文件中的类就是代理类
* 实现传入的接口 ,ps:这个(些)接口也就是委托类的接口
* ps:这里应该只实现第一个接口
* -----------
* 构造方法传入一个MyInvocationHandler参数handler
* 实现接口中的所有方法:m1,m2,m3...
* 注意实现的接口与委托类实现的接口相同,
* 所以委托类中也有这些方法:m1,m2,m3...
* 执行$MyProxy0的方法时会调用handler的invoke(arg0,arg1,arg2)方法
* arg0为$MyProxy0对象
* arg1为与当前方法相同的方法
* arg2为arg1方法的参数
* if:代理对象为proxy,m1参数为args
* then:proxy.m1(args)->handler.invoke(proxy,m1.class,args)
*
*/
String src = generateSrc(interfaces);
//Java文件输出磁盘
String filePath = MyProxy.class.getResource("").getPath();
File f = new File(filePath + "$MyProxy0.java");
FileWriter fw = new FileWriter(f);
fw.write(src);
fw.flush();
fw.close();
//把生成的.java文件编译成.class文件
JavaCompiler compiler = ToolProvider.getSystemJavaCompiler();
StandardJavaFileManager manager = compiler.getStandardFileManager(null,null,null);
Iterable<? extends JavaFileObject> iterable = manager.getJavaFileObjects(f);
JavaCompiler.CompilationTask task = compiler.getTask(null,manager,null,null,null, iterable);
task.call();
manager.close();
//把编译生成的.class文件加载到JVM中
Class<?> proxyClass = classLoader.findClass("$MyProxy0");
Constructor<?> c = proxyClass.getConstructor(MyInvocationHandler.class);
f.delete();//如果想要看代理类的话,把这行注释掉即可
//返回字节码重组以后的新的代理对象
return c.newInstance(h);
}catch (Exception e){
e.printStackTrace();
}
return null;
}
//以字符串的形式返回一个java源代码
private static String generateSrc(Class<?>[] interfaces){
StringBuffer sb = new StringBuffer();
//引入相关的类
sb.append("import myjdkproxy.MyProxy;" + ln);
sb.append("import myjdkproxy.Human;" + ln);
sb.append("import myjdkproxy.MyInvocationHandler;"+ln);
sb.append("import java.lang.reflect.*;" + ln);
//代理类的类名,及其实现的接口
sb.append("public class $MyProxy0 implements " + interfaces[0].getName() + "{" + ln);
/*
* 添加1个MyInvocationHandler的对象
*/
sb.append("MyInvocationHandler h;" + ln);
/*
* 代理类的构造方法,接收一个MyInvocationHandler对象,并将其保存起来
* 在这个MyInvocationHandler对象的invoke方法完成对目标方法的增强与调用
*/
sb.append("public $MyProxy0(MyInvocationHandler h) { " + ln);
sb.append("this.h = h;");
sb.append("}" + ln);
/*
* 首先获得接口中的所有方法,然后实现这些方法
* 这也就是为什么委托类必须要实现接口的原因,因为此处只会实现接口中的方法
*/
for (Method m : interfaces[0].getMethods()){
//获得当前方法的所有参数类型
Class<?>[] params = m.getParameterTypes();
StringBuffer paramNames = new StringBuffer();
StringBuffer paramValues = new StringBuffer();
StringBuffer paramClasses = new StringBuffer();
//获得当前方法每个参数的类型名字,并将类型首字母小写作为形参
for (int i = 0; i < params.length; i++) {
Class<?> cls = params[i];
String type = cls.getName();
String paramName = toLowerFirstCase(cls.getSimpleName());
paramNames.append(type + " " + paramName);
paramValues.append(paramName);
paramClasses.append(cls.getName() + ".class");
if(i > 0 && i < params.length-1){
paramNames.append(",");
paramClasses.append(",");
paramValues.append(",");
}
}
/*
* 实现当前方法,其方法头由方法的返回类型、方法名、参数构成
* 在方法体的逻辑里,首先获得目标对象中与当前方法同名的方法
* 然后调用MyInvocationHandler对象的invoke()方法,参数为代理对象本身、目标对象的方法、目标对象的方法的参数
* 将这些信息传递进去,就可以在invoke()内调用目标对象的方法
* 当然,在invoke()内也可以添加别的逻辑
*/
sb.append("public " + m.getReturnType().getName() + " " + m.getName() + "(" + paramNames.toString() + ") {" + ln);
sb.append("try{" + ln);
//获得目标对象的方法
sb.append("Method m = " + interfaces[0].getName() + ".class.getMethod (\"" + m.getName() + "\",new Class[]{" + paramClasses.toString() + "});" + ln);
/*
* 调用invoke()方法,并将该方法的返回值作为当前方法的返回值返回
* 该返回值类型要求与目标对象该方法返回值一致
* 我觉得,invoke()中目标对象方法的返回值作为invoke()的返回值即可
* 这样代理对象该方法的返回值就是目标对象该方法的返回值
* 当然也可以不这样处理,不过返回值类型要一样
*/
sb.append((hasReturnValue(m.getReturnType()) ? "return " : "") + getCaseCode("this.h.invoke(this,m,new Object[]{" + paramValues + "})",m.getReturnType()) + ";" + ln);
sb.append("}catch(Error _ex) { }");
sb.append("catch(Throwable e){" + ln);
sb.append("throw new UndeclaredThrowableException(e);" + ln);
sb.append("}");
/*
* 判断当前方法的返回值类型,这一部分是在try-catch之外加上的
* void,那么就不添加任何语句
* int,那么就添加return 0;
* 其他,那么就添加return null;
*/
sb.append(getReturnEmptyCode(m.getReturnType()));
sb.append("}");
}
sb.append("}" + ln);
return sb.toString();
}
//HashMap,并添加一个entry,key="int.class",value="Integer.class"
private static Map<Class<?>,Class<?>> mappings = new HashMap<Class<?>, Class<?>>();
static {
mappings.put(int.class,Integer.class);
}
/*
* 判断类型
* 如果是int类,返回"return 0;"
* 如果是void类,返回""
* 如果是其他类型,返回"return null;"
*/
private static String getReturnEmptyCode(Class<?> returnClass){
if(mappings.containsKey(returnClass)){
return "return 0;";
}else if(returnClass == void.class){
return "";
}else {
return "return null;";
}
}
/*
* 如果返回类型是int,那么将invoke()返回值转化成Integer,然后再转化成int
* 如果返回值不是 int,就不处理
*/
private static String getCaseCode(String code,Class<?> returnClass){
if(mappings.containsKey(returnClass)){
return "((" + mappings.get(returnClass).getName() + ")" + code + ")." + returnClass.getSimpleName() + "Value()";
}
return code;
}
//判断是否有返回值
private static boolean hasReturnValue(Class<?> cls){
return cls != void.class;
}
//首字母小写
private static String toLowerFirstCase(String src){
char [] chars = src.toCharArray();
chars[0] += 32;
return String.valueOf(chars);
}
}
委托类的接口及实现:
package myjdkproxy;
public interface IHuman {
public void talk();
}
package myjdkproxy;
public class Human implements IHuman{
public void talk() {
System.out.println("Hello World");
}
}
测试代码:
package myjdkproxy;
public class Test {
public static void main(String[] args) {
try {
Human human=new Human();
IHuman proxy=(IHuman)new MyInvoke().getInstance(human);
proxy.talk();
}catch (Exception e) {
e.printStackTrace();
}
}
}