1.代理模式
定義:代理模式(Proxy),為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的方法。

2.應(yīng)用場景
(1)遠(yuǎn)程代理:也就是為一個(gè)對(duì)象在不同的地址空間提供局部代表,這樣可以隱藏一個(gè)對(duì)象存在于不同地址空間的事實(shí),例如,RPC遠(yuǎn)程過程調(diào)用。
(2)虛擬代理:是根據(jù)需要?jiǎng)?chuàng)建開銷很大的對(duì)象。通過它來存放實(shí)例化需要很長時(shí)間的真實(shí)對(duì)象,以達(dá)到性能的最優(yōu)化。
(3)安全代理:用來控制真實(shí)對(duì)象訪問時(shí)的權(quán)限。
(4)智能引用:是指當(dāng)調(diào)用真實(shí)的對(duì)象時(shí),代理處理另外一些事。如計(jì)算真實(shí)對(duì)象的引用次數(shù),這樣當(dāng)該對(duì)象沒有引用時(shí),可以自動(dòng)釋放它。
3.代理類型
下面我們通過一個(gè)購票流程來詳細(xì)說明一下靜態(tài)代理、動(dòng)態(tài)代理和Cglib代理三種代理的優(yōu)缺點(diǎn)。以前沒有12306軟件的時(shí)候,購買火車票只能到售票處購買,現(xiàn)在有了12306軟件以后,可以實(shí)現(xiàn)在線購買火車票的功能了,售票處是真實(shí)實(shí)體,12306軟件就是售票處的代理類。
(1)靜態(tài)代理
package com.nwpu.pattern.proxy.statics;
public interface Subject {
void buyTicket();
}
package com.nwpu.pattern.proxy.statics;
public class TicketOffice implements Subject {
@Override
public void buyTicket() {
System.out.println("成功買到一張票!");
}
}
package com.nwpu.pattern.proxy.statics;
public class TicketSoftware implements Subject {
// 代理類維持一個(gè)真實(shí)實(shí)體的引用
private TicketOffice office;
public void setTicketOffice(TicketOffice office) {
this.office = office;
}
@Override
public void buyTicket() {
office.buyTicket();
}
}
package com.nwpu.pattern.proxy.statics;
public class Client {
public static void main(String[] args) {
TicketOffice office = new TicketOffice();
TicketSoftware software = new TicketSoftware();
software.setTicketOffice(office);
software.buyTicket();
}
}
從上述例子我們可以得知,靜態(tài)代理的代理關(guān)系在編譯期間就已確定,適合代理類較少且確定的場景,否則要新建大量的代理類。靜態(tài)代理模式要求目標(biāo)類和代理類要實(shí)現(xiàn)共同的接口,當(dāng)目標(biāo)類中有大量方法時(shí),代理類也需要重寫大量的方法,違背重復(fù)代碼只寫一次原則。此外,當(dāng)接口新增方法時(shí),代理類和目標(biāo)類也需要實(shí)現(xiàn)此方法,增加了代碼的維護(hù)成本。
(2)動(dòng)態(tài)代理
package com.nwpu.pattern.proxy.dynamic;
public interface Subject {
void buyTicket();
}
package com.nwpu.pattern.proxy.dynamic;
public class TicketOffice implements Subject {
@Override
public void buyTicket() {
System.out.println("成功買到一張票!");
}
}
package com.nwpu.pattern.proxy.dynamic;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class MyInvocationHandler implements InvocationHandler {
private TicketOffice ticketOffice;
public void setTicketOffice(TicketOffice ticketOffice) {
this.ticketOffice = ticketOffice;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
Object result = method.invoke(ticketOffice, args);
return result;
}
}
package com.nwpu.pattern.proxy.dynamic;
import java.lang.reflect.Proxy;
public class Client {
public static void main(String[] args) {
TicketOffice office = new TicketOffice();
MyInvocationHandler handler = new MyInvocationHandler();
handler.setTicketOffice(office);
Subject proxy = (Subject) Proxy.newProxyInstance(office.getClass().getClassLoader(),
office.getClass().getInterfaces(), handler);
proxy.buyTicket();
}
}
從上述示例中我們可以得知,利用動(dòng)態(tài)代理可以實(shí)現(xiàn)在運(yùn)行時(shí)創(chuàng)建一個(gè)實(shí)現(xiàn)了一組給定接口的新類,這種功能只有在編譯時(shí)無法確定需要實(shí)現(xiàn)哪個(gè)接口時(shí)才有必要使用到。
(3)Cglib代理
jdk動(dòng)態(tài)代理依賴于接口的實(shí)現(xiàn),而當(dāng)我們只有類沒有接口的時(shí)候就需要使用另一種動(dòng)態(tài)代理技術(shù) Cglib動(dòng)態(tài)代理。Cglib代理是針對(duì)類來實(shí)現(xiàn)代理的,原理是對(duì)指定的目標(biāo)類生成一個(gè)子類并重寫其中業(yè)務(wù)方法來實(shí)現(xiàn)代理。
代理類對(duì)象是由 Enhancer 類創(chuàng)建的,Cglib創(chuàng)建動(dòng)態(tài)代理類的模式如下:
- a.查找目標(biāo)類上的所有非 final 的 public 類型的方法 (final 的不能被重寫);
- b.將這些方法的定義轉(zhuǎn)成字節(jié)碼;
- c.將組成的字節(jié)碼轉(zhuǎn)換成相應(yīng)的代理的 Class 對(duì)象然后通過反射獲得代理類的實(shí)例對(duì)象;
- d.實(shí)現(xiàn) MethodInterceptor 接口, 用來處理對(duì)代理類上所有方法的請(qǐng)求。
package com.nwpu.pattern.proxy.cglib;
public class TicketOffice {
public void buyTicket() {
System.out.println("成功買到一張票!");
}
}
package com.nwpu.pattern.proxy.cglib;
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 {
public TicketOffice creatProxyedObj(CglibProxy cglibProxy) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(TicketOffice.class);
enhancer.setCallback(cglibProxy);
return (TicketOffice) enhancer.create();
}
@Override
public Object intercept(Object object, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
return methodProxy.invokeSuper(object, args);
}
}
package com.nwpu.pattern.proxy.cglib;
public class Client {
public static void main(String[] args) {
CglibProxy proxy = new CglibProxy();
TicketOffice ticketOffice = proxy.creatProxyedObj(proxy);
ticketOffice.buyTicket();
}
}
從上述示例我們可以得知:對(duì)于需要被代理的目標(biāo)類,它只是動(dòng)態(tài)生成一個(gè)子類以覆蓋非 final 的方法,同時(shí)綁定鉤子回調(diào)自定義的攔截器。不同于jdk動(dòng)態(tài)代理,我們不能使用目標(biāo)對(duì)象來創(chuàng)建代理,目標(biāo)對(duì)象只能被 Cglib創(chuàng)建。在示例中,默認(rèn)的無參構(gòu)造方法enhancer.create()被使用來創(chuàng)建目標(biāo)對(duì)象。
原創(chuàng)不易,如需轉(zhuǎn)載,請(qǐng)注明出處@author Davince!