一分鐘瞭解JAVA代理模式
代理模式定義: 為其他物件提供一種代理以控制對這個物件的訪問.
遠端代理:為不同地理的物件提供區域網代表物件
虛擬代理: 根據需要將資源消耗很大的物件進行延遲載入 當我們正真需要這個物件的時候再進行建立.
保護代理:控制對一個物件的許可權
智慧引用代理: 對代理物件提供一些額外的服務.
下面我來使用 靜態代理和動態代理 分別實現 智慧引用代理功能
靜態代理: 代理和被代理物件在代理之前是確定的他們都實現了相同的介面或者繼承了相同的抽象類。
場景1: 我們有一輛汽車 汽車有行駛的功能 而我們現在需要給汽車增加一個記錄行駛時間 下面是具體實現:
package com.zs.spring.demo1;
//汽車行駛介面
public interface Moveable {
public void move();
}
下面是我們傳統的做法沒有使用到代理類
package com.zs.spring.demo1;
import java.util.Random;
public class Car implements Moveable {
public Car() {
// TODO Auto-generated constructor stub
}
//行駛方法
@Override
public void move() {
long begen= System.currentTimeMillis();
// TODO Auto-generated method stub
System.out.println("汽車開始行駛");
try {
Thread.sleep(new Random().nextInt(1000));
System.out.println("汽車行駛中");
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
long end = System.currentTimeMillis();
System.out.println("汽車結束用時"+(end-begen)+"毫秒");
}
public static void main(String[] args) {
Car c =new Car();
c.move();
}
}
控制檯:
汽車開始行駛
汽車行駛中
汽車結束用時701毫秒
下面是靜態代理實現:
package com.zs.spring.demo1;
import java.util.Random;
public class Car2 extends Car{
@Override
public void move() {
long begen= System.currentTimeMillis();
// TODO Auto-generated method stub
System.out.println("汽車開始行駛");
super.move();
long end = System.currentTimeMillis();
System.out.println("汽車結束用時"+(end-begen)+"毫秒");
}
}
上面我們使用了繼承來實現代理 新建Car2這個類 並且繼承Car
重寫move()方法 在子類中呼叫父類的行駛方法 而記錄行駛時間就放到了Car2子類來做了。
下面我們來使用聚合方式做代理 所謂聚合方式就是一個類中呼叫了另外一個類就叫聚合.
package com.zs.spring.demo1;
public class Car3 implements Moveable{
private Car car;
public Car3(Car car){
this.car=car;
}
@Override
public void move() {
long begen= System.currentTimeMillis();
System.out.println("汽車開始行駛");
car.move();
long end = System.currentTimeMillis();
System.out.println("汽車結束用時"+(end-begen)+"毫秒");
}
public static void main(String[] args) {
Car c =new Car();
Car3 car3 =new Car3(c);
car3.move();
}
}
上面這種代理就是把Car類和代理類Car3同樣的實現了Moveable 介面
在代理類中定義一個Car類物件 在使用的時候先將Car類交給Car3代理類
增加記錄時間的特性.
誠然 繼承的方式也有缺陷:比如 我們不光需要加時間代理 還要加日誌代理和許可權代理 那麼是不是又要重新建立 日誌代理類 和許可權代理類呢 如果我們要 先記錄 日誌–時間–許可權 那麼就得 日誌代理類繼承Car 時間代理類繼承日誌類 許可權代理類繼承時間代理類 一層層的繼承做代理,並且如果我們可能 先記錄 時間-日誌-許可權 那麼是不是我們又要重新寫一套 擴充套件性太低.
下面我們使用 聚合代理方式模擬實現 日誌 和時間代理
//日誌代理類
package com.zs.spring.demo1;
public class CarLogProxy implements Moveable{
private Moveable car;
public CarLogProxy(Moveable car){
this.car=car;
}
@Override
public void move() {
System.out.println("日誌開始......");
car.move();
System.out.println("日誌結束......");
}
}
時間代理類
package com.zs.spring.demo1;
public class CarTimeProxy implements Moveable{
//因為日誌類還是car類還是時間類都實現了Moveable介面所以定義介面
使用多型
private Moveable car;
public CarTimeProxy(Moveable car){
this.car=car;
}
@Override
public void move() {
long begen= System.currentTimeMillis();
System.out.println("汽車開始行駛");
car.move();
long end = System.currentTimeMillis();
System.out.println("汽車結束用時"+(end-begen)+"毫秒");
}
public static void main(String[] args) {
Car c =new Car();
CarTimeProxy car3 =new CarTimeProxy(c);
CarLogProxy clp = new CarLogProxy(car3);
clp.move();
}
}
控制檯輸出:
日誌開始......
汽車開始行駛
汽車行駛中
汽車結束用時606毫秒
日誌結束......
那麼現在是先開始記錄日誌在記錄時間 我現在想先記錄時間再記錄日誌怎麼做 不用像繼承一樣再寫一套代理類繼承
public static void main(String[] args) {
Car c =new Car();
CarLogProxy clp = new CarLogProxy(c);
CarTimeProxy car3 =new CarTimeProxy(clp);
car3.move();
}
控制檯輸出:
汽車開始行駛
日誌開始......
汽車行駛中
日誌結束......
汽車結束用時852毫秒
所以我們儘量使用聚合代理 擴充套件性高
動態代理:
所謂動態代理是在執行時生成class 該class需要實現一組介面(interface) 使用動態代理類時 必須實現
InvocationHandler介面
package com.zs.spring.demo1;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class CarHandle implements InvocationHandler{
private Object target;
public CarHandle(Object target) {
this.target=target;
}
/**
* 引數:
* proxy:被代理的物件
* method:被代理物件的方法
* args[] 方法的引數
* 返回值:object
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// TODO Auto-generated method stub
long begen= System.currentTimeMillis();
System.out.println("開始行駛");
method.invoke(target);
long end = System.currentTimeMillis();
System.out.println("結束行駛"+(end-begen)+"毫秒");
return null;
}
public static void main(String[] args) {
//建立被代理物件
Car c =new Car();
//要代理的物件給事件代理類CarHandle
InvocationHandler invo = new CarHandle(c);
//獲取class物件
Class<?> invoCalss =c.getClass();
/**
* loader 被代理類
* interface 被代理類實現介面
* h 代理類 CarHandle
*/
Moveable m =(Moveable) Proxy.newProxyInstance(invoCalss.getClassLoader(),invoCalss.getInterfaces(),invo);
m.move();
}
}
控制檯輸出
汽車開始行駛
汽車行駛中
汽車結束用時133毫秒
以上為JDK使用 InvocationHandler實現的動態代理 這個樣子不管是汽車類還是火車飛機 我們都能獲取到他的行駛時間 也就是說能代理任何類