1. 程式人生 > 實用技巧 >Java常見23中設計模式之【代理模式】

Java常見23中設計模式之【代理模式】

一、靜態代理模式
靜態代理,使用繼承的方式實現自己新增的服務
這種模式可以實現幫助被代理者完成一些前期的準備工作和後期的善後工作,但是核心的業務邏輯仍然是由被代理者完成。
在某些情況下,一個客戶不想或者不能直接引用一個物件,此時可以通過一個代理的第三者間接引用,。起到中介的作用,並且可以通過代理物件去掉客戶不能看到的內容和服務或額外服務。

代理是一個物件,代理物件為其他物件提供一種代理,以控制對這個物件的訪問,代理物件起到中介作用,可以去掉或者增加額外的服務。
如:火車票代售點就是火車站售票處的一個代理物件,可通過訪問代售點進行業務處理。
二,靜態代理的2種實現方式:繼承和聚合
靜態代理中的代理和被代理物件在代理之前關係是確定的。它們都實現了相同的介面或者繼承相同的抽象類。

下面的例子分別講述了所有的類實現同一個介面,類物件之間是如何代理的。

案例程式碼實現:
1.定義一個介面類Moveable.java
package com.sjms.test.proxy.interfaces;

public interface Moveable {
void move();
}

2.定義一個實體類Car.java 並實現以上介面。
package com.sjms.test.proxy.impl;

import com.sjms.test.proxy.interfaces.Moveable;

public class Car implements Moveable {

@Override

public void move() {
try {
System.out.println("開始嘍");
Thread.sleep(1000L);

} catch (Exception e) {
e.printStackTrace();
}
}

}

3.定義一個代理類Car2.java(此類,即實現了父類方法,又實現了新增的業務)
package com.sjms.test.proxy.impl;

public class Car2 extends Car {
public void move() {//實現自身方法
Long startTime = System.currentTimeMillis();

System.out.println("開始開車。。。。");
super.move();//實現父類方法
Long endTime = System.currentTimeMillis();
System.out.println("已經停止。。。。,用時:"+(endTime-startTime));
}
}
4.Test.java類
package com.sjms.test.proxy;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;

import com.sjms.test.proxy.handler.TimerHandler;
import com.sjms.test.proxy.impl.Car;
import com.sjms.test.proxy.impl.Car2;
import com.sjms.test.proxy.impl.Car3;
import com.sjms.test.proxy.interfaces.Moveable;

4.通過聚合建構函式方式 (通過將引數傳遞進來,然後我們使用傳遞進來的引數,呼叫其方法,實現靜態代理)
public class Car3 implements Moveable{

private Car car;
public Car3(Car car) {
super();
this.car = car;
}

@Override
public void move() {
long starttime = System.currentTimeMillis();
System.out.println("汽車開始行駛.......");
     //通過構造方法將car傳進來,然後通過car呼叫方法
car.move();
long endtime = System.currentTimeMillis();
System.out.println("汽車結束行駛......");
System.out.println("汽車行駛時間:"+(endtime - starttime)+"毫秒!");
}
}

public class Test {
public static void main(String[] args) {
//1.靜態代理,使用繼承的方式實現自己新增的服務
Moveable moveable = new Car2();
moveable.move();

//2.聚合靜態代理,使用構造方法傳入引數。
/*Car car = new Car();
Moveable car3 = new Car3(car);
car3.move();*/

}
}

二、使用靜態代理,會讓我們的代理類無限膨脹下去,所以出現了動態代理模式(JDK動態代理、CGLIB動態代理)
在代理類和被代理類之間加上一個事務處理器,將我們需要處理的具體功能放在其中進行處理。

1.JDK動態代理(InvocationHandler事務處理器,Proxy:代理類)
InvocationHandler介面中僅定義了一個方法public object invoke(Object obj,Method method,Object[] args)
第一個引數:代理類,第二個引數:被代理的方法,第三個引數:該方法的引數陣列
Proxy:動態代理類,通過newProxyInstance方法返回代理類的例項,

實現步驟:
1).建立一個實現InvocationHandler介面的實現類,並實現invoke方法
2).建立被代理的類和介面(Car和Moveable 在上面的案例中已經構建)
3).動態建立一個代理類(Proxy.newProxyInstance)package com.sjms.test.proxy.handler;
TimerHandler.java

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;

