隨筆 - 63  文章 - 0  trackbacks - 0
          <2009年4月>
          2930311234
          567891011
          12131415161718
          19202122232425
          262728293012
          3456789

          常用鏈接

          留言簿(2)

          隨筆分類

          隨筆檔案

          搜索

          •  

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

          利用Java的反射與代理實(shí)現(xiàn)AOP

           

          一.AOP概述
                 AOP(Aspect Oriented Programing),即面向切面編程,它主要用于日志記錄、性能統(tǒng)計(jì)、控制、事務(wù)處理、異常處理等方面。它的主要意圖就要將日志記錄,性能統(tǒng)計(jì),安全控制、事務(wù)處理、異常處理等等代碼從業(yè)務(wù)邏輯代碼中清楚地劃分出來。通過對(duì)這些行為的分離,我們希望可以將它們獨(dú)立地配置到業(yè)務(wù)邏輯方法中,而要改變這些行為的時(shí)候也不需要影響到業(yè)務(wù)邏輯方法代碼。
                 下面讓我們來看一個(gè)利用AOP來實(shí)現(xiàn)日志記錄的例子,在沒有使用AOP之前,我們的代碼如下面所講述。
                 下面這段代碼為業(yè)務(wù)的接口類代碼:

          package org.amigo.proxy; /** * 業(yè)務(wù)邏輯類接口. * @author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a> * Creation date: 2007-10-7 - 上午09:09:53 */ public interface BusinessObj { /** * 執(zhí)行業(yè)務(wù). */ public void process(); } BusinessObj接口的某個(gè)實(shí)現(xiàn)類代碼如下: package org.amigo.proxy; /** * 業(yè)務(wù)邏輯對(duì)象實(shí)現(xiàn)類. * @author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a> * Creation date: 2007-10-7 - 上午09:11:49 */ public class BusinessObjImpl implements BusinessObj { /** * 執(zhí)行業(yè)務(wù). */ public void process() { try { System.out.println("before process"); System.out.println("執(zhí)行業(yè)務(wù)邏輯"); System.out.println("after process"); } catch (Exception e) { System.err.println("發(fā)生異常:" + e.toString()); } } }
              在上例中我們可以看到,在執(zhí)行業(yè)務(wù)方法前、執(zhí)行業(yè)務(wù)方法后以及異常發(fā)生時(shí)的日志記錄,我們都是通過在對(duì)應(yīng)的類中寫入記錄日志的代碼來實(shí)現(xiàn)的,當(dāng)有這種日志記錄需求的業(yè)務(wù)邏輯類不斷增多時(shí),將會(huì)給我們的維護(hù)帶來很大困難,而且,在上面的例子中,日志代碼和業(yè)務(wù)邏輯代碼混合在一起,為日后的維護(hù)工作又抹上了一層“恐怖”色彩。
                 按照AOP的思想,我們首先需要尋找一個(gè)切面,在這里我們已經(jīng)找到,即在業(yè)務(wù)邏輯執(zhí)行前后以及異常發(fā)生時(shí),進(jìn)行相應(yīng)的日志記錄。我們需要將這部分日志代碼放入一個(gè)單獨(dú)的類中,以便為以后的修改提供方便。
                 我們?cè)诮孬@某個(gè)業(yè)務(wù)邏輯方法時(shí),可以采用Java的動(dòng)態(tài)代理機(jī)制來實(shí)現(xiàn)。
           
           
          二.Java的動(dòng)態(tài)代理機(jī)制
          代理模式是常用的Java設(shè)計(jì)模式。代理類主要負(fù)責(zé)為委托類預(yù)處理消息、過濾信息、把消息轉(zhuǎn)發(fā)給委托類,以及事后處理信息等。
          動(dòng)態(tài)代理類不僅簡化了編程工作,而且提高了軟件系統(tǒng)的擴(kuò)展性和可維護(hù)性。我們可以通過實(shí)現(xiàn)java.lang.reflect.InvocationHandler接口提供一個(gè)執(zhí)行處理器,然后通過java.lang.reflect.Proxy得到一個(gè)代理對(duì)象,通過這個(gè)代理對(duì)象來執(zhí)行業(yè)務(wù)邏輯方法,在業(yè)務(wù)邏輯方法被調(diào)用的同時(shí),自動(dòng)調(diào)用會(huì)執(zhí)行處理器。
                
          下面讓我們來創(chuàng)建一個(gè)日志攔截器類LogInterceptor.java文件,該類實(shí)現(xiàn)java.lang.reflect.InvocationHandler接口,其內(nèi)容如下所示:

          package org.amigo.proxy; import java.lang.reflect.InvocationHandler; import java.lang.reflect.Method; import java.lang.reflect.Proxy; /** * 日志攔截器,用來進(jìn)行日志處理. * @author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a> * Creation date: 2007-10-7 - 上午09:31:44 */ public class LogInterceptor implements InvocationHandler { private Object delegate; /** * 構(gòu)造函數(shù),設(shè)置代理對(duì)象. */ public LogInterceptor(Object delegate){ this.delegate = delegate; } /** * 方法的調(diào)用. * @param proxy * @param method 對(duì)應(yīng)的方法 * @param args 方法的參信息 * @return 返回操作結(jié)果對(duì)象 * @throws Throwable */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { Object result = null; try { System.out.println("before process" + method); //調(diào)用代理對(duì)象delegate的method方法,并將args作為參數(shù)信息傳入 result = method.invoke(delegate, args); System.out.println("after process" + method); } catch (Exception e){ System.err.println("發(fā)生異常:" + e.toString()); } return result; } /** * 測試方法. * @param args * @throws Exception */ public static void main(String[] args) throws Exception { BusinessObj obj = new BusinessObjImpl(); //創(chuàng)建一個(gè)日志攔截器 LogInterceptor interceptor = new LogInterceptor(obj); //通過Proxy類的newProxyInstance(...)方法來獲得動(dòng)態(tài)的代理對(duì)象 BusinessObj proxy = (BusinessObj) Proxy.newProxyInstance( BusinessObjImpl.class.getClassLoader(), BusinessObjImpl.class.getInterfaces(), interceptor); //執(zhí)行動(dòng)態(tài)代理對(duì)象的業(yè)務(wù)邏輯方法 proxy.process(); } }
           
          此時(shí)還需要對(duì)BusinessObj的實(shí)現(xiàn)類BusinessObjImpl類進(jìn)行修改,去掉其日志記錄等內(nèi)容,修改后的文件如下:

          package org.amigo.proxy; /** * 業(yè)務(wù)邏輯對(duì)象實(shí)現(xiàn)類. * @author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a> * Creation date: 2007-10-7 - 上午09:11:49 */ public class BusinessObjImpl implements BusinessObj { /** * 執(zhí)行業(yè)務(wù). */ public void process() { System.out.println("執(zhí)行業(yè)務(wù)邏輯"); } }
          運(yùn)行LogInterceptor類我們可以發(fā)現(xiàn),它實(shí)現(xiàn)了前面所需要的功能,但是很好的將業(yè)務(wù)邏輯方法的代碼和日志記錄的代碼分離開來,并且所有的業(yè)務(wù)處理對(duì)象都可以利用該類來完成日志的記錄,防止了重復(fù)代碼的出現(xiàn),增加了程序的可擴(kuò)展性和可維護(hù)性,從而提高了代碼的質(zhì)量。那么Spring中的AOP的實(shí)現(xiàn)是怎么樣的呢?接著讓我們來對(duì)Spring的AOP進(jìn)行探討,了解其內(nèi)部實(shí)現(xiàn)原理。
          三.Spring中AOP的模擬實(shí)現(xiàn)
                 在學(xué)習(xí)了Java的動(dòng)態(tài)代理機(jī)制后,我們?cè)诒竟?jié)中將學(xué)習(xí)Java的動(dòng)態(tài)代理機(jī)制在Spring中的應(yīng)用。首先我們創(chuàng)建一個(gè)名為AopHandler的類,該類可生成代理對(duì)象,同時(shí)可以根據(jù)設(shè)置的前置或后置處理對(duì)象分別在方法執(zhí)行前后執(zhí)行一些另外的操作,該類的內(nèi)容如下所示:

          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 { //需要代理的目標(biāo)對(duì)象 private Object target; //方法前置顧問 Advisor beforeAdvisor; //方法后置顧問 Advisor afterAdvisor; /** * 設(shè)置代理目標(biāo)對(duì)象,并生成動(dòng)態(tài)代理對(duì)象. * @param target 代理目標(biāo)對(duì)象 * @return 返回動(dòng)態(tài)代理對(duì)象 */ public Object setObject(Object target) { //設(shè)置代理目標(biāo)對(duì)象 this.target = target; //根據(jù)代理目標(biāo)對(duì)象生成動(dòng)態(tài)代理對(duì)象 Object obj = Proxy.newProxyInstance( target.getClass().getClassLoader(), target.getClass().getInterfaces(), this); return obj; } /** * 若定義了前置處理,則在方法執(zhí)行前執(zhí)行前置處理, * 若定義了后置處理,則在方法調(diào)用后調(diào)用后置處理. * @param proxy 代理對(duì)象 * @param method 調(diào)用的業(yè)務(wù)方法 * @param args 方法的參數(shù) * @return 返回結(jié)果信息 * @throws Throwable */ public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //進(jìn)行業(yè)務(wù)方法的前置處理 if (beforeAdvisor != null) { beforeAdvisor.doInAdvisor(proxy, method, args); } //執(zhí)行業(yè)務(wù)方法 Object result = method.invoke(target, args); //進(jìn)行業(yè)務(wù)方法的后置處理 if (afterAdvisor != null) { afterAdvisor.doInAdvisor(proxy, method, args); } //返回結(jié)果對(duì)象 return result; } /** * 設(shè)置方法的前置顧問. * @param advisor 方法的前置顧問 */ public void setBeforeAdvisor(Advisor advisor) { this.beforeAdvisor = advisor; } /** * 設(shè)置方法的后置顧問. * @param advisor 方法的后置顧問 */ public void setAfterAdvisor(Advisor advisor) { this.afterAdvisor = advisor; } }
              在上類中,前置和后置顧問對(duì)象都繼承Advisor接口,接下來讓我們來看看顧問接口類的內(nèi)容,該類定義了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> * Creation date: 2007-10-7 - 上午10:21:18 */ public interface Advisor { /** * 所做的操作. */ public void doInAdvisor(Object proxy, Method method, Object[] args); } BeforeMethodAdvisor和AfterMethodAdvisor都實(shí)現(xiàn)了Advisor接口,分別為方法的前置顧問和后置顧問類。 BeforeMethodAdvisor.java文件(前置顧問類)的內(nèi)容如下所示: package org.amigo.proxy; import java.lang.reflect.Method; /** * * 方法前置顧問,它完成方法的前置操作. * @author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a> * Creation date: 2007-10-7 - 上午10:19:57 */ public class BeforeMethodAdvisor implements Advisor { /** * 在方法執(zhí)行前所進(jìn)行的操作. */ public void doInAdvisor(Object proxy, Method method, Object[] args) { System.out.println("before process " + method); } } AfterMethodAdvisor.java文件(后置顧問類)的內(nèi)容如下所示: package org.amigo.proxy; import java.lang.reflect.Method; /** * * 方法的后置顧問,它完成方法的后置操作. * @author <a href="mailto:xiexingxing1121@126.com">AmigoXie</a> * Creation date: 2007-10-7 - 上午10:20:43 */ public class AfterMethodAdvisor implements Advisor { /** * 在方法執(zhí)行后所進(jìn)行的操作. */ public void doInAdvisor(Object proxy, Method method, Object[] args) { System.out.println("after process " + method); } }
          這兩個(gè)類分別在方法執(zhí)行前和方法執(zhí)行后做一些額外的操作。
                 對(duì)于在配置文件中對(duì)某個(gè)bean配置前置或后置處理器,我們可以在bean中增加兩個(gè)屬性aop和aopType,aop的值為對(duì)應(yīng)的前置顧問類或后置顧問類的名稱,aopType用于指明該顧問類為前置還是后置顧問,為before時(shí)表示為前置處理器,為after時(shí)表示為后置處理器,這時(shí)候我們需要修改上一篇文章中的BeanFactory.java這個(gè)文件,添加其對(duì)aop和aopType的處理,修改后的該文件內(nèi)容如下:

          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> * Creation date: 2007-10-7 - 下午04:04:34 */ public class BeanFactory { private Map<String, Object> beanMap = new HashMap<String, Object>(); /** * bean工廠的初始化. * @param xml xml配置文件 */ public void 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; //創(chuàng)建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屬性時(shí),需進(jìn)行攔截操作 if (aop != null && aopType != null) { //根據(jù)aop字符串獲取對(duì)應(yīng)的類 Class advisorCls = Class.forName(aop.getText()); //創(chuàng)建該類的對(duì)象 Advisor advisor = (Advisor) advisorCls.newInstance(); //根據(jù)aopType的類型來設(shè)置前置或后置顧問 if ("before".equals(aopType.getText())) { aopHandler.setBeforeAdvisor(advisor); } else if ("after".equals(aopType.getText())) { aopHandler.setAfterAdvisor(advisor); } } //利用Java反射機(jī)制,通過class的名稱獲取Class對(duì)象 Class bean = Class.forName(cls.getText()); //獲取對(duì)應(yīng)class的信息 java.beans.BeanInfo info = java.beans.Introspector.getBeanInfo(bean); //獲取其屬性描述 java.beans.PropertyDescriptor pd[] = info.getPropertyDescriptors(); //設(shè)置值的方法 Method mSet = null; //創(chuàng)建一個(gè)對(duì)象 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的反射極致調(diào)用對(duì)象的某個(gè)set方法,并將值設(shè)置進(jìn)去 mSet.invoke(obj, value); } } } //為對(duì)象增加前置或后置顧問 obj = (Object) aopHandler.setObject(obj); //將對(duì)象放入beanMap中,其中key為id值,value為對(duì)象 beanMap.put(id.getText(), obj); } } catch (Exception e) { System.out.println(e.toString()); } } /** * 通過bean的id獲取bean的對(duì)象. * @param beanName bean的id * @return 返回對(duì)應(yīng)對(duì)象 */ public Object getBean(String beanName) { Object obj = beanMap.get(beanName); return obj; } /** * 測試方法. * @param args */ public static void main(String[] args) { BeanFactory factory = new BeanFactory(); factory.init("config.xml"); BusinessObj obj = (BusinessObj) factory.getBean("businessObj"); obj.process(); } }
              觀察此類我們可以發(fā)現(xiàn),該類添加了對(duì)bean元素的aop和aopType屬性的處理。編寫完該文件后,我們還需要修改src目錄下的配置文件:config.xml文件,在該文件中添加名為businessObj的bean,我們?yōu)槠渑渲昧薬op和aopType屬性,增加了方法的前置顧問。增加的部分為:

          <bean id="businessObj" class="org.amigo.proxy.BusinessObjImpl" aop="org.amigo.proxy.BeforeMethodAdvisor" aopType="before"/>
                 此時(shí)運(yùn)行BeanFactory.java這個(gè)類文件,運(yùn)行結(jié)果如下:
          before process public abstract void org.amigo.proxy.BusinessObj.process()
          執(zhí)行業(yè)務(wù)邏輯
                 由運(yùn)行結(jié)果可以看出,前置處理已經(jīng)生效。
                 本節(jié)中的例子只是實(shí)現(xiàn)了Spring的AOP一小部分功能,即為某個(gè)bean添加前置或后置處理,在Spring中,考慮的比這多很多,例如,為多個(gè)bean配置動(dòng)態(tài)代理等等。但是究其根源,Spring中AOP的實(shí)現(xiàn),是基于Java中無比強(qiáng)大的反射和動(dòng)態(tài)代理機(jī)制。
           
          四.總結(jié)
                 本文講述了AOP的概念等信息,并詳細(xì)講解了Java的動(dòng)態(tài)代理機(jī)制,接著又通過一個(gè)簡單的實(shí)例講解了Spring中AOP的模擬實(shí)現(xiàn),使得讀者能夠更好地學(xué)習(xí)Java的反射和代理機(jī)制。
          通過這兩篇文章,使得我們能夠更加深入地理解Java的反射和動(dòng)態(tài)代理機(jī)制,同時(shí)對(duì)Spring中盛行的IOC和AOP的后臺(tái)實(shí)現(xiàn)原理有了更加清晰的理解,Java的反射和動(dòng)態(tài)代理機(jī)制的強(qiáng)大功能在這兩篇文章中可見一斑。有興趣的朋友可以通過學(xué)習(xí)Spring框架的源碼來進(jìn)一步的理解Java的反射和動(dòng)態(tài)代理機(jī)制,從而在實(shí)際的開發(fā)工作中更好地理解
          posted on 2009-04-06 11:50 lanxin1020 閱讀(171) 評(píng)論(0)  編輯  收藏 所屬分類: spring
          主站蜘蛛池模板: 平泉县| 大安市| 桑植县| 文昌市| 贵阳市| 张家川| 沙坪坝区| 林口县| 龙里县| 米脂县| 肃南| 库尔勒市| 繁昌县| 朝阳市| 晋江市| 德格县| 娄底市| 安远县| 木兰县| 庄浪县| 息烽县| 阜阳市| 唐海县| 万源市| 大丰市| 峨边| 华池县| 汨罗市| 黑山县| 洛浦县| 德化县| 太白县| 常州市| 富蕴县| 临汾市| 舞阳县| 福泉市| 玛多县| 耿马| 阜新| 姜堰市|