1. 程式人生 > >代理模式靜態代理 動態代理(JDK代理、cglib代理)

代理模式靜態代理 動態代理(JDK代理、cglib代理)

面向物件的設計原則:

開閉原則:對於修改來說是關閉,對於擴充套件來說是開放的
單一職責原則:
       一個專案的功能單一
       一個模組的功能單一
       一個介面/類的功能單一
       一個方法的功能單一
       一個變數的功能單一

代理設計模式:

提出一個問題:

  有一套原有的業務模型   符合單一職責
  有一套新的功能                 符合單一職責
  要把新的功能新增到原有業務模型中,填在原有業務模型的前或後,不是添在中間

解決方案:

方案一:
        在原有業務方法的第一行填寫新功能程式碼
        在原有業務方法的最後一行填寫新功能程式碼
        違反開閉原則,和單一職責
        是能完成新增新功能的需求,只是設計的不太好
方案二:
        代理設計模式實現
         遵守開閉原則,和單一職責

代理設計模式的分類:

靜態代理
動態代理
        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可以對部分業務方法橫切