1. 程式人生 > >詳解JDK的動態代理+例項理解

詳解JDK的動態代理+例項理解

引言:當有無數個代理類的話,傳統做法要寫100個代理類,顯然這樣子類會過於膨脹,那麼有沒有一種方法,可以動態產生代理,實現對不同類,不同方法的代理。

動態代理:JDK的動態代理,ca

在代理類和被代理類之間加入了InvocationHander的一個類,也叫事務處理器。

被代理類:

package com.imooc.jdkproxy;

import com.imooc.jdkproxy.Moveable;

import java.util.Random;

public class Car implements Moveable {

    @Override
    public void move() {
        //實現開車
        try {
            Thread.sleep(new Random().nextInt(1000));
            System.out.println("汽車行駛中....");
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

}

被代理類的方法:

package com.imooc.jdkproxy;

/**
 * Created by Administrator on 2018/9/4.
 */
public interface Moveable {
   void move();
}

事物處理器:

public class TimeHandler implements InvocationHandler{
    private Object target;
    public TimeHandler(Object target) {
        this.target = target;
    }

    //proxy,被代理物件;
    //method 被代理物件的方法;
    //方法的引數
     //
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        long startTime=System.currentTimeMillis();
        System.out.println("汽車開始行駛....");
        method.invoke(target);// target為被代理物件
        long endTime=System.currentTimeMillis();
        System.out.println("汽車結束行駛....  汽車行駛時間:"+(endTime-startTime)+"毫秒!");
        return null;//該示例中car的move()沒有返回值
    }
}

測試類:

package com.imooc.jdkproxy;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

/**
 * Created by Administrator on 2018/9/4.
 */
public class Test {
    public static  void main(String [] args){
        Car car=new Car();
        InvocationHandler h=new TimeHandler(car);
       Class<?> cls =car.getClass();
/**
 * loader  類載入器
 * interfaces  實現介面
 * h InvocationHandler
 */
        Moveable m=(Moveable) Proxy.newProxyInstance(cls.getClassLoader(),cls.getInterfaces(),h);//生成動態代理類,返回的值都代理類,都實現了movable介面
                m.move();//呼叫返回動態代理的move方法


    }
}

 run:  在move方法的基礎上,增加了時間的代理方法。

JDK動態的代理的原理和內部實現: 

procy:

package com.imooc.proxy;

import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;

import org.apache.commons.io.FileUtils;
//定義一個靜態方法,返回一個代理物件
//第一步宣告一段原始碼
public class Proxy {
    //(Class infce)把我們需要實現的介面傳遞過來
    public static Object newProxyInstance(Class infce) throws Exception{
        String rt = "\r\n";
        String methodStr = "";//獲取方法
        for(Method m : infce.getMethods()){
            methodStr += "	@Override" + rt +
                    "	public void " + m.getName() + "() {" + rt +
                    "		long starttime = System.currentTimeMillis();" + rt +
                    "		System.out.println(\"汽車開始行駛....\");" + rt +
                    "		m." + m.getName() + "();" + rt +
                    "		long endtime = System.currentTimeMillis();" + rt +
                    "		System.out.println(\"汽車結束行駛....  汽車行駛時間:\" " + rt +
                    "				+ (endtime - starttime) + \"毫秒!\");" + rt +
                    "	}" ;
        }

        String str =
                "package com.imooc.proxy;" + rt +
                        "public class $Proxy0 implements " + infce.getName() + " {" + rt +
                        "	public $Proxy0(" + infce.getName() + " m) {" + rt +
                        "		super();" + rt +
                        "		this.m = m;" + rt +
                        "	}" + rt +
                        "	private " + infce.getName() + " m;" + rt +
                        methodStr + rt +
                        "}" ;
        //先產生代理類的java檔案,在對java檔案進行編譯
        String filename = System.getProperty("user.dir") +"/bin/com/imooc/proxy/$Proxy0.java";//檔案的路徑取當前應用所在的路徑,然後放在bin目錄下
        File file = new File(filename);
        FileUtils.writeStringToFile(file, str);//將這段原始碼生成到我們的java檔案當中;

        //編譯
        //得到當前系統的編譯器
        JavaCompiler complier = ToolProvider.getSystemJavaCompiler();
        //檔案管理者
        StandardJavaFileManager fileMgr =
                complier.getStandardFileManager(null, null, null);
        //獲取檔案
        Iterable units = fileMgr.getJavaFileObjects(filename);
        //編譯任務
        CompilationTask t = complier.getTask(null, fileMgr, null, null, null, units);
        //進行編譯
        t.call();
        fileMgr.close();

        //編譯好的檔案()代理類load 到記憶體
        ClassLoader cl = ClassLoader.getSystemClassLoader();
        Class c = cl.loadClass("com.imooc.proxy.$Proxy0");//loader檔名
//產生代理物件
        Constructor ctr = c.getConstructor(infce);
        return ctr.newInstance(new Car());
    }




}

