您的位置:首页 > 编程语言 > Java开发

利用Java的反射与代理机制实现AOP

2009-12-03 14:35 806 查看
在上一篇文章中,我们讲述了利用
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的反射和动态代理机制,从而在实际的开发工作中更好地理解它。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息