原文出處:http://publish.itpub.net/j/2007-10-17/200710170923968.shtml
本文禁止轉載。
在上一篇文章中,我們講述了利用Java的反射機制中實現Spring中的IOC,在本文中,我們將更進一步,講述用Java的反射和動態代理機制來實現Spring的AOP。
一.AOP概述
AOP(Aspect Oriented Programing),即面向切面編程,它主要用于日志記錄、性能統計、安全控制、事務處理、異常處理等方面。它的主要意圖就要將日志記錄,性能統計,安全控制、事務處理、異常處理等等代碼從業務邏輯代碼中清楚地劃分出來。通過對這些行為的分離,我們希望可以將它們獨立地配置到業務邏輯方法中,而要改變這些行為的時候也不需要影響到業務邏輯方法代碼。
下面讓我們來看一個利用AOP來實現日志記錄的例子,在沒有使用AOP之前,我們的代碼如下面所講述。
下面這段代碼為業務的接口類代碼:
package org.amigo.proxy;


/** *//**
*業務邏輯類接口.
*@author<a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
*Creationdate:2007-10-7-上午09:09:53
*/

publicinterface BusinessObj
{


/** *//**
*執行業務.
*/
publicvoid process();
}


BusinessObj接口的某個實現類代碼如下:
package org.amigo.proxy;


/** *//**
* 業務邏輯對象實現類.
* @author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
* Creation date: 2007-10-7 - 上午09:11:49
*/

public class BusinessObjImpl implements BusinessObj
{


/** *//**
* 執行業務.
*/

public void process()
{

try
{
System.out.println("before process");
System.out.println("執行業務邏輯");
System.out.println("after process");

} catch (Exception e)
{
System.err.println("發生異常:" + e.toString());
}
}
}

在上例中我們可以看到,在執行業務方法前、執行業務方法后以及異常發生時的日志記錄,我們都是通過在對應的類中寫入記錄日志的代碼來實現的,當有這種日志記錄需求的業務邏輯類不斷增多時,將會給我們的維護帶來很大困難,而且,在上面的例子中,日志代碼和業務邏輯代碼混合在一起,為日后的維護工作又抹上了一層“恐怖”色彩。
按照AOP的思想,我們首先需要尋找一個切面,在這里我們已經找到,即在業務邏輯執行前后以及異常發生時,進行相應的日志記錄。我們需要將這部分日志代碼放入一個單獨的類中,以便為以后的修改提供方便。
我們在截獲某個業務邏輯方法時,可以采用Java的動態代理機制來實現。在下節中我們將重點講述Java的動態代理機制。
二.Java的動態代理機制
代理模式是常用的Java設計模式。代理類主要負責為委托類預處理消息、過濾信息、把消息轉發給委托類,以及事后處理信息等。
動態代理類不僅簡化了編程工作,而且提高了軟件系統的擴展性和可維護性。我們可以通過實現java.lang.reflect.InvocationHandler接口提供一個執行處理器,然后通過java.lang.reflect.Proxy得到一個代理對象,通過這個代理對象來執行業務邏輯方法,在業務邏輯方法被調用的同時,自動調用會執行處理器。
下面讓我們來創建一個日志攔截器類LogInterceptor.java文件,該類實現java.lang.reflect.InvocationHandler接口,其內容如下所示:
package org.amigo.proxy;

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


/** *//**
*日志攔截器,用來進行日志處理.
*@author<a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
*Creationdate:2007-10-7-上午09:31:44
*/

publicclass LogInterceptor implements InvocationHandler
{
private Object delegate;


/** *//**
*構造函數,設置代理對象.
*/

public LogInterceptor(Object delegate)
{
this.delegate = delegate;
}


/** *//**
*方法的調用.
*@paramproxy
*@parammethod對應的方法
*@paramargs方法的參信息
*@return返回操作結果對象
*@throwsThrowable
*/

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
Object result = null;

try
{
System.out.println("before process" + method);
//調用代理對象delegate的method方法,并將args作為參數信息傳入
result = method.invoke(delegate, args);
System.out.println("after process" + method);

} catch (Exception e)
{
System.err.println("發生異常:" + e.toString());
}
return result;
}