 測試類:

package com.imooc.proxy;


public class Client {

    /**
     * 測試類
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        Moveable m = (Moveable) Proxy.newProxyInstance(Moveable.class);//產生一個代理物件,並實現Moveable介面
        m.move();
    }

}

 這樣子就好了,但是我們會發現以上程式碼中,業務邏輯是寫死的,也就是我們要怎麼做才能適用於任意類和任意方法呢?

--------即,我們需要在動態代理中新增 InvocationHandler;

package com.imooc.proxy;

import java.lang.reflect.Method;

/**
 * Created by Administrator on 2018/9/14.
 */
public interface InvocationHandler{
        public void invoke(Object o,Method m);//invoke 對某個物件的方法進行處理。
}

修改我們的proxy類:

package com.imooc.proxy;

import java.io.File;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;

import javax.tools.JavaCompiler;
import javax.tools.StandardJavaFileManager;
import javax.tools.ToolProvider;
import javax.tools.JavaCompiler.CompilationTask;

import org.apache.commons.io.FileUtils;

public class Proxy {

    @SuppressWarnings("unchecked")
    public static Object newProxyInstance(Class infce,InvocationHandler h) throws Exception{
        String rt = "\r\n";
        String methodStr = "";
        for(Method m : infce.getMethods()){
            methodStr += "	@Override" + rt +
                    "	public void " + m.getName() + "() {" + rt +
                    "  try{" + rt +
                    "  Method md = " + infce.getName() + ".class.getMethod(\""
                    + m.getName() + "\");" + rt +
                    "  h.invoke(this,md);" +rt+
                    "  }catch(Exception e){ e.printStackTrace();}" + rt +
                    "	}" ;
        }

        String str =
                "package com.imooc.proxy;" + rt +
                        "import java.lang.reflect.Method;" + rt +
                        "import com.imooc.proxy.InvocationHandler;" +  rt+
                        "public class $Proxy0 implements " + infce.getName() + " {" + rt +
                        "	public $Proxy0(InvocationHandler h) {" + rt +
                        "		this.h = h;" + rt +
                        "	}" + rt +
                        "  private InvocationHandler h;" + rt+
                        methodStr + rt +
                        "}" ;
        //產生代理類的java檔案
        String filename = System.getProperty("user.dir") +"/bin/com/imooc/proxy/$Proxy0.java";
        File file = new File(filename);
        FileUtils.writeStringToFile(file, str);

        //編譯
        //拿到編譯器
        JavaCompiler complier = ToolProvider.getSystemJavaCompiler();
        //檔案管理者
        StandardJavaFileManager fileMgr =
                complier.getStandardFileManager(null, null, null);
        //獲取檔案
        Iterable units = fileMgr.getJavaFileObjects(filename);
        //編譯任務
        CompilationTask t = complier.getTask(null, fileMgr, null, null, null, units);
        //進行編譯
        t.call();
        fileMgr.close();

        //load 到記憶體
        ClassLoader cl = ClassLoader.getSystemClassLoader();
        Class c = cl.loadClass("com.imooc.proxy.$Proxy0");

        Constructor ctr = c.getConstructor(InvocationHandler.class);
        return ctr.newInstance(h);
    }




}

 測試類:

package com.imooc.proxy;

public class Client {

    /**
     * 測試類
     * @throws Exception
     */
    public static void main(String[] args) throws Exception {
        Car car = new Car();
        InvocationHandler h = new TimeHandler(car);
        Moveable m = (Moveable)Proxy.newProxyInstance(Moveable.class,h);
        m.move();
    }

}