public class TimerHandler implements InvocationHandler{
private Object target;
public TimerHandler(Object target){
this.target = target;
}
@Override
public Object invoke(Object proxy, Method method, Object[] param)
throws Throwable {
Long startTime = System.currentTimeMillis();
System.out.println("汽車開始行使了...");
method.invoke(target);
Long entTime = System.currentTimeMillis();
System.out.println("汽車結束行駛......");
System.out.println("汽車行駛時間:"+(entTime - startTime)+"毫秒!");
return null;
}

}

Test.java 構造代理類,實現具體的代理類

package com.sjms.test.proxy;

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

import com.sjms.test.proxy.handler.TimerHandler;
import com.sjms.test.proxy.impl.Car;
import com.sjms.test.proxy.impl.Car2;
import com.sjms.test.proxy.impl.Car3;
import com.sjms.test.proxy.interfaces.Moveable;

public class Test {
public static void main(String[] args) {
//1.靜態代理,使用繼承的方式實現自己新增的服務
Moveable moveable = new Car2();
moveable.move();

//2.聚合靜態代理,使用構造方法傳入引數。
/*Car car = new Car();
Moveable car3 = new Car3(car);
car3.move();*/

//3.動態代理
Car carObj = new Car();//被代理的類
InvocationHandler handler = new TimerHandler(carObj);//事務處理器
//使用代理器實現
Moveable move = (Moveable)Proxy.newProxyInstance(carObj.getClass().getClassLoader(), carObj.getClass().getInterfaces(), handler);
//move 為生產的代理類
move.move();
}
}

2.cgLib的動態代理實現
由於JDK只能針對實現了介面的類做動態代理,而不能對沒有實現介面的類做動態代理,所以cgLib橫空出世!
CGlib是一個強大、高效能的Code生成類庫,它可以在程式執行期間動態擴充套件類或介面,它的底層是使用java位元組碼操作框架ASM實現。它可以動態的操作類部方法

1) 引入cgLib 庫
2)定義業務類,被代理的類沒有實現任何介面(有實現介面的也可以)
package com.sjms.test.proxy.impl;

public class Car4 {
public void move() {
System.out.println("開始發車");
}
}
3)定義攔截器,在呼叫目標方法時,CGlib會回撥MethodInterceptor 介面方法攔截,來實現你自己的代理邏輯,類似於JDK中的InvocationHandler介面。
package com.sjms.test.proxy.handler;

import java.lang.reflect.Method;

import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;

/**
* cglib 動態代理攔截器實現
* */
public class CglibDynProxyInterceptor implements MethodInterceptor{

@Override
public Object intercept(Object arg0, Method arg1, Object[] params,
MethodProxy methodProxy) throws Throwable {
if (arg1.getName().equals("move")){
System.out.println("cgLib動態代理攔截器開始,攔截方法mmove()......");
//代理類呼叫父類的方法
return methodProxy.invokeSuper(arg0, params);
}
return null;
}

}
4)定義動態代理工廠,生成動態代理
package com.sjms.test.proxy.factory;

import com.sjms.test.proxy.handler.CglibDynProxyInterceptor;

import net.sf.cglib.proxy.Enhancer;
//生產代理工廠

public class CglibProxyFactory {
public static Object getCarProxy(Object target){
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(target.getClass());
enhancer.setCallback(new CglibDynProxyInterceptor());
return enhancer.create();
}
}
5)測試
package com.sjms.test.proxy;

import com.sjms.test.proxy.factory.CglibProxyFactory;
import com.sjms.test.proxy.impl.Car4;

public class Car4CglibTest {
public static void main(String[] args){
//Cglib動態代理原理,就是通過攔截器來新增自己的增值業務
Car4 car = (Car4)CglibProxyFactory.getCarProxy(new Car4());
car.move();
}
}

ps:切面編碼實際底層原理使用動態代理模式
cgLib的動態代理原理
CGLIB原理:動態生成一個要代理類的子類,子類重寫要代理的類的所有不是final的方法。在子類中採用方法攔截的技術攔截所有父類方法的呼叫,順勢織入橫切邏輯。它比使用java反射的JDK動態代理要快。

CGLIB底層:使用位元組碼處理框架ASM,來轉換位元組碼並生成新的類。不鼓勵直接使用ASM,因為它要求你必須對JVM內部結構包括class檔案的格式和指令集都很熟悉。

CGLIB缺點:對於final方法,無法進行代理。