jdk动态代理的实现

代理对象的方法到目标对象的方法过程:

自定义类加载器:

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();
			}
	}

}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值