學過設計模式的編友們,一定對代理模式很熟悉,最近在學習中發現Spring中更好的使用了動態帶來來降低耦合,提高代碼復用性,那么為什么要使用動態代理?動態代理和我們之前使用的普通代理有什么區別和好處?
**? 預熱代理模式**

代理是代替某個對象去控制目標對象,且代理類不會改變原來的接口,在代理的同時控制相關的目標;代理類和真實對象目的相同;
**? 情景設置**
現有一段程序,程序中有userManager的接口和UserManagerImpl的實現類,接口和實現類中都有對應實現的增刪改查和安全校驗的方法:
UserManagerImpl實現類:
~~~
<span style="font-size:18px;">package com.bjpowernode.spring;
public class UserManagerImpl implements UserManager {
@Override
public void addUser(String username, String password) {
checkSecurity();
System.out.println("----UserManagerImpl:addUser-----");
}
@Override
public void delUser(int userId) {
checkSecurity();
System.out.println("----UserManagerImpl:delUser-----");
}
@Override
public String findUserByid(int userId) {
checkSecurity();
System.out.println("----UserManagerImpl:findUserByid-----");
return "張三";
}
@Override
public void modefyUser(int userId, String username, String password) {
//引入安全性校驗的方法
checkSecurity();
System.out.println("----UserManagerImpl:modefyUser-----");
}
/**
* 校驗安全性的方法
*/
private void checkSecurity(){
System.out.println("----UserManagerImpl:checkSecurity-----");
}
}
</span>
~~~
當我們需要對增刪改查中的安全校驗方法進行修改,或者刪除四個方法中對“checkSecurity”的調用,我們需要更改四個方法,這樣一來,如果上萬個方法調用了“checkSecurity”方法我們就需要更改上萬個方法,很明顯不符合開閉原則,該實現類與checkSecurity方法之間的耦合太高;于是我們采用靜態代理的方法來降低耦合:
**? 回顧靜態代理:**
為了降低耦合,我們為UserManagerImpl添加一個代理類UserManagerImplProxy;這兩個類都實現UserManager的接口:
UserManagerImpl 類:
~~~
<span style="font-size:18px;">package com.bjpowernode.spring;
public class UserManagerImpl implements UserManager {
@Override
public void addUser(String username, String password) {
//checkSecurity();
System.out.println("----UserManagerImpl:addUser-----");
}
@Override
public void delUser(int userId) {
//checkSecurity();
System.out.println("----UserManagerImpl:delUser-----");
}
@Override
public String findUserByid(int userId) {
//checkSecurity();
System.out.println("----UserManagerImpl:findUserByid-----");
return "張三";
}
@Override
public void modefyUser(int userId, String username, String password) {
//引入安全性校驗的方法
//checkSecurity();
System.out.println("----UserManagerImpl:modefyUser-----");
}
}
</span>
~~~
UserManagerImplProxy代理類:
~~~
<span style="font-size:18px;">package com.bjpowernode.spring;
public class UserManagerImplProxy implements UserManager {
//使用代理時,添加對目標的引用:
private UserManager userManager;
//使用構造方法傳遞usermanager
public UserManagerImplProxy(UserManager userManager){
this.userManager=userManager;
}
@Override
public void addUser(String username, String password) {
checkSecurity();
userManager.addUser(username, password);
}
@Override
public void delUser(int userId) {
checkSecurity();
userManager.delUser(userId);
}
@Override
public String findUserByid(int userId) {
checkSecurity();
return userManager.findUserByid(userId);
}
@Override
public void modefyUser(int userId, String username, String password) {
checkSecurity();
userManager.modefyUser(userId, username, password);
}
/**
* 校驗安全性的方法
*/
private void checkSecurity(){
System.out.println("----UserManagerImpl:checkSecurity-----");
}
}</span>
~~~
雖然使用靜態代理我們我們已經可以降低耦合,避免直接修改UserManagerImpl的實現;但不得不考慮當需要代理的類足夠多,而且每一個代理類中我們都需要有類似“checkSecurity”的方法,我們依舊面臨耦合和代碼復用問題;
而且校驗安全性的方法,與增刪改查之間沒有直接聯系,也就是說沒有太強的耦合,所以我們可以把“checkSecurity”作為遍布在各個角落的獨立服務,也就是Java中所謂的橫切關注點;
所以,我們可以將checkSecurity方法從程序中剝離出來,這樣就可以保證程序變動,我們只需更更改剝離出來的這一個方法即可:
**? 進攻“動態代理**
繼續上面的程序優化,我們將“checkSecurity” 方法從靜態代理類(UserManagerImplProxy)中剝離到動態代理類(SecurityHandler):
動態代理類(SecurityHandler):
~~~
<span style="font-size:18px;">package com.bjpowernode.spring;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
public class SecurityHandler implements InvocationHandler{
//使用一個通用的對象
private Object targetObject;
public Object createProxyInstance(Object object){
this.targetObject = targetObject;
//根據目標接口生成代理
return Proxy.newProxyInstance(targetObject.getClass().getClassLoader(),
targetObject.getClass().getInterfaces(),
this);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args)
throws Throwable {
checkSecurity();
//調用目標方法
Object ret = method.invoke(targetObject, args);
return ret;
}
/**
* 校驗安全性的方法
*/
private void checkSecurity(){
System.out.println("----UserManagerImpl:checkSecurity-----");
}
}
</span>
~~~
這樣我們就可以在客戶端中對方法進行直接調用:
~~~
<span style="font-size:18px;">package com.bjpowernode.spring;
public class Client {
public static void main(String[] args) {
SecurityHandler handler = new SecurityHandler();
UserManager userManager = (UserManager)handler.createProxyInstance(new UserManagerImpl());
userManager.addUser("張三", "1234");
}
}
</span>
~~~
使用動態代理,歸根到底是面向接口編程,隱藏要調用的真正方法,降低類與類之間的耦合:我們通過object代理不同類型的對象,如果我們把對外的接口都通過動態代理來實現,那么所有的函數調用最終都會經過invoke函數的轉發,因此我們就可以在這里做一些自己想做的操作,比如日志系統、事務、攔截器、權限控制等。這也就是AOP(面向切面編程)的基本原理。
動態代理與靜態代理相比較,最大的好處是接口中聲明的所有方法都被轉移到調用處理器一個集中的方法中處理(InvocationHandler.invoke)。這樣,在接口方法數量比較多的時候,我們可以進行靈活處理,而不需要像靜態代理那樣每一個方法進行中轉。而且動態代理的應用使我們的類職責更加單一,復用性更強;
誠然,Proxy 已經設計得非常優美,但是還是有一點點小小的遺憾之處,那就是它始終無法擺脫僅支持 interface 代理的桎梏,因為它的設計注定了這個遺憾。回想一下那些動態生成的代理類的繼承關系圖,它們已經注定有一個共同的父類叫 Proxy。Java 的繼承機制注定了這些動態代理類們無法實現對 class 的動態代理,原因是多繼承在 Java 中本質上就行不通。?
**? 結不同代理**
代理扮演的是一個中介的角色,可以代替對象A去完成某一件事,這樣一來,對象A可以完全不出面,就可以收獲這件事“事成之后”的”豐厚回報“;
不論是靜態代理還是動態代理,都是在代理的基礎上進行再升華;從靜態代理到動態代理,代理類被不斷的封裝和剝離,逐漸引入并踐行AOP(面向切面編程)的思想,使得程序中代碼的復用性和封裝性更強,更好的執行開閉原則;所以說動態代理是AOP思想的一種體現;
?
? ??
? ? ?