代理模式靜態代理 動態代理(JDK代理、cglib代理)
阿新 • • 發佈:2019-01-14
面向物件的設計原則:
開閉原則:對於修改來說是關閉,對於擴充套件來說是開放的
單一職責原則:
一個專案的功能單一
一個模組的功能單一
一個介面/類的功能單一
一個方法的功能單一
一個變數的功能單一
代理設計模式:
提出一個問題:
有一套原有的業務模型 符合單一職責
有一套新的功能 符合單一職責
要把新的功能新增到原有業務模型中,填在原有業務模型的前或後,不是添在中間
解決方案:
方案一: 在原有業務方法的第一行填寫新功能程式碼 在原有業務方法的最後一行填寫新功能程式碼 違反開閉原則,和單一職責 是能完成新增新功能的需求,只是設計的不太好 方案二: 代理設計模式實現 遵守開閉原則,和單一職責
代理設計模式的分類:
靜態代理
動態代理
jdk的動態代理
cglib的動態代理
靜態代理:
原有的業務模型
UserDao.java UserDaoImpl.java
UserService.java UserServiceImpl.java
新功能:事務管理
TransactionManager.java
目標:要把新的功能(事務管理)切入到指定業務方法的前或後
在業務方法的前新增事務的開啟 在業務方法的後新增事務的,沒錯誤就提交,有錯誤就回滾 原有業務有了 新業務也有了 新建一個類,在新建類中把老業務和新業務耦合在一起
TransactionManager.java
/**
* 此類是一個新功能類,用於給原有業務新增事務處理的功能
* 符合單一職責原則,此類只做事務管理
* @author Administrator
*
*/
public class TransactionManager {
/**
* 事務的開啟
*/
public void begin(){
System.out.println("事務開啟");
}
/**
* 事務提交
*/
public void commit(){
System.out.println( "事務提交");
}
/**
* 事務的回滾
*/
public void rollback(){
System.out.println("事務回滾");
}
}
StaticProxy.java
public class StaticProxy implements UserService {
//老業務
private UserService userService;
//新業務
private TransactionManager transactionManager;
public void setUserService(UserService userService) {
this.userService = userService;
}
public void setTransactionManager(TransactionManager transactionManager) {
this.transactionManager = transactionManager;
}
@Override
public boolean addUser(User user) {
try{
transactionManager.begin();
userService.addUser(user);
transactionManager.commit();
}catch(Exception e){
transactionManager.rollback();
e.printStackTrace();
}
return false;
}
@Override
public boolean updateUser(User user) {
try{
transactionManager.begin();
userService.updateUser(user);
transactionManager.commit();
}catch(Exception e){
transactionManager.rollback();
e.printStackTrace();
}
return false;
}
}
總結靜態代理:
1.原有的老業務,沒有被破壞,符合單一職責
2.新的業務,符合單一職責
3.把新的業務要新增到老業務中
a.新建一個老業務和新業務的耦合類,此耦合類就叫做靜態代理類
b.靜態代理類要求必須實現自業務的介面,因為靜態代理類的功能不能少於原有業務功能
c.靜態代理類不滿足單一職責原則,因為老功能和新功能耦合在一起
d.每一個套業務,都會對應一個靜態代理類
比如:UserService UserStaticProxy implements UserService
ProductService ProductStaticProxy implements ProductService
業務的模型越多,靜態代理類就越多
程式設計師會額外寫很多的靜態代理類,增加程式設計師的負擔
e.每個靜態代理類都會建立很多代理物件
f.靜態代理是在編譯期間就確定了老業務和新業務的耦合關係
動態代理:(jdk動態代理,cglib動態代理)
jdk動態代理:是由jdk提供的功能
老的業務功能:
UserDao.java UserDaoImpl.java
UserService.java UserServiceImpl.java
新的業務功能:
TransactionMananger.java
目的:把新的功能新增到老業務功能,遵守開閉原則和單一職責原則
學習jdk動態代理的幾個層次
1.能夠寫出jdk動態代理的程式碼
2.能清晰的瞭解jdk動態代理的呼叫關係 參見:jdk動態代理呼叫原理圖.png
3.瞭解jdk的動態代理類的內容
jdk的動態代理流程
1.建立生成代理物件的類JDKProxy.java getProxyObject方法使用者生成代理物件
2.建立InvocationHandler介面的子實現,在子實現中的invoke方法把老業務功能和新業務功能耦合
OldAndNewtogether.java invoke方法,耦合了新和老的業務
在jdk建立代理物件的之前,由jdk底層根據目標物件和目標物件所對應的介面,
創建出代理類,此代理類只是臨時出現,用完就沒有了,
建立完代理類,例項化此代理類的物件,Object proxyObject=Proxy.newProxyInstance(引數一,引數二,引數三);
只有呼叫Object proxyObject=Proxy.newProxyInstance(引數一,引數二,引數三);才會生成代理類和代理物件
下面是生成的代理類的模型:
/**
* jdk的代理類,由jdk的底層建立的
* @author Administrator
*
*/
public final class $Proxy0 extends Proxy implements UserService {
//靜態程式碼塊,m0---mx賦值 型別是Method
static {
try {
m0 = Class.forName("java.lang.Object").getMethod("hashCode", new Class[0]);
m1 = Class.forName("java.lang.Object").getMethod("equals",
new Class[] { Class.forName("java.lang.Object") });
m2 = Class.forName("java.lang.Object").getMethod("toString", new Class[0]);
m3 = Class.forName("com.arno.service.UserService").getMethod("addUser",
new Class[] { Class.forName("com.tarean.entity.User") });
m4 = Class.forName("com.arno.service.UserService").getMethod("updateUser",
new Class[] { Class.forName("com.tarean.entity.User") });
return;
} catch (NoSuchMethodException localNoSuchMethodException) {
throw new NoSuchMethodError(localNoSuchMethodException.getMessage());
} catch (ClassNotFoundException localClassNotFoundException) {
throw new NoClassDefFoundError(localClassNotFoundException.getMessage());
}
}
//若干私有屬性
private static Method m0;//hashCode方法,從Object中獲取
private static Method m1;//equals方法,從Object中獲取
private static Method m2;//toString方法,從Object中獲取
private static Method m3;//addUser方法,從UserService介面中獲取
private static Method m4;//updateUser方法,從UserService介面中獲取
//代理類構造
public $Proxy0(InvocationHandler paramInvocationHandler)throws {
super(paramInvocationHandler);
}
//重寫UserService介面的addUser方法
public final void addUser(User user)throws{
try{
this.h.invoke(this, m3, new Object[] { user });
return;
}catch (Error|RuntimeException localError){
throw localError;
}catch (Throwable localThrowable){
throw new UndeclaredThrowableException(localThrowable);
}
}
//重寫UserService介面的updateUser方法
public final void updateUser(User user)throws{
try{
this.h.invoke(this, m4, new Object[] { user });
return;
}catch (Error|RuntimeException localError){
throw localError;
}catch (Throwable localThrowable){
throw new UndeclaredThrowableException(localThrowable);
}
}
//equals方法
public final boolean equals(Object paramObject)throws {
try{
return ((Boolean)this.h.invoke(this, m1, new Object[] { paramObject })).booleanValue();
}catch (Error|RuntimeException localError){
throw localError;
}catch (Throwable localThrowable){
throw new UndeclaredThrowableException(localThrowable);
}
}
public final String toString()throws{
try{
return (String)this.h.invoke(this, m2, null);
}catch (Error|RuntimeException localError){
throw localError;
}catch (Throwable localThrowable){
throw new UndeclaredThrowableException(localThrowable);
}
}
public final int hashCode()throws{
try{
return ((Integer)this.h.invoke(this, m0, null)).intValue();
}catch (Error|RuntimeException localError){
throw localError;
}catch (Throwable localThrowable){
throw new UndeclaredThrowableException(localThrowable);
}
}
}
總結jdk動態代理:
1.原有的老業務,沒有被破壞,符合單一職責 UserService.java
2.新的業務,符合單一職責 TransactionManager.java
3.建立一個java類使用者生成代理物件JDKProxy.java
4.把新的業務要新增到老業務中
a.必須實現InvocationHandler介面 OldAndNewTogether.java
b.重寫invoke的方法
c.在方法中把老業務和新業務耦合
5.一定要先由jdk生成代理類 參見:$Proxy0.java
6.由代理類生成代理物件
7.代理物件和目標物件是兄弟關係
代理類和目標類是兄弟關係
8.jdk動態代理要求目標類必須介面
9.jdk的代理類是在執行期間出現,一定不在編譯期間出現
所謂的動態代理是代理類是動態出現的,什麼時候呼叫,什麼時候出現代理類
CGLIB動態代理:需要第三方庫支撐
總結CGLIB動態代理:
0.需要額外第三方jar支撐
1.原有的老業務,沒有被破壞,符合單一職責 UserService.java
2.新的業務,符合單一職責 TransactionManager.java
3.建立一個java類使用者生成代理物件CGLIBProxy.java
4.把新的業務要新增到老業務中
a.必須實現MethodInterceptor介面 OldAndNewTogether.java
b.重寫intercept的方法
c.在方法中把老業務和新業務耦合
5.一定要先由asm.jar生成代理類
6.由代理類生成代理物件
7.代理物件和目標物件是父子關係
目標類是代理類的父親
目標類不能是final
8.cglib動態代理的目標類有無介面皆可
9.cglib的代理類是在執行期間出現,一定不在編譯期間出現
所謂的動態代理是代理類是動態出現的,什麼時候呼叫,什麼時候出現代理類
jdk動態代理和cglib動態代理的主要區別:
jdk動態代理:
1.目標類必須有介面
2.代理物件和目標物件是兄弟關係
3.代理類是在執行期間動態建立的
4.建立代理物件快,用代理物件執行目標方法的時候是慢
5.jdk動態代理會把代理類快取
cglib動態代理:
1.目標類有無介面皆可,但不能是final類
2.目標類是代理類的父類
3.代理類是在執行期間動態建立的
4.建立代理物件慢,用代理物件執行目標方法的時候快
代理就把新功能橫切到原有業務上,在滿足開閉原則和單一職責前提下
可以把新功能看成某一方面的功能,把某一個方面的功能橫切到需要的業務上
spring aop的底層原理就是用的動態代理(有介面就用jdk,沒有介面就用cglib)
提出問題:
jdk動態代理和cglib動態代理,把新功能橫切到原有老業務中的所有方法上
能否把新功能橫切到業務中的指定的一部分方法上,其他的業務方法不需要橫切
答案:用spring aop可以對部分業務方法橫切