1 代理模式概要
- 分類:靜態(tài)代理彼乌、動態(tài)代理
- 角色:抽象接口汁展、代理對象端三、被代理對象(目標)汉嗽,由被代理對象來做最終的決定
- 代理對象通常來說會持有被代理對象的引用(以便代理角色完成工作之前,或者之后能找到被代理對象返顺,能夠通知被代理對象)
2 代理的實現(xiàn)
2.1 靜態(tài)代理
定義一個抽象接口 Person
憨攒,定義一個實現(xiàn)類 Son
冈敛,創(chuàng)建一個對象 Mother
實現(xiàn)接口 Person
昂羡,同時持有 Person
的引用絮记,完整代碼如下
-
Person
接口
package com.lushwe.pattern.proxy;
/**
* 說明:人
*
* @author Jack Liu
* @date 2019-05-27 10:38
* @since 1.0
*/
public interface Person {
/**
* 找對象
*/
void findLove();
/**
* 找工作
*/
void findJob();
}
-
Son
類
package com.lushwe.pattern.proxy;
/**
* 說明:兒子
*
* @author Jack Liu
* @date 2019-05-27 10:38
* @since 1.0
*/
public class Son implements Person {
public void findLove() {
System.out.println("兒子找對象,膚白貌美大長腿");
}
public void findJob() {
System.out.println("兒子找工作虐先,錢多事少離家近");
}
}
-
Mother
類
package com.lushwe.pattern.proxy.staticed;
import com.lushwe.pattern.proxy.Person;
/**
* 說明:母親(靜態(tài)代理)
*
* @author Jack Liu
* @date 2019-05-27 10:40
* @since 1.0
*/
public class Mother implements Person {
private Person person;
public Mother(Person person) {
this.person = person;
}
public void findLove() {
System.out.println("媽媽幫忙物色對象");
this.person.findLove();
System.out.println("對象是否合適");
}
public void findJob() {
// do nothing
}
}
測試代碼
package com.lushwe.pattern.proxy.staticed;
import com.lushwe.pattern.proxy.Son;
/**
* 說明:靜態(tài)代理測試
*
* @author Jack Liu
* @date 2019-05-27 10:43
* @since 1.0
*/
public class StaProxyTest {
public static void main(String[] args) {
Mother mother = new Mother(new Son());
mother.findLove();
}
}
運行 StaProxyTest
怨愤,日志打印結果如下
媽媽幫忙物色對象
兒子找對象,膚白貌美大長腿
對象是否合適
總結
- 優(yōu)點:開發(fā)簡單
- 缺點:實現(xiàn)類
Son
方法發(fā)生變化蛹批,代理類Mother
也要調(diào)整撰洗,不符合開閉原則
2.2 動態(tài)代理
2.2.1 JDK代理
創(chuàng)建 JdkMatchmaker
類篮愉,實現(xiàn) InvocationHandler
接口,通過 Proxy
工具類創(chuàng)建代理對象差导,完整代碼如下
package com.lushwe.pattern.proxy.jdk;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
/**
* 說明:媒婆(Jdk代理)
*
* @author Jack Liu
* @date 2019-05-27 11:15
* @since 1.0
*/
public class JdkMatchmaker<T> implements InvocationHandler {
/**
* 被代理對象
*/
private T target;
/**
* 返回代理對象實例
*
* @param target
* @return
*/
public T getInstance(T target) {
this.target = target;
Class<?> targetClass = target.getClass();
return (T) Proxy.newProxyInstance(targetClass.getClassLoader(), targetClass.getInterfaces(), this);
}
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("媒婆幫忙物色對象");
// 調(diào)用被代理對象方法
method.invoke(this.target, args);
System.out.println("對象是否合適");
return null;
}
}
測試代碼
package com.lushwe.pattern.proxy.jdk;
import com.lushwe.pattern.proxy.Person;
import com.lushwe.pattern.proxy.Son;
/**
* 說明:Jdk動態(tài)代理測試類
*
* @author Jack Liu
* @date 2019-05-27 11:19
* @since 1.0
*/
public class JdkProxyTest {
public static void main(String[] args) {
JdkMatchmaker<Person> jdkMatchmaker = new JdkMatchmaker();
Person person = jdkMatchmaker.getInstance(new Son());
person.findLove();
}
}
運行 JdkProxyTest
试躏,日志打印結果如下
媒婆幫忙物色對象
兒子找對象,膚白貌美大長腿
對象是否合適
總結
- 優(yōu)點:動態(tài)生成代理類柿汛,被代理類接口變更冗酿,
JdkMatchmaker
不需要修改 - 缺點:被代理類必須實現(xiàn)接口
2.2.2 CGlib代理
創(chuàng)建 CglibMatchmaker
類埠对,實現(xiàn) MethodInterceptor
接口络断,通過 Enhancer
工具類創(chuàng)建代理對象,完整代碼如下
package com.lushwe.pattern.proxy.cglib;
import net.sf.cglib.proxy.Enhancer;
import net.sf.cglib.proxy.MethodInterceptor;
import net.sf.cglib.proxy.MethodProxy;
import java.lang.reflect.Method;
/**
* 說明:媒婆(Cglib代理)
*
* @author Jack Liu
* @date 2019-05-30 11:12
* @since 1.0
*/
public class CglibMatchmaker<T> implements MethodInterceptor {
public T getInstance(Class<T> clazz) {
Enhancer enhancer = new Enhancer();
enhancer.setSuperclass(clazz);
enhancer.setCallback(this);
return (T) enhancer.create();
}
public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable {
System.out.println("媒婆開始物色對象");
methodProxy.invokeSuper(o, objects);
System.out.println("如果適合项玛,交往");
return null;
}
}
測試代碼
package com.lushwe.pattern.proxy.cglib;
/**
* 說明:Tom
*
* @author Jack Liu
* @date 2019-05-30 11:12
* @since 1.0
*/
public class Tom {
public void findLove() {
System.out.println("Tom找對象貌笨,膚白貌美大長腿");
}
}
package com.lushwe.pattern.proxy.cglib;
/**
* 說明:Cglib動態(tài)代理測試類
*
* @author Jack Liu
* @date 2019-05-30 11:17
* @since 1.0
*/
public class CglibProxyTest {
public static void main(String[] args) {
CglibMatchmaker<Tom> cglibMatchmaker = new CglibMatchmaker();
Tom tom = cglibMatchmaker.getInstance(Tom.class);
tom.findLove();
}
}
運行 CglibProxyTest
,日志打印結果如下
媒婆開始物色對象
Tom找對象襟沮,膚白貌美大長腿
如果適合锥惋,交往
總結
-
優(yōu)點:
- i 動態(tài)生成代理類,被代理類接口變更开伏,
CglibMatchmaker
不需要修改 - ii 代理類不需要實現(xiàn)接口
- i 動態(tài)生成代理類,被代理類接口變更开伏,
-
缺點:
- i 不能對
final
類進行繼承
- i 不能對
3 代理模式的應用
-
Spring Aop
就是運用動態(tài)代理實現(xiàn)膀跌,當被代理類實現(xiàn)了接口,使用Jdk動態(tài)代理
固灵,沒有實現(xiàn)接口捅伤,則使用Cglib動態(tài)代理
-
MyBatis DAO
也是運用動態(tài)代理,使用MyBatis
開發(fā)DAO
過程中巫玻,并不需要寫DAO
的實現(xiàn)類丛忆,由MyBatis
通過Jdk動態(tài)代理
自動生成DAO
的實現(xiàn)類