代理模式:一個委托類,一個代理類,實現相同的接口,通常是在代理類中有一個委托類的對象,代理類并不會真正的執(zhí)行方法,只是在委托類執(zhí)行方法之前或之后提供一些服務。比如賬戶驗證,權限查看啥的。
代理模式主要分為靜態(tài)代理和動態(tài)代理。
靜態(tài)代理:在它在運行的時候,代理類的.class文件就存在了。
動態(tài)代理:利用反射,在運行時動態(tài)的加載類。
靜態(tài)代理
// Car.java
package com.codeliu.dao;
public interface Car {
// 預訂車
public void revCar();
// vip客戶可以直接買車
public void buyCar();
}
// CarImpl.java
package com.codeliu.dao.impl;
import com.codeliu.dao.Car;
/**
* 委托類
*/
public class CarImpl implements Car {
public void revCar() {
System.out.println("我要去預訂一輛車咯");
}
public void buyCar() {
System.out.println("我要買車咯");
}
}
/ CarProxy.java
package com.codeliu.dao.impl;
import com.codeliu.dao.Car;
/**
* 代理類
*/
public class CarProxy implement Car {
private CarImpl ci;
public CarProxy(CarImpl ci) {
this.ci = ci;
}
public void revCar() {
System.out.println("該客戶是否有足夠的錢預訂車");
ci.revCar();
System.out.println("該客戶預訂完成");
}
public void buyCar() {
System.out.println("該客戶是否是vip客戶");
ci.buyCar();
System.out.println("該客戶買車完成");
}
}
package com.codeliu.test;
import com.codeliu.dao.impl.CarImpl;
import com.codeliu.dao.impl.CarProxy;
public class TestProxy {
public static void main(String[] args) {
CarImpl ci = new CarImpl();
CarProxy cp = new CarProxy(ci);
cp.revCar();
cp.buyCar();
}
}
靜態(tài)代理是一個代理類只能為一個委托類服務,如果有多個委托類實現了同一個接口,則就要有多個代理類,除了委托類調用的方法不一樣,
其他操作都一樣,這樣勢必就會產生很多重復代碼。這時就是動態(tài)代理類上場的時候了。
動態(tài)代理又分為JDK動態(tài)代理和Cglib動態(tài)代理。
JDK動態(tài)代理要使用Java提供的InvocationHandler接口和Proxy類的newProxyInstance方法。
// InvocationHandler接口
public interface InvocationHandler {
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable;
}
nvocationHandler接口接口中只有一個invoke方法,所有動態(tài)代理類都要實現這個接口,并實現invoke方法。
關于該方法的三個參數
proxy表示被代理的對象
method表示被代理對象的方法
args表示方法的參數
// Proxy類的newProxyInstance方法
public static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces, InvocationHandler h)
loader表示委托類的類加載器
interfaces表示委托類實現的接口(這就說明委托類一定要實現了接口才能使用)
h表示InvocationHandler接口的子實例,也就是動態(tài)代理類的實例
為了說明動態(tài)代理類和靜態(tài)代理類的不同,我定義了兩個委托類
// Car.java
package com.codeliu.dao;
public interface Car {
// vip客戶可以直接買車
public void buyCar();
}
// CarImp1.java
package com.codeliu.dao.impl;
import com.codeliu.dao.Car;
/**
* 委托類1
*/
public class CarImp1 implements Car {
public void buyCar() {
System.out.println("我是vip1");
}
}
// CarImp2.java
package com.codeliu.dao.impl;
import com.codeliu.dao.Car;
/**
* 委托類2
*/
public class CarImp2 implements Car {
public void buyCar() {
System.out.println("我是vip2");
}
}
// CarProxy.java
package com.codeliu.dao.impl;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 動態(tài)代理類
*/
public class CarProxy implements InvocationHandler {
private Object target;
/**
* 綁定一個委托對象并獲得一個代理類對象
* @param target [description]
* @return [description]
*/
public Object bind(Object target) {
this.target = target;
// 取得代理對象
return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(),
this);
}
@Override
//這個方法并不是我們自己去調用
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
System.out.println("該客戶是否是vip客戶");
// 執(zhí)行委托類的方法
Object result = method.invoke(target,args);
System.out.println("該客戶買車完成");
return result;
}
}
package com.codeliu.test;
import com.codeliu.dao.impl.CarImp1;
import com.codeliu.dao.impl.CarImp2;
import com.codeliu.dao.impl.CarProxy;
import com.codeliu.dao.Car;
public class TestProxy {
public static void main(String[] args) {
CarProxy cp = new CarProxy();
// 傳入一個實現了該接口的實例就行
Car car = (Car)cp.bind(new CarImp1());
// Car car = (Car)cp.bind(new CarImp2());
car.buyCar();
}
}
輸出
該客戶是否是vip客戶
我是vip1
該客戶買車完成
以上就是一個JDK動態(tài)代理的例子,但我們可以看到,如果委托類沒有實現接口的話,就不能使用newProxyInstance方法,進而不能使用JDK動態(tài)代理。這時候,Cglib動態(tài)代理上場了。
Cglib是針對類來實現代理的,對指定的目標類生成一個子類,通過方法攔截技術攔截所有父類方法的調用。因為是生成子類,所以就不能用在final修飾的類上。
要使用Cglib,首先的下載jar包。
package com.codeliu.cglibproxy;
public class Train {
public void move() {
System.out.println("火車行駛中。。。。");
}
}
package com.codeliu.cglibproxy;
import java.lang.reflect.Method;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
public class CglibProxy implements MethodInterceptor {
private Enhancer enhancer = new Enhancer();
/**
* 獲得代理類
* @return
*/
public Object getProxy(Class<?> c) {
// 設置創(chuàng)建子類的類
enhancer.setSuperclass(c);
// 回調方法
enhancer.setCallback(this);
// 返回代理對象
return enhancer.create();
}
@Override
/**
* 攔截所有目標方法的調用
* obj 目標類的實例
* m 目標方法的反射對象
* args 方法的參數
* proxy 代理類的實例
*/
public Object intercept(Object obj, Method m, Object[] args,
MethodProxy proxy) throws Throwable {
System.out.println("火車啟動啦??!");
// 代理類調用父類的方法
Object result = proxy.invokeSuper(obj, args);
System.out.println("火車停止啦??!");
return result;
}
}
package com.codeliu.cglibproxy;
public class Test {
public static void main(String[] args) {
CglibProxy proxy = new CglibProxy();
Train t = (Train)proxy.getProxy(Train.class);
t.move();
}
}
輸出
火車啟動啦??!
火車行駛中。。。。
火車停止啦?。?
上面的程序我都是用sublime手敲的,不知道有沒錯誤。
吃完飯我又在Eclipse運行了一遍,確保沒有錯誤,要對自己和大家負責,不要傳播錯誤的知識,嘿嘿。