1、Proxy 代理模式
- 代理(Proxy)提供了對(duì)目標(biāo)對(duì)象另外的訪問(wèn)方式;即通過(guò)代理對(duì)象訪問(wèn)目標(biāo)對(duì)象.這樣做的好處是:可以在目標(biāo)對(duì)象實(shí)現(xiàn)的基礎(chǔ)上,增強(qiáng)額外的功能操作,即擴(kuò)展目標(biāo)對(duì)象的功能.
- 代理模式可分為靜態(tài)代理和動(dòng)態(tài)代理(JDK動(dòng)態(tài)代理與CGLib動(dòng)態(tài)代理)
1.1靜態(tài)代理
- 靜態(tài)代理在使用時(shí),需要定義接口或者父類(lèi),被代理對(duì)象與代理對(duì)象一起實(shí)現(xiàn)相同的接口或者是繼承相同父類(lèi)。
1)定義接口
public interface Image {
void display();
}
2)接口實(shí)現(xiàn)類(lèi),擴(kuò)展了新功能
public class RealImage implements Image {
private String fileName;
public RealImage(String fileName){
this.fileName = fileName;
loadFromDisk(fileName);
}
@Override
public void display() {
System.out.println("Displaying " + fileName);
}
private void loadFromDisk(String fileName){
System.out.println("Loading " + fileName);
}
}
3)接口代理類(lèi),可以調(diào)用實(shí)現(xiàn)類(lèi)的功能。
public class ProxyImage implements Image{
private RealImage realImage;
private String fileName;
public ProxyImage(String fileName){
this.fileName = fileName;
}
@Override
public void display() {
if(realImage == null){
realImage = new RealImage(fileName);
}
realImage.display();
}
}
4)Demo,通過(guò)代理類(lèi)來(lái)調(diào)用實(shí)體類(lèi)功能。
public class ProxyPatternDemo {
public static void main(String[] args) {
Image image = new ProxyImage("test_10mb.jpg");
// 圖像將從磁盤(pán)加載
image.display();
System.out.println("");
// 圖像不需要從磁盤(pán)加載
image.display();
}
}
5)執(zhí)行程序,輸出結(jié)果:
Loading test_10mb.jpg
Displaying test_10mb.jpg
Displaying test_10mb.jpg
1.2動(dòng)態(tài)代理
- 代理對(duì)象不需要實(shí)現(xiàn)接口,但是目標(biāo)對(duì)象一定要實(shí)現(xiàn)接口,否則不能用動(dòng)態(tài)代理
- JDK代理:只對(duì)有實(shí)現(xiàn)接口的目標(biāo)對(duì)象生成代理,通過(guò)反射機(jī)制實(shí)現(xiàn)aop的動(dòng)態(tài)代理,使用的是委派機(jī)制,在動(dòng)態(tài)生成的實(shí)現(xiàn)類(lèi)里委托處理器去調(diào)用原始實(shí)現(xiàn)類(lèi)的方法。
-Cglib代理: 目標(biāo)對(duì)象沒(méi)有實(shí)現(xiàn)接口,也能生成代理,使用字節(jié)碼處理框架asm,修改字節(jié)碼生成子類(lèi),使用的是繼承機(jī)制,代理類(lèi)繼承被代理類(lèi)。
1.2.1 JDK 自帶的動(dòng)態(tài)代理
java.lang.reflect.Proxy:生成動(dòng)態(tài)代理類(lèi)和對(duì)象;
java.lang.reflect.InvocationHandler(處理器接口):可以通過(guò)invoke方法實(shí)現(xiàn)對(duì)真實(shí)角色的代理訪問(wèn)。
每次通過(guò) Proxy 生成的代理類(lèi)對(duì)象都要指定對(duì)應(yīng)的處理器對(duì)象。
JDK實(shí)現(xiàn)代理只需要使用newProxyInstance方法,但是該方法需要接收三個(gè)參數(shù),完整的寫(xiě)法是:
static Object newProxyInstance(ClassLoader loader, Class<?>[] interfaces,InvocationHandler h )
- ClassLoader loader,:指定當(dāng)前目標(biāo)對(duì)象使用類(lèi)加載器,獲取加載器的方法是固定的
- Class<?>[] interfaces,:目標(biāo)對(duì)象實(shí)現(xiàn)的接口的類(lèi)型,使用泛型方式確認(rèn)類(lèi)型
- InvocationHandler h:事件處理,執(zhí)行目標(biāo)對(duì)象的方法時(shí),會(huì)觸發(fā)事件處理器的方法,會(huì)把當(dāng)前執(zhí)行目標(biāo)對(duì)象的方法作為參數(shù)傳入
1) 接口:Subject.java
public interface Subject {
public int sellBooks();
public String speak();
}
2) 真實(shí)對(duì)象:RealSubject.java
public class RealSubject implements Subject{
@Override
public int sellBooks() {
System.out.println("賣(mài)書(shū)");
return 1 ;
}
@Override
public String speak() {
System.out.println("說(shuō)話");
return "張三";
}
}
3)處理器對(duì)象:MyInvocationHandler.java
public class MyInvocationHandler implements InvocationHandler {
/**
* 因?yàn)樾枰幚碚鎸?shí)角色,所以要把真實(shí)角色傳進(jìn)來(lái)
*/
Subject realSubject ;
public MyInvocationHandler(Subject realSubject) {
this.realSubject = realSubject;
}
/**
*
* @param proxy 代理類(lèi)
* @param method 正在調(diào)用的方法
* @param args 方法的參數(shù)
* @return
* @throws Throwable
*/
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println("調(diào)用代理類(lèi)");
if(method.getName().equals("sellBooks")){
int invoke = (int)method.invoke(realSubject, args);
System.out.println("調(diào)用的是賣(mài)書(shū)的方法");
return invoke ;
}else {
String string = (String) method.invoke(realSubject,args) ;
System.out.println("調(diào)用的是說(shuō)話的方法");
return string ;
}
}
}
4)測(cè)試用例 :Client.java
public class Client {
public static void main(String[] args) {
//真實(shí)對(duì)象
Subject realSubject = new RealSubject();
MyInvocationHandler myInvocationHandler = new MyInvocationHandler(realSubject);
//代理對(duì)象
Subject proxyClass = (Subject) Proxy.newProxyInstance(ClassLoader.getSystemClassLoader(), new Class[]{Subject.class}, myInvocationHandler);
proxyClass.sellBooks();
proxyClass.speak();
}
}
1.2.2 CGLib動(dòng)態(tài)代理
CGLib采用了非常底層的字節(jié)碼技術(shù),其原理是通過(guò)字節(jié)碼技術(shù)為一個(gè)類(lèi)創(chuàng)建子類(lèi),并在子類(lèi)中采用方法攔截的技術(shù)攔截所有父類(lèi)方法的調(diào)用,順勢(shì)織入橫切邏輯。但因?yàn)椴捎玫氖抢^承,所以不能對(duì)final修飾的類(lèi)進(jìn)行代理。JDK動(dòng)態(tài)代理與CGLib動(dòng)態(tài)代理均是實(shí)現(xiàn)Spring AOP的基礎(chǔ)。
1) 目標(biāo)對(duì)象類(lèi):UserDao.java
/**
* 目標(biāo)對(duì)象,沒(méi)有實(shí)現(xiàn)任何接口
*/
public class UserDao {
public void save() {
System.out.println("----已經(jīng)保存數(shù)據(jù)!----");
}
}
2) Cglib代理工廠:ProxyFactory.java
/**
* Cglib子類(lèi)代理工廠
* 對(duì)UserDao在內(nèi)存中動(dòng)態(tài)構(gòu)建一個(gè)子類(lèi)對(duì)象
*/
public class ProxyFactory implements MethodInterceptor{
//維護(hù)目標(biāo)對(duì)象
private Object target;
public ProxyFactory(Object target) {
this.target = target;
}
//給目標(biāo)對(duì)象創(chuàng)建一個(gè)代理對(duì)象
public Object getProxyInstance(){
//1.工具類(lèi)
Enhancer en = new Enhancer();
//2.設(shè)置父類(lèi)
en.setSuperclass(target.getClass());
//3.設(shè)置回調(diào)函數(shù)
en.setCallback(this);
//4.創(chuàng)建子類(lèi)(代理對(duì)象)
return en.create();
}
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
System.out.println("開(kāi)始事務(wù)...");
//執(zhí)行目標(biāo)對(duì)象的方法
Object returnValue = method.invoke(target, args);
System.out.println("提交事務(wù)...");
return returnValue;
}
}
3) 測(cè)試類(lèi)
/**
* 測(cè)試類(lèi)
*/
public class App {
@Test
public void test(){
//目標(biāo)對(duì)象
UserDao target = new UserDao();
//代理對(duì)象
UserDao proxy = (UserDao)new ProxyFactory(target).getProxyInstance();
//執(zhí)行代理對(duì)象的方法
proxy.save();
}
}