spring aop源碼想到的代理模式

代理(pxoxy)無處不在,哪怕在現實生活中

 在看源碼的時候,看到了aop,

就以此引出代理的知識擴展。

爲什麼要有代理呢,主要是不改變原來代碼的功能的前提下,增加一些特殊功能,比如記錄日誌,權限認證,事務相關。

代理有靜態代理和動態代理,而動態代理有兩種實現方式:jdk代理和cglib代理,下面分別介紹下其實現方式

0  靜態代理

這是很古老的方式了,一開始入行時用的代理就是這樣方式實現的。核心僞代碼如下

/**
 * 
 * @author dgm
 * @describe "產品接口"
 * @date 2020年4月20日
 */
public interface ProductDao {
    Product createProduct(String name, String size);
}

public class ProductDaoImpl implements ProductDao {

	public ProductDaoImpl() {
	}
	public Product createProduct(String name, String size) {
		return new Product(name,size);
	}
}

/**
 * 
 * @author dgm
 * @describe "靜態代理"
 * @date 2020年4月20日
 */
public class ProductStaticProxy implements ProductDao {

	private ProductDao target;
	
	public ProductStaticProxy(ProductDao target) {
		this.target = target;
	}

	@Override
	public Product createProduct(String name, String size) {
		System.out.println("代理執行,開始工地幹活生產房子辦公樓產品了...");
		return target.createProduct(name,size);
	}
}

public class ProductStaticProxyTest {

	public static void main(String[] args) {
		// 目標對象
		ProductDao dao = new ProductDaoImpl();
		System.out.println("目標對象: " + dao.getClass());
		// 獲取代理對象
		ProductDao proxy = new ProductStaticProxy(dao);
		System.out.println("代理對象: "+proxy.getClass());
		// 代理生產產品
		System.out.println(proxy.createProduct("手機屏幕", "4寸"));
	}
}

執行結果圖示

 此種方式簡單,但隨着項目量越來越大,維護的代碼也就越多,且接口改動也頻繁,迫切需要一種新的方式,這時動態代理登場了

1.  jdk動態代理(自帶功能,不需要引入第三方包)

先介紹下java.lang.reflect.Proxy類,它提供的一個newProxyInstance方法用來創建一個對象的代理對象

 public static Object newProxyInstance(ClassLoader loader,
                                          Class<?>[] interfaces,
                                          InvocationHandler h)

newProxyInstance方法用來返回一個代理對象,這個方法總共有3個參數,ClassLoader loader用來指明生成代理對象使用哪個類裝載器,Class<?>[] interfaces用來指明生成哪個對象的代理對象,通過接口指定,InvocationHandler h用來指明產生的這個代理對象要做什麼事情。所以我們只需要調用newProxyInstance方法就可以得到某一個對象的代理對象了。

示例代碼:

/**
 * 
 * @author dgm
 * @describe "jdk動態代理"
 * @date 2020年4月20日
 */
public class ProductJdkDynamicProxy {
	// 接收一個目標對象
	private Object target;

	public ProductJdkDynamicProxy(Object target) {
		this.target = target;
	}

	// 返回對目標對象(target)代理後的對象(proxy)
	public Object getProxyInstance() {
		Object proxy = Proxy.newProxyInstance(target.getClass()
				.getClassLoader(), // 目標對象使用的類加載器
				target.getClass().getInterfaces(), // 目標對象實現的所有接口
				new InvocationHandler() { // 執行代理對象方法時候觸發
					@Override
					public Object invoke(Object proxy, Method method,
							Object[] args) throws Throwable {

						System.out.println("原生jdk開啓生產產品線,拼命加班中");
						// 執行目標對象方法
						Object result = method.invoke(target, args);
						System.out.println("原生jdk乾的不錯,結束生產產品");

						return result;
					}
				});
		return proxy;
	}
}


public class ProductJdkDynamicProxyTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//目標對象
		ProductDao target = new  ProductDaoImpl();
		System.out.println("目標對象: "+target.getClass());
		//代理生產產品
		ProductDao proxy = (ProductDao) new ProductJdkDynamicProxy(target).getProxyInstance();
		System.out.println("代理對象: "+proxy.getClass());
        //執行代理業務具體業務操作
		System.out.println(proxy.createProduct("手機屏幕", "4寸"));
	}

}

 測試效果:

spring aop的實現方式之一就是它實現的

2.  cglib代理

慢慢地工作到了一定階段,時代在發展爲了快速開發,項目化運作,開始大量引進框架和第三方庫,真的很好用(特別是項目開發),比如cglib,由於它是針對類實現的代理,故類不能是final類型。

/**
 * 
 * @author dgm
 * @describe "cglib動態代理"
 * @date 2020年4月20日
 */
public class ProductCglibDynamicProxy implements MethodInterceptor {

	@Override
	public Object intercept(Object obj, Method method, Object[] args,
			MethodProxy methodProxy) throws Throwable {
		try {
			before(method);// 前置通知
			Object ret = methodProxy.invokeSuper(obj, args);// 目標方法執行
			after(method, ret);// 後置通知
			return ret;
		} catch (Exception e) {
			exception();// 異常通知
		} finally {
			afterReturning();// 方法返回通知
		}
		return null;
	}

	// 前置增強
	private void before(Method method) {
		System.out.printf("cglib 動態代理:%s", method.getName());
	}

	// 後置增強
	private void after(Method method, Object ret) {
		System.out.printf(",cglib動態代理:%s, ret:%s", method.getName(), ret);
	}

	// 異常增強
	private void exception() {
		System.out.println(",程序出錯了!");
	}

	// after返回增強
	private void afterReturning() {
		System.out.println(",產品成型");
	}

}


/**
 * 
 * @author dgm
 * @describe "測試cglib動態代理"
 * @date 2020年4月20日
 */
public class ProductCglibDynamicProxyTest {

	public static void main(String[] args) {
		// TODO Auto-generated method stub
		//代理對象
		ProductDao proxy = CglibProxyFactory.create();
		System.out.println("代理對象: "+proxy.getClass());
        //執行代理業務具體業務操作
		System.out.println(proxy.createProduct("手機屏幕", "4寸"));
	}

}

運行結果:

 spring-aop的另一實現也是基於此

 

簡單總結:

  1. 靜態代理實現較簡單,只要代理對象對目標對象進行包裝,即可實現增強功能,但靜態代理只能爲一個目標對象服務,如果目標對象過多,則會產生很多代理類。
  2. JDK動態代理需要目標對象實現業務接口,代理類只需實現InvocationHandler接口。
  3. 靜態代理在編譯時產生class字節碼文件,可以直接使用,效率高。
  4. 動態代理必須實現InvocationHandler接口,通過反射代理方法,比較消耗系統性能,但可以減少代理類的數量,使用更靈活。
  5. cglib代理無需實現接口,通過生成類字節碼實現代理,比反射稍快,不存在性能問題,但cglib會繼承目標對象,需要重寫方法,所以目標對象不能爲final類。

 

延伸閱讀:

0. java動態代理原理及解析 https://www.sohu.com/a/246547051_132276

1. Spring AOP 實現原理與 CGLIB 應用 https://www.ibm.com/developerworks/cn/java/j-lo-springaopcglib/

2. 從動態代理到SpringAop以及AspectJ風格  https://segmentfault.com/a/1190000015262333

3. Java 動態代理機制分析及擴展,第 1 部分 https://www.ibm.com/developerworks/cn/java/j-lo-proxy1/index.html

4. spring AOP是什麼?你都拿它做什麼?  https://zhuanlan.zhihu.com/p/28097563

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章