閱讀283 返回首頁    go 阿裏雲 go 技術社區[雲棲]


Java動態代理學習2——靜態代理和動態代理並對照spring的通知

 

一、代理模式 

代理模式是常用的java設計模式,特征是代理類委托類有同樣的接口,代理類主要負責為委托類預處理消息、過濾消息、把消息轉發給委托類,以及事後處理消息等。

代理類與委托類之間通常會存在關聯關係,一個代理類的對象與一個委托類的對象關聯,代理類的對象本身並不真正實現服務,而是通過調用委托類的對象的相關方法,來提供特定的服務。 

按照代理的創建時期,代理類可以分為兩種:

靜態代理:由程序員創建或特定工具自動生成源代碼再對其編譯。在程序運行前代理類的.class文件就已經存在了。 
動態代理:在程序運行時運用反射機製動態創建而成。 

 

 

二、靜態代理

public interface CountDao
{
	// 查看賬戶方法
	public void queryCount();

	// 修改賬戶方法
	public void updateCount();

}

public class CountDaoImpl implements CountDao
{
	public void queryCount()
	{
		System.out.println("查看賬戶方法...");
	}

	public void updateCount()
	{
		System.out.println("修改賬戶方法...");
	}
}

public class CountProxy implements CountDao
{
	private CountDao countDao;

	public CountProxy(CountDao countDao)
	{
		this.countDao = countDao;
	}

	@Override
	public void queryCount()
	{
		System.out.println("事務處理之前");
		countDao.queryCount();
		System.out.println("事務處理之後");
	}

	@Override
	public void updateCount()
	{
		System.out.println("事務處理之前");
		countDao.updateCount();
		System.out.println("事務處理之後");
	}
}

public class TestCount
{
	public static void main(String[] args)
	{
		CountProxy countProxy = new CountProxy(new CountDaoImpl());
		countProxy.updateCount();
		countProxy.queryCount();
	}
}

 

觀察代碼可以發現每一個代理類隻能為一個接口服務,這樣一來程序開發中必然會產生過多的代理,而且所有的代理操作除了調用的方法不一樣之外其他的操作都一樣,則此時肯定是重複代碼。解決這一問題最好的做法是可以通過一個代理類完成全部的代理功能,那麼此時就必須使用動態代理完成。 


 

三、動態代理

與靜態代理類對照的是動態代理類,動態代理類的字節碼在程序運行時由Java反射機製動態生成,無需程序員手工編寫它的源代碼。動態代理類不僅簡化了編程工作,而且提高了軟件係統的可擴展性,因為Java 反射機製可以生成任意類型的動態代理類。java.lang.reflect 包中的Proxy類和InvocationHandler 接口提供了生成動態代理類的能力。

 

1 JDK動態代理

JDK動態代理中包含一個類和一個接口

 

InvocationHandler接口

public interface InvocationHandler { 
    public Object invoke(Object proxy,Method method,Object[] args) throws Throwable; 
}  

Object proxy:指被代理的對象。
Method method:要調用的方法
Object[] args:方法調用時所需要的參數

可以將InvocationHandler接口的子類想象成一個代理的最終操作類,替換掉ProxySubject

 

Proxy類

Proxy類是專門完成代理的操作類,可以通過此類為一個或多個接口動態地生成實現類,此類提供了如下的操作方法:

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

ClassLoader loader:類加載器
Class<?>[] interfaces:得到全部的接口
InvocationHandler h:得到InvocationHandler接口的子類實例

public interface PersonService
{
	public void save();
}

public class PersonServiceImpl implements PersonService
{
	public void save()
	{
		System.out.println("人員增加");
	}
}

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

public class PersonProxy implements InvocationHandler
{
	// 目標對象
	private Object target;

    // 返回一個代理類對象
	public Object createProxyInstance(Object target)
	{
		this.target = target;
		return Proxy.newProxyInstance(target.getClass().getClassLoader(), target.getClass().getInterfaces(), this);
	}