/** *//**
*測試方法.
*@paramargs
*@throwsException
*/

publicstaticvoid main(String[] args) throws Exception
{
BusinessObj obj = new BusinessObjImpl();
//創建一個日志攔截器
LogInterceptor interceptor = new LogInterceptor(obj);
//通過Proxy類的newProxyInstance(
)方法來獲得動態的代理對象
BusinessObj proxy = (BusinessObj) Proxy.newProxyInstance(
BusinessObjImpl.class.getClassLoader(),
BusinessObjImpl.class.getInterfaces(),
interceptor);
//執行動態代理對象的業務邏輯方法
proxy.process();
}
}

此時還需要對BusinessObj的實現類BusinessObjImpl類進行修改,去掉其日志記錄等內容,修改后的文件如下:
package org.amigo.proxy;

/** *//**
* 業務邏輯對象實現類.
* @author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
* Creation date: 2007-10-7 - 上午09:11:49
*/

public class BusinessObjImpl implements BusinessObj
{

/** *//**
* 執行業務.
*/

public void process()
{
System.out.println("執行業務邏輯");
}
}

運行LogInterceptor類我們可以發現,它實現了前面所需要的功能,但是很好的將業務邏輯方法的代碼和日志記錄的代碼分離開來,并且所有的業務處理對象都可以利用該類來完成日志的記錄,防止了重復代碼的出現,增加了程序的可擴展性和可維護性,從而提高了代碼的質量。那么Spring中的AOP的實現是怎么樣的呢?接著讓我們來對Spring的AOP進行探討,了解其內部實現原理。
三.Spring中AOP的模擬實現
在學習了Java的動態代理機制后,我們在本節中將學習Java的動態代理機制在Spring中的應用。首先我們創建一個名為AopHandler的類,該類可生成代理對象,同時可以根據設置的前置或后置處理對象分別在方法執行前后執行一些另外的操作,該類的內容如下所示:
package org.amigo.proxy;

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


/** *//**
* AOP處理器.
* @author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
* Creation date: 2007-10-7 - 上午10:13:28
*/

public class AopHandler implements InvocationHandler
{
//需要代理的目標對象
private Object target;
//方法前置顧問
Advisor beforeAdvisor;
//方法后置顧問
Advisor afterAdvisor;


/** *//**
* 設置代理目標對象,并生成動態代理對象.
* @param target 代理目標對象
* @return 返回動態代理對象
*/

public Object setObject(Object target)
{
//設置代理目標對象
this.target = target;
//根據代理目標對象生成動態代理對象
Object obj = Proxy.newProxyInstance(
target.getClass().getClassLoader(),
target.getClass().getInterfaces(), this);
return obj;
}


/** *//**
* 若定義了前置處理,則在方法執行前執行前置處理
* 若定義了后置處理,則在方法調用后調用后置處理.
* @param proxy 代理對象
* @param method 調用的業務方法
* @param args 方法的參數
* @return 返回結果信息
* @throws Throwable
*/

public Object invoke(Object proxy, Method method, Object[] args) throws Throwable
{
//進行業務方法的前置處理

if (beforeAdvisor != null)
{
beforeAdvisor.doInAdvisor(proxy, method, args);
}

//執行業務方法
Object result = method.invoke(target, args);
//進行業務方法的后置處理

if (afterAdvisor != null)
{
afterAdvisor.doInAdvisor(proxy, method, args);
}
//返回結果對象
return result;
}


/** *//**
* 設置方法的前置顧問.
* @param advisor 方法的前置顧問
*/

public void setBeforeAdvisor(Advisor advisor)
{
this.beforeAdvisor = advisor;
}


/** *//**
* 設置方法的后置顧問.
* @param advisor 方法的后置顧問
*/

public void setAfterAdvisor(Advisor advisor)
{
this.afterAdvisor = advisor;
}
}

