前言:
在開(kāi)發(fā)中一定被老大這樣說(shuō)過(guò)不要隨意去修改別人已經(jīng)寫(xiě)好的代碼或者方法。但我們又希望原來(lái)的對(duì)象可以得到擴(kuò)展扇雕,又能保護(hù)原對(duì)象,這時(shí)代理模式就出來(lái)了。
在百科中這樣描述(定義):為其他對(duì)象提供一種代理以控制對(duì)這個(gè)對(duì)象的訪問(wèn)地熄。在某些情況下,一個(gè)對(duì)象不適合或者不能直接引用另一個(gè)對(duì)象芯杀,而代理對(duì)象可以在客戶端和目標(biāo)對(duì)象之間起到中介的作用端考。aop的實(shí)現(xiàn)對(duì)方法前后進(jìn)行攔截雅潭,改變?cè)袑?duì)象或方法都是基于代理模式。代理模式分為靜態(tài)代理和jdk動(dòng)態(tài)代理却特。
靜態(tài)代理
需求:張三看上了妹子扶供,怕被拒絕而苦惱,于是李四說(shuō)我替你把她約出來(lái)裂明。
- 定義代理和被代理的公共的接口
//張三和李四的公共行為
public interface Person {
void meetGirl();
}
- 實(shí)現(xiàn)person接口,即具體動(dòng)作的實(shí)現(xiàn)
//動(dòng)作的具體實(shí)現(xiàn)
public class Man implements Person{
private String name;
public Man(String name){
this.name = name;
}
@Override
public void meetGirl() {
System.out.println(name+"正在和女孩吃飯椿浓,感謝你李四");
}
}
- 持有一個(gè)被代理對(duì)象的代理類,同樣要實(shí)現(xiàn)person接口
//man代理類
public class ManProxy implements Person {
//被代理的對(duì)象
private Man man;
//代理
public ManProxy(Person man){
this.man = (Man) man;
}
//被代理的執(zhí)行接口方法
@Override
public void meetGirl() {
man.meetGirl();
}
}
- client 測(cè)試闽晦,李四替張三約妹子
//代理的測(cè)試類
public class ProxyClient {
@Test
public void test(){
//被代理的對(duì)象
Person zhangsan = new Man("張三");
//生成代理對(duì)象李四 幫張三去約姑娘扳碍,把張三傳給代理對(duì)象
Person lisi = new ManProxy(zhangsan);
lisi.meetGirl();
}
}
動(dòng)態(tài)代理參考
代理類在程序運(yùn)行時(shí)創(chuàng)建的代理方式被成為動(dòng)態(tài)代理(生成相應(yīng)的class文件在加入jvm)。 我們上面靜態(tài)代理的例子中仙蛉,代理類(ManProxy實(shí)現(xiàn)了接口和業(yè)務(wù)調(diào)用)是自己定義好的笋敞,在程序運(yùn)行之前就已經(jīng)編譯完成。然而動(dòng)態(tài)代理荠瘪,代理類并不是在代碼中定義的夯巷,而是在運(yùn)行時(shí)根據(jù)我們?cè)诖a中的“指示”動(dòng)態(tài)生成的, 動(dòng)態(tài)代理的優(yōu)勢(shì)在于可以很方便的對(duì)代理類的函數(shù)進(jìn)行統(tǒng)一的處理巧还,而不用修改每個(gè)代理類中的方法鞭莽。動(dòng)態(tài)代理分為兩種,一種分為基于接口的jdk代理麸祷;另一種則是基于類的代理如cglib(spring aop 實(shí)現(xiàn)),javassist等澎怒。
jdk 動(dòng)態(tài)代理
JVM根據(jù)傳進(jìn)來(lái)的 業(yè)務(wù)實(shí)現(xiàn)類對(duì)象 以及 方法名 ,動(dòng)態(tài)地創(chuàng)建了一個(gè)代理類的class文件并被字節(jié)碼引擎執(zhí)行阶牍,然后通過(guò)該代理類對(duì)象進(jìn)行方法調(diào)用喷面。我們需要做的,只需指定代理類的預(yù)處理走孽、調(diào)用后操作惧辈;在java的java.lang.reflect包下提供了一個(gè)Proxy類和一個(gè)InvocationHandler接口,實(shí)現(xiàn)InvocationHandler接口就可以生成被代理的對(duì)象磕瓷。
- 假如如以上person 接口不變
- 業(yè)務(wù)邏輯的具體實(shí)現(xiàn)
public class RealPerson implements Person {
@Override
public void meetGirl() {
System.out.println("他們說(shuō)你最美盒齿,來(lái)自jdk動(dòng)態(tài)代理");
}
}
3 . 實(shí)現(xiàn)InvocationHandler接口定義代理類
//實(shí)現(xiàn)invocationHandler 接口的jdk動(dòng)態(tài)代理類
public class SubjectHandler implements InvocationHandler {
//被代理的對(duì)象,包含相應(yīng)的業(yè)務(wù)方法
private Object target;
//綁定被代理的對(duì)象
public SubjectHandler(Object target){
this.target = target;
}
// //或者如下調(diào)用是會(huì)更加簡(jiǎn)單 直接new SubjectHandler.bind(targect)
// public Object bind(Object target) {
// this.target = target; //接收業(yè)務(wù)實(shí)現(xiàn)類對(duì)象
//
// //通過(guò)反射機(jī)制,創(chuàng)建一個(gè)代理類對(duì)象實(shí)例并返回困食。用戶進(jìn)行方法調(diào)用時(shí)使用
// //創(chuàng)建代理對(duì)象時(shí)边翁,需要傳遞該業(yè)務(wù)類的類加載器、接口硕盹、handler實(shí)現(xiàn)類
// return Proxy.newProxyInstance(target.getClass().getClassLoader(),
// target.getClass().getInterfaces(), this);
// }
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//具體要執(zhí)行的邏輯
Object result = null;
System.out.println("代理前的操作-----");
//合并邏輯符匾,并通過(guò)return決定是否執(zhí)行
result = method.invoke(target,args);
System.out.println("方法處理后的操作-----");
//執(zhí)行后續(xù)操作
return result;
}
}
- 測(cè)試
public class JdkProxyClient {
@Test
public void test(){
//需要代理的目標(biāo)對(duì)象
RealPerson realPerson = new RealPerson();
//攔截器
SubjectHandler interceptor = new SubjectHandler(realPerson);
//生成代理類對(duì)象,執(zhí)行代理類的業(yè)務(wù)方法
//newProxyInstance 參數(shù):1.目標(biāo)類的加載器 2.目標(biāo)類接口 3.攔截器(處理的類)
Person person = (Person) Proxy.newProxyInstance(realPerson.getClass().getClassLoader(),realPerson.getClass().getInterfaces(),interceptor);
//執(zhí)行代理的業(yè)務(wù)方法
person.meetGirl();
}
}
spring 中的cglib 動(dòng)態(tài)代理
JDK動(dòng)態(tài)代理的代理對(duì)象在創(chuàng)建時(shí)瘩例,需要根據(jù)接口內(nèi)的方法名進(jìn)行調(diào)用啊胶,如果業(yè)務(wù)實(shí)現(xiàn)類是沒(méi)有實(shí)現(xiàn)接口或者業(yè)務(wù)實(shí)現(xiàn)類中新增了接口中沒(méi)有的方法甸各,就無(wú)法使用JDK動(dòng)態(tài)代理了(因?yàn)闊o(wú)法被調(diào)用),spirng 的cglib是針對(duì)實(shí)現(xiàn)代理的需要實(shí)現(xiàn)MethodInterceptor 接口
- 假設(shè)以上實(shí)現(xiàn)類不變,接口可有可無(wú)焰坪,則定義代理類如下
//基于spring 的cglib的動(dòng)態(tài)代理
public class CglibInterceptor implements MethodInterceptor {
//代理對(duì)象
private Object target;
//綁定被代理的對(duì)象
public Object createProxyObject(Object obj) {
this.target = obj;
//創(chuàng)建代理類
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(obj.getClass());
enhancer.setCallback(this);
Object proxyObj = enhancer.create();
return proxyObj;// 返回代理對(duì)象
}
@Override
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
Object result = null;
//執(zhí)行業(yè)務(wù)邏輯
System.out.println("我是cglib代理");
//合并邏輯
result = method.invoke(target,objects);
return result; //返回結(jié)果
}
}
- 測(cè)試
public class CglibProxyClient {
@Test
public void test(){
//目標(biāo)對(duì)象
RealPerson realPerson = new RealPerson();
//攔截器
CglibInterceptor cglibInterceptor = new CglibInterceptor();
//獲得代理對(duì)象,區(qū)別jdk動(dòng)態(tài)趣倾,不需要接口
RealPerson cglib = (RealPerson) cglibInterceptor.createProxyObject(realPerson);
cglib.meetGirl();
}
}
總結(jié)
1.JDK動(dòng)態(tài)代理只能對(duì)實(shí)現(xiàn)了接口的類生成代理,通過(guò)Proxy.newProxyInstance產(chǎn)生代理對(duì)象而不能針對(duì)類琳彩。
2.CGLIB是針對(duì)類實(shí)現(xiàn)代理誊酌,主要是對(duì)指定的類生成一個(gè)子類部凑,覆蓋其中的方法