	// 上述代碼中調用的this就是當前代理對象,會自動調用該方法
	public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
	{
		Object result = null;
		System.out.println("前置通知");
		try
		{		
			result = method.invoke(target, args);
			System.out.println("後置通知");
		}
		catch(Exception e)
		{
			System.out.println("例外通知");
		}
		finally
		{
			System.out.println("最終通知"); 
		}
		return result;
	}
}

public class TestProxy
{
	public static void main(String[] args)
	{
		PersonProxy bp = new PersonProxy();
		PersonService ps = (PersonService)bp.createProxyInstance(new PersonServiceImpl());
		ps.save();
	}
}

前置通知
人員增加
後置通知
最終通知

JDK的動態代理依靠接口實現,如果有些類並沒有實現接口,則不能使用JDK代理,這就要使用cglib動態代理了。 

 

 

2 CGLIB動態代理

JDK的動態代理機製隻能代理實現了接口的類,而不能實現接口的類就不能實現JDK的動態代理,cglib是針對類來實現代理的,他的原理是對指定的目標類生成一個子類,並覆蓋其中方法實現增強,但因為采用的是繼承所以不能對final修飾的類進行代理。 

public interface PersonService
{
	public void save();
}

public class PersonServiceImpl implements PersonService
{
	public void save()
	{
		System.out.println("人員增加");
	}
}

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;

/**
 * 使用CGLIB動態代理
 */
public class PersonProxy implements MethodInterceptor
{
	private Object target;

	// 返回一個代理類對象
	public Object createProxyInstance(Object target)
	{
		this.target = target;
		Enhancer enhancer = new Enhancer();

		// 設置目標類為父類,會覆蓋目標類的非final方法
		enhancer.setSuperclass(this.target.getClass());

		// 回調方法
		enhancer.setCallback(this);

		// 創建代理對象
		return enhancer.create();
	}

	// 上述代碼中調用的this就是當前代理對象,會自動調用該方法
	// 方法一
	@Override
	public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable
	{
		Object result = null;
		System.out.println("前置通知");
		try
		{
			result = method.invoke(target, args);
			System.out.println("後置通知");
		}
		catch (Exception e)
		{
			System.out.println("例外通知");
		}
		finally
		{
			System.out.println("最終通知");
		}
		return result;
	}
}

public class PersonProxy2 implements MethodInterceptor
{
	private Object target;

	// 返回一個代理類對象
	public Object createProxyInstance(Object target)
	{
		this.target = target;
		Enhancer enhancer = new Enhancer();

		// 設置目標類為父類,會覆蓋目標類的非final方法
		enhancer.setSuperclass(this.target.getClass());

		// 回調方法
		enhancer.setCallback(this);

		// 創建代理對象
		return enhancer.create();
	}
	
	// 方法二
	public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable
	{
		Object result = null;
		System.out.println("前置通知");
		try
		{
			result = proxy.invokeSuper(obj, args);
			System.out.println("後置通知");
		}
		catch (Exception e)
		{
			System.out.println("例外通知");
		}
		finally
		{
			System.out.println("最終通知");
		}
		return result;
	}
}

public class TestProxy
{
	public static void main(String[] args)
	{
		PersonProxy cglib = new PersonProxy();
		PersonServiceClass ps1 = (PersonServiceClass) cglib.createProxyInstance(new PersonServiceClass());
		ps1.save();
		System.out.println("--------------------");
		PersonProxy2 cglib2 = new PersonProxy2();
		PersonServiceClass ps2 = (PersonServiceClass) cglib2.createProxyInstance(new PersonServiceClass());
		ps2.save();
	}
}

 

前置通知
增加人員
後置通知
最終通知
--------------------
前置通知
增加人員
後置通知
最終通知

參考並演繹自地址:https://www.cnblogs.com/jqyp/archive/2010/08/20/1805041.html

 

最後更新:2017-04-03 07:57:28

  上一篇:go Android開發5——文件讀寫
  下一篇:go Java類加載器學習2——自定義類加載器和父類委托機製帶來的問題