本文筆記來自於:狂神的設計模式
代理模式的分類:
- 靜態代理
- 動態代理
一、靜態代理
1.1 角色分析:
- 抽象角色:一般會使用接口或者抽象類來解決
- 真實角色:被代理的角色(eg:房東)
- 代理角色:代理真實角色後,我們一般會做一些附屬操作(eg:中介)
- 客戶:訪問對象的人(eg:租房的人)
1.2 代碼實現
-
首先創建抽象角色,將共同業務放入其中
public interface Rent { void rent();//租房 }
-
創建房東,繼承租房接口
//房東 public class Host implements Rent{ @Override public void rent() { System.out.println("房東租房"); } }
-
創建中介,繼承租房接口,並擴展業務
//中介代理 public class Proxy implements Rent{ private Host host; public Proxy(Host host) { this.host = host; } @Override public void rent() { System.out.println("代理幫忙租房!"); host.rent(); } public void look() { System.out.println("幫忙看房"); } public void agreement() { System.out.println("簽署合同"); } }
-
創建客戶
//客戶 public class Client { public static void main(String[] args) { Host host = new Host(); Proxy proxy = new Proxy(host); proxy.look(); proxy.rent(); proxy.agreement(); } }
調用結果如下:
幫忙看房
代理幫忙租房!
房東租房
簽署合同
代理模式的好處:
● 可以使真實角色的操作更加純粹!不用去關注一 些公共的業務
● 公共業務交給代理角色!實現了業務的分工!
● 公共業務發生擴展的時候,方便集中管理!
缺點:
● 一個真實角色就會產生一個代理角色;代碼量會翻倍開發效率會變低~
1.3 深入理解靜態代理
在現實的項目中,如果我們之前的項目已經寫好了接口(比如增刪改查),那麼我們需要增強功能的時候,不需要修改原來的代碼(在公司中修改原有的代碼是大忌),而是增加一個代理類,達到增強業務的目的。
原有接口和實現方法:
public interface UserService {
void add();
void delete();
void update();
void query();
}
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("增加");//執行的操作
}
@Override
public void delete() {
System.out.println("刪除");
}
@Override
public void update() {
System.out.println("更新");
}
@Override
public void query() {
System.out.println("查詢");
}
}
這時候我們的業務需要在執行每個操作前,輸出日誌,我們不需要去修改UserServiceImpl
的代碼,而是去增加一個代理類UserProxy
public class UserProxy implements UserService{
private UserServiceImpl userServiceImpl;
public void userProxy(UserServiceImpl userServiceImpl) {
this.userServiceImpl = userServiceImpl;
}
@Override
public void add() {
System.out.println("[DEBUG]: 調用了add方法");
userServiceImpl.add();
}
@Override
public void delete() {
System.out.println("[DEBUG]: 調用了delete方法");
userServiceImpl.delete();
}
@Override
public void update() {
System.out.println("[DEBUG]: 調用了update方法");
userServiceImpl.update();
}
@Override
public void query() {
System.out.println("[DEBUG]: 調用了query方法");
userServiceImpl.query();
}
}
二、動態代理
- 動態代理和靜態代理角色一樣
- 動態代理的代理類是動態生成的,不是我們直接寫好的
- 動態代理分爲兩大類:基於接口的動態代理,基於類的動態代理
- 基於接口–JDK動態代理
- 基於類:cglib
需要了解兩個類:Proxy 和 InvocationHandler
2.1 代碼實現
還是以UserService的例子來說:
public interface UserService {
void add();
void delete();
void update();
void query();
}
public class UserServiceImpl implements UserService {
@Override
public void add() {
System.out.println("增加");
}
@Override
public void delete() {
System.out.println("刪除");
}
@Override
public void update() {
System.out.println("更新");
}
@Override
public void query() {
System.out.println("查詢");
}
}
不過我們動態代理裏面不需要自己寫代理類了,而是使用一個生成代理類的機制,需要擴展的方法在invoke中調用:
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
//代理調用處理程序
//會用這個類自動生成代理類
public class ProxyInvocatinHandler implements InvocationHandler{
//被代理的接口
private Object in;
public void setRent(Object in) {
this.in = in;
}
public Object getProxy() {
return Proxy.newProxyInstance(this.getClass().getClassLoader(), in.getClass().getInterfaces(), this);
}
//處理代理實例,並返回結果
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
//動態代理的本質,就是使用反射實現的
log(method.getName());//需要擴展的業務
Object result = method.invoke(in, args);
return result;
}
private void log(String msg) {
System.out.println("[DEBUG]執行了 "+msg+" 的方法");
}
}
然後我們使用這個機制,根據被代理的對象自動生成代理類:
public class Client {
public static void main(String[] args) {
//真實角色
Host host = new Host();
//代理角色
ProxyInvocatinHandler pih = new ProxyInvocatinHandler();
//通過調用程序處理角色
pih.setRent(host);
//獲取代理
Rent proxy = (Rent)pih.getProxy();
proxy.rent();
}
}
運行結果:
[DEBUG]執行了 add 的方法
增加
應用場景:下次需要在某個方法前打印日誌時,可以直接使用這個動態代理的處理類來代理,不受接口類型的限制,同樣可以運用到房東買房的例子
動態代理的好處:
● 可以使真實角色的操作更加純粹!不用去關注一 些公共的業務
● 公共也就就交給代理角色!實現了業務的分工!
● 公共業務發生擴展的時候,方便集中管理!
● 一個動態代理類代理的是一個接口,一般就是對應的一類業務
//獲取代理
Rent proxy = (Rent)pih.getProxy();
proxy.rent();
}
}
運行結果:
[DEBUG]執行了 add 的方法
增加
應用場景:下次需要在某個方法前打印日誌時,可以直接使用這個動態代理的處理類來代理,不受接口類型的限制,同樣可以運用到房東買房的例子
動態代理的好處:
● 可以使真實角色的操作更加純粹!不用去關注一 些公共的業務
● 公共也就就交給代理角色!實現了業務的分工!
● 公共業務發生擴展的時候,方便集中管理!
● 一個動態代理類代理的是一個接口,一般就是對應的一類業務
●一個動態代理類可以代理多個類,只要是實現了同一個接口即可