在上類中,前置和后置顧問對象都繼承Advisor接口,接下來讓我們來看看顧問接口類的內容,該類定義了doInAdvisor(Object proxy, Method method, Object[] args)方法,如下所示:
package org.amigo.proxy;

import java.lang.reflect.Method;


/** *//**
*顧問接口類.
*@author<a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
*Creationdate:2007-10-7-上午10:21:18
*/

publicinterface Advisor
{


/** *//**
*所做的操作.
*/
publicvoid doInAdvisor(Object proxy, Method method, Object[] args);
}
BeforeMethodAdvisor和AfterMethodAdvisor都實現了Advisor接口,分別為方法的前置顧問和后置顧問類。
BeforeMethodAdvisor.java文件(前置顧問類)的內容如下所示:
package org.amigo.proxy;

import java.lang.reflect.Method;


/** *//**
*
*方法前置顧問,它完成方法的前置操作.
*@author<a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
*Creationdate:2007-10-7-上午10:19:57
*/

publicclass BeforeMethodAdvisor implements Advisor
{

/** *//**
*在方法執行前所進行的操作.
*/

publicvoid doInAdvisor(Object proxy, Method method, Object[] args)
{
System.out.println("before process " + method);
}
}

AfterMethodAdvisor.java文件(后置顧問類)的內容如下所示:
package org.amigo.proxy;

import java.lang.reflect.Method;


/** *//**
*方法的后置顧問,它完成方法的后置操作.
*@author<a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
*Creationdate:2007-10-7-上午10:20:43
*/

publicclass AfterMethodAdvisor implements Advisor
{


/** *//**
*在方法執行后所進行的操作.
*/

publicvoid doInAdvisor(Object proxy, Method method, Object[] args)
{
System.out.println("after process " + method);
}
}

這兩個類分別在方法執行前和方法執行后做一些額外的操作。
對于在配置文件中對某個bean配置前置或后置處理器,我們可以在bean中增加兩個屬性aop和aopType,aop的值為對應的前置顧問類或后置顧問類的名稱,aopType用于指明該顧問類為前置還是后置顧問,為before時表示為前置處理器,為after時表示為后置處理器,這時候我們需要修改上一篇文章中的BeanFactory.java這個文件,添加其對aop和aopType的處理,修改后的該文件內容如下:
package org.amigo.proxy;

import java.io.InputStream;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import org.dom4j.Attribute;
import org.dom4j.Document;
import org.dom4j.Element;
import org.dom4j.io.SAXReader;


/** *//**
*bean工廠類.
*@author<a href="mailto:xiexingxing1121@126.com">AmigoXie</a>
*Creationdate:2007-10-7-下午04:04:34
*/

publicclass BeanFactory
{
private Map<String, Object> beanMap = new HashMap<String, Object>();


/** *//**
*bean工廠的初始化.
*@paramxmlxml配置文件
*/

publicvoid init(String xml)
{

try
{
//讀取指定的配置文件
SAXReader reader = new SAXReader();
ClassLoader classLoader = Thread.currentThread().getContextClassLoader();
InputStream ins = classLoader.getResourceAsStream(xml);
Document doc = reader.read(ins);
Element root = doc.getRootElement();
Element foo;

//創建AOP處理器
AopHandler aopHandler = new AopHandler();

//遍歷bean

for (Iterator i = root.elementIterator("bean"); i.hasNext();)
{
foo = (Element) i.next();
//獲取bean的屬性id、class、aop以及aopType
Attribute id = foo.attribute("id");
Attribute cls = foo.attribute("class");
Attribute aop = foo.attribute("aop");
Attribute aopType = foo.attribute("aopType");

//配置了aop和aopType屬性時,需進行攔截操作

if (aop != null && aopType != null)
{
//根據aop字符串獲取對應的類
Class advisorCls = Class.forName(aop.getText());
//創建該類的對象
Advisor advisor = (Advisor) advisorCls.newInstance();

//根據aopType的類型來設置前置或后置顧問

if ("before".equals(aopType.getText()))
{
aopHandler.setBeforeAdvisor(advisor);

} elseif ("after".equals(aopType.getText()))
{
aopHandler.setAfterAdvisor(advisor);
}
}

//利用Java反射機制,通過class的名稱獲取Class對象
Class bean = Class.forName(cls.getText());

//獲取對應class的信息
java.beans.BeanInfo info = java.beans.Introspector.getBeanInfo(bean);
//獲取其屬性描述
java.beans.PropertyDescriptor pd[] = info.getPropertyDescriptors();
//設置值的方法
Method mSet = null;
//創建一個對象
Object obj = bean.newInstance();
//遍歷該bean的property屬性

for (Iterator ite = foo.elementIterator("property"); ite.hasNext();)
{
Element foo2 = (Element) ite.next();
//獲取該property的name屬性
Attribute name = foo2.attribute("name");
String value = null;

//獲取該property的子元素value的值

for(Iterator ite1 = foo2.elementIterator("value"); ite1.hasNext();)
{
Element node = (Element) ite1.next();
value = node.getText();
break;
}


for (int k = 0; k < pd.length; k++)
{

if (pd[k].getName().equalsIgnoreCase(name.getText()))
{
mSet = pd[k].getWriteMethod();
//利用Java的反射極致調用對象的某個set方法,并將值設置進去
mSet.invoke(obj, value);
}
}
}

//為對象增加前置或后置顧問
obj = (Object) aopHandler.setObject(obj);

//將對象放入beanMap中,其中key為id值,value為對象
beanMap.put(id.getText(), obj);
}

} catch (Exception e)
{
System.out.println(e.toString());
}
}


/** *//**
*通過bean的id獲取bean的對象.
*@parambeanNamebean的id
*@return返回對應對象
*/

public Object getBean(String beanName)
{
Object obj = beanMap.get(beanName);
return obj;
}


/** *//**
*測試方法.
*@paramargs
*/

publicstaticvoid main(String[] args)
{
BeanFactory factory = new BeanFactory();
factory.init("config.xml");
BusinessObj obj = (BusinessObj) factory.getBean("businessObj");
obj.process();
}
}
觀察此類我們可以發現,該類添加了對bean元素的aop和aopType屬性的處理。編寫完該文件后,我們還需要修改src目錄下的配置文件:config.xml文件,在該文件中添加名為businessObj的bean,我們為其配置了aop和aopType屬性,增加了方法的前置顧問。增加的部分為:
<bean id="businessObj" class="org.amigo.proxy.BusinessObjImpl" aop="org.amigo.proxy.BeforeMethodAdvisor" aopType="before"/>
此時運行BeanFactory.java這個類文件,運行結果如下:
before process public abstract void org.amigo.proxy.BusinessObj.process()
執行業務邏輯
由運行結果可以看出,前置處理已經生效。
本節中的例子只是實現了Spring的AOP一小部分功能,即為某個bean添加前置或后置處理,在Spring中,考慮的比這多很多,例如,為多個bean配置動態代理等等。但是究其根源,Spring中AOP的實現,是基于Java中無比強大的反射和動態代理機制。
四.總結
本文講述了AOP的概念等信息,并詳細講解了Java的動態代理機制,接著又通過一個簡單的實例講解了Spring中AOP的模擬實現,使得讀者能夠更好地學習Java的反射和代理機制。
通過這兩篇文章,使得我們能夠更加深入地理解Java的反射和動態代理機制,同時對Spring中盛行的IOC和AOP的后臺實現原理有了更加清晰的理解,Java的反射和動態代理機制的強大功能在這兩篇文章中可見一斑。有興趣的朋友可以通過學習Spring框架的源碼來進一步的理解Java的反射和動態代理機制,從而在實際的開發工作中更好地理解它。
posted on 2007-10-17 12:20
阿蜜果 閱讀(3436)
評論(9) 編輯 收藏 所屬分類:
Java