1、代理模式
代理對象代表目標對象被訪問,代理類可以在目標對象實現的基礎上,增強額外的功能操作,即擴展目標對象的功能。
2、常見的三種代理實現
3、靜態代理
- 代理類和被代理類實現相同的接口或繼承相同的父類,代理對象通過他們的共同的接口或父類訪問被代理類。
- 實現:下面是問手機(YourPhone)的價格,但實際上是問華爲手機的價格。
//公共接口:手機
publicinterface Phone{
publicvoid price();
}
//被代理類:華爲
classHuaWei implements Phone{
@Override
publicvoid price() {
System.out.println("1000RMB");
}
}
//代理類
classYourPhone implements Phone{
HuaWeiphone; //必須要有被代理類的對象
publicYourPhone(HuaWei phone){
this.phone=phone;
}
@Override
publicvoid price() {
phone.price(); //實際是訪問被代理類的價格
}
}
//測試代理
publicvoid testProxy(){
Phonep=newYourPhone(newHuaWei()); //代理對象
p.price(); //訪問代理類的價格
}
- 優點:可以看到:在YourPhone類的price函數里加上其他功能,如:敬語。即添加額外功能。
- 缺點:每一個類都應要有一個代理類,即類數量增多了。接口一旦要更改,其實現類也需要更改。
4、java JDK動態代理
- 特點:代理類不需要實現共同接口或繼承共同父類,直接通過JDK的API生成代理對象。
- 要動態創建代理對象需要用到InvocationHandler這個接口
//公共接口:手機
publicinterface Phone{
publicvoid price();
}
//被代理類:華爲
classHuaWei implements Phone{
@Override
publicvoid price() {
System.out.println("1000RMB");
}
}
/*編寫一個動態代理,不需實現接口,但需要制定接口。
* 值得注意的是,這可以代理不同的被代理類了*/
classProxyFactory{
Objectphone; //被代理類對象,不應指定某個代理類,讓其動態綁定
publicProxyFactory(Object phone) {
this.phone=phone;
}
//h獲取代理對象
publicObject getProxyInstance() {
//創建被代理類的事務處理器
InvocationHandlerih=newInvocationHandler() {
@Override
publicObject invoke(Object proxy, Methodmethod, Object[]args)throws Throwable {
returnmethod.invoke(phone,args);
}
};
/*
* phone.getClass().getClassLoader():被代理類的類加載器
* phone.getClass().getInterfaces():被代理類的接口
*/
returnProxy.newProxyInstance(phone.getClass().getClassLoader(),phone.getClass().getInterfaces(),ih);
}
}
//測試動態代理
publicvoid testProxy(){
Phonep=newHuaWei();//指定具體代理類
PhoneDProxy=(Phone)newProxyFactory(p).getProxyInstance(); //創建動態代理對象
DProxy.price();
}
5、動態代理與靜態代理的區別
- 上面說過,動態代理不用創建每一個代理類。即對於2個被代理類,靜態代理需要創建2個代理類,而動態代理只需創建另一個動態代理對象即可。
- 下面增加apple手機的類,高亮的爲新增代碼
//公共接口:手機
public
interface
Phone{
public
void
price();
}
//被代理類:華爲
class
HuaWei
implements
Phone{
@Override
public
void
price() {
System.out.println("1000RMB");
}
}
//被代理類:蘋果
class
Apple
implements
Phone{
@Override
public
void
price() {
System.out.println("5000RMB");
}
}
//代理類HuaWei
class
YourPhone
implements
Phone{
HuaWei
phone; //必須要有被代理類的對象
public
YourPhone(HuaWei
phone){
this.phone=phone;
}
@Override
public
void
price() {
phone.price();
//實際是訪問被代理類的價格
}
}
//代理類apple
class
YourPhone2
implements
Phone{
Apple
phone; //必須要有被代理類的對象
public
YourPhone2(Apple
phone){
this.phone=phone;
}
@Override
public
void
price() {
phone.price();
//實際是訪問被代理類的價格
}
}
//測試靜態代理
public
void
testProxy2(){
Phone
p=new
YourPhone(new
HuaWei()); //代理對象
Phone
p2=new
YourPhone2(new
Apple());
p.price();
//訪問代理類的價格
p2.price();
}
//公共接口:手機
public
interface
Phone{
public
void
price();
}
//被代理類:華爲
class
HuaWei
implements
Phone{
@Override
public
void
price() {
System.out.println("1000RMB");
}
}
//被代理類:蘋果
class
Apple
implements
Phone{
@Override
public
void
price() {
System.out.println("5000RMB");
}
}
/*編寫一個動態代理,不需實現接口,但需要制定接口。
* 值得注意的是,這可以代理不同的被代理類了*/
class
ProxyFactory{
Object
phone; //被代理類對象,不應指定某個代理類,讓其動態綁定
public
ProxyFactory(Object
phone) {
this.phone=phone;
}
//h獲取代理對象
public
Object getProxyInstance() {
//創建被代理類的事務處理器
InvocationHandler
ih=new
InvocationHandler() {
@Override
public
Object invoke(Object
proxy, Method
method, Object[]
args)
throws
Throwable {
return
method.invoke(phone,
args);
}
};
/*
* phone.getClass().getClassLoader():被代理類的類加載器
* phone.getClass().getInterfaces():被代理類的接口
*/
return
Proxy.newProxyInstance(phone.getClass().getClassLoader(),phone.getClass().getInterfaces(),ih);
}
}
//測試動態代理
public
void
testProxy(){
Phone
p=new
HuaWei();//指定具體被代理類
Phone
DProxy=(Phone)new
ProxyFactory(p).getProxyInstance(); //創建動態代理對象
DProxy.price();
Phone
p2=new
Apple();
Phone
DProxy2=(Phone)new
ProxyFactory(p2).getProxyInstance();
DProxy2.price();
}
6、Cglib代理
- Cglib代理也是動態代理,其與JDK代理不同的地方在於:JDK的被代理類需實現接口,而Cglib代理的被代理類不需實現接口。Cglib以被代理類的子類對象作爲其代理類。
- 需下載底層包:Cglib-nodep 或其核心包Cglib-core
- 將下載的jar包導入項目:右鍵項目——Properties——Java Build Path,選Libraries選卡項,點擊Add External JARs...。選擇包導入後確認即可
- Cglib包的底層是通過使用一個字節碼處理框架ASM來轉換字節碼並生成新的類.不鼓勵直接使用ASM,因爲它要求你必須對JVM內部結構包括class文件的格式和指令集都很熟悉。
- 被代理的類不能爲final,否則報錯
- 被代理對象的方法如果爲final/static,那麼就不會被攔截,而是直接執行該方法.
//被代理類(需要注意的是,該類不能是內部類)
publicclass HuaWei {
publicvoid price() {
System.out.println(this+"1000RMB");
}
}
//另起一類
publicclass Cglib {
//Cglib代理
classProxyFactory2 implements MethodInterceptor{
Objectphone;
publicProxyFactory2(Object phone) {
this.phone=phone;
}
publicObject getProxyInstance() {
Enhancerenhance=newEnhancer(); //工具類
enhance.setSuperclass(phone.getClass()); //設置父類:被代理類
enhance.setCallback(this); //設置回調函數,這裏的this是函數所在的類
//返回創建的子類:代理對象
returnenhance.create();
}
/**
* 執行被代理類的方法時攔截,並執行該方法
*@param
* obj:被代理類的對象
* method:被代理類的方法反射對象
* args:方法參數
* proxy:代理類對象
*/
@Override
publicObject intercept(Object obj, Methodmethod, Object[]args, MethodProxyproxy)throws Throwable {
returnmethod.invoke(phone,args);
//寫法2
// proxy.invokeSuper(obj,args);
// return null;
}
}
//測試Cglib代理
publicvoid testProxy3() {
HuaWeihw=newHuaWei();
HuaWeiproxy=(HuaWei)newProxyFactory2(hw).getProxyInstance();
proxy.price();
}
publicstatic void main(String[] args) {
//TODO Auto-generated method stub
Cglibc=newCglib();
c.testProxy3();
}
}
- 程序分析:從testProxy3()函數開始,生成HuaWei類對象,這裏我踩了一下坑:先前把這個類設置爲內部類(原因不知道是什麼,估計要看源碼,望告知)。然後生成ProxyFactory2對象,主要是爲了執行get類的getProxyInstance()方法,從而得到被代理類的子類:代理對象
- 該代理對象執行被代理類的方法時,都會被intercept 方法攔截(爲了得到被代理對象)。