﻿<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:trackback="http://madskills.com/public/xml/rss/module/trackback/" xmlns:wfw="http://wellformedweb.org/CommentAPI/" xmlns:slash="http://purl.org/rss/1.0/modules/slash/"><channel><title>BlogJava-天道酬勤-文章分类-Spring</title><link>http://www.blogjava.net/hanhonl/category/25415.html</link><description /><language>zh-cn</language><lastBuildDate>Thu, 30 Aug 2007 14:50:35 GMT</lastBuildDate><pubDate>Thu, 30 Aug 2007 14:50:35 GMT</pubDate><ttl>60</ttl><item><title>AOP编程入门--Java篇</title><link>http://www.blogjava.net/hanhonl/articles/141397.html</link><dc:creator>Alex_Han</dc:creator><author>Alex_Han</author><pubDate>Thu, 30 Aug 2007 07:24:00 GMT</pubDate><guid>http://www.blogjava.net/hanhonl/articles/141397.html</guid><wfw:comment>http://www.blogjava.net/hanhonl/comments/141397.html</wfw:comment><comments>http://www.blogjava.net/hanhonl/articles/141397.html#Feedback</comments><slash:comments>0</slash:comments><wfw:commentRss>http://www.blogjava.net/hanhonl/comments/commentRss/141397.html</wfw:commentRss><trackback:ping>http://www.blogjava.net/hanhonl/services/trackbacks/141397.html</trackback:ping><description><![CDATA[<div>
<p>Aspect Oriented Programming(AOP)，面向切面编程，是一个比较热门的话题。AOP主要实现的目的是针对业务处理过程中的切面进行提取，它所面对的是处理过程中的某个步骤或阶段，以获得逻辑过程中各部分之间低耦合性的隔离效果。比如我们最常见的就是日志记录了，举个例子，我们现在提供一个服务查询学生信息的，但是我们希望记录有谁进行了这个查询。如果按照传统的OOP的实现的话，那我们实现了一个查询学生信息的服务接口(StudentInfoService)和其实现类（StudentInfoServiceImpl.java），同时为了要进行记录的话，那我们在实现类(StudentInfoServiceImpl.java)中要添加其实现记录的过程。这样的话，假如我们要实现的服务有多个呢？那就要在每个实现的类都添加这些记录过程。这样做的话就会有点繁琐，而且每个实现类都与记录服务日志的行为紧耦合，违反了面向对象的规则。那么怎样才能把记录服务的行为与业务处理过程中分离出来呢？看起来好像就是查询学生的服务自己在进行，但是背后日志记录对这些行为进行记录，但是查询学生的服务不知道存在这些记录过程，这就是我们要讨论AOP的目的所在。AOP的编程，好像就是把我们在某个方面的功能提出来与一批对象进行隔离，这样与一批对象之间降低了耦合性，可以就某个功能进行编程。<br>&nbsp;&nbsp;&nbsp;&nbsp;我们直接从代码入手吧，要实现以上的目标，我们可以使用一个动态代理类(Proxy)，通过拦截一个对象的行为并添加我们需要的功能来完成。Java中的java.lang.reflect.Proxy类和java.lang.reflect.InvocationHandler接口为我们实现动态代理类提供了一个方案，但是该方案针对的对象要实现某些接口；如果针对的目的是类的话，cglib为我们提供了另外一个实现方案。等下会说明两者的区别。<br>一、接口的实现方案：<br>1）首先编写我们的业务接口（StudentInfoService.java）：<br>public interface StudentInfoService{<br>&nbsp;void findInfo(String studentName);<br>}<br>&nbsp;&nbsp;&nbsp;&nbsp; 及其实现类（StudentInfoServiceImpl.java）：<br>public class StudentInfoServiceImpl implements StudentInfoService{<br>&nbsp;public void findInfo(String name){<br>&nbsp;&nbsp;System.out.println("你目前输入的名字是:"+name);<br>&nbsp;}<br>}<br>2）现在我们需要一个日志功能，在findInfo行为之前执行并记录其行为，那么我们就首先要拦截该行为。在实际执行的过程中用一个代理类来替我们完成。Java中为我们提供了实现动态代理类的方案：
<p>1'处理拦截目的的类（MyHandler.java）<br>import org.apache.log4j.Logger;<br>import java.lang.reflect.InvocationHandler;<br>import java.lang.reflect.Proxy;<br>import java.lang.reflect.Method;
<p>public class MyHandler implements InvocationHandler{<br>&nbsp;private Object proxyObj;<br>&nbsp;private static Logger log=Logger.getLogger(MyHandler.class);<br>&nbsp;<br>&nbsp;public Object bind(Object obj){<br>&nbsp;&nbsp;this.proxyObj=obj;<br>&nbsp;&nbsp;return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),this);<br>&nbsp;}<br>&nbsp;<br>&nbsp;public Object invoke(Object proxy,Method method,Object[] args) throws Throwable{<br>&nbsp;&nbsp;Object result=null;<br>&nbsp;&nbsp;try{<br>&nbsp;&nbsp;&nbsp;//请在这里插入代码，在方法前调用<br>&nbsp;&nbsp;&nbsp;log.info("调用log日志方法"+method.getName());<br>&nbsp;&nbsp;&nbsp;result=method.invoke(proxyObj,args); //原方法<br>&nbsp;&nbsp;&nbsp;//请在这里插入代码，方法后调用<br>&nbsp;&nbsp;}catch(Exception e){<br>&nbsp;&nbsp;&nbsp;e.printStackTrace();<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;return result;<br>&nbsp;}<br>}<br>2'我们实现一个工厂，为了方便我们使用该拦截类(AOPFactory.java)：<br>public class AOPFactory{<br>&nbsp;private static Object getClassInstance(String clzName){<br>&nbsp;&nbsp;Object obj=null;<br>&nbsp;&nbsp;try{<br>&nbsp;&nbsp;&nbsp;Class cls=Class.forName(clzName);<br>&nbsp;&nbsp;&nbsp;obj=(Object)cls.newInstance();<br>&nbsp;&nbsp;}catch(ClassNotFoundException cnfe){<br>&nbsp;&nbsp;&nbsp;System.out.println("ClassNotFoundException:"+cnfe.getMessage());<br>&nbsp;&nbsp;}catch(Exception e){<br>&nbsp;&nbsp;&nbsp;e.printStackTrace();<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;return obj;<br>&nbsp;}<br>&nbsp;<br>&nbsp;public static Object getAOPProxyedObject(String clzName){<br>&nbsp;&nbsp;Object proxy=null;<br>&nbsp;&nbsp;MyHandler handler=new MyHandler();<br>&nbsp;&nbsp;Object obj=getClassInstance(clzName);<br>&nbsp;&nbsp;if(obj!=null) {<br>&nbsp;&nbsp;&nbsp;proxy=handler.bind(obj);<br>&nbsp;&nbsp;}else{<br>&nbsp;&nbsp;&nbsp;System.out.println("Can't get the proxyobj");<br>&nbsp;&nbsp;&nbsp;//throw<br>&nbsp;&nbsp;}<br>&nbsp;&nbsp;return proxy;<br>&nbsp;}<br>}
<p>3)基本的拦截与其工厂我们都实现了，现在测试（ClientTest.java）：<br>public class ClientTest{<br>&nbsp;public static void main(String[] args){<br>&nbsp;&nbsp;StudentInfoService studentInfo=(StudentInfoService)AOPFactory.getAOPProxyedObject("StudentInfoServiceImpl");<br>&nbsp;&nbsp;studentInfo.findInfo("阿飞");<br>&nbsp;}<br>}<br>输出结果（看你的log4j设置）：<br>[INFO]调用log日志方法findInfo<br>你目前输入的名字是:阿飞<br>&nbsp;&nbsp;&nbsp;&nbsp; 这样我们需要的效果就出来了，业务处理自己在进行，但是我们实现了日志功能，而业务处理(StudentInfoService)根本不知道存在该行为的。但是Java中提供的动态代理类的实现是针对实现了某些接口的类，如果没有实现接口的话，不能创建代理类，看以上部分：<br>return Proxy.newProxyInstance(obj.getClass().getClassLoader(),obj.getClass().getInterfaces(),this);<br>看到了没有？obj.getClass().getInterfaces()要求实现了某些接口。以下提供哪些没有实现接口的实现方案：<br><br>二、子类的实现方案。<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 首先，请上网下CGLib的包，<a href="http://sourceforge.net/project/showfiles.php?group_id=56933"><font color=#000000><u>http://sourceforge.net/project/showfiles.php?group_id=56933</u></font></a>&nbsp;。设置好classpath路径，CGLib与java标准库提供的实现方案不同，cglib主要是基于实现类（如StudentInfoServiceImpl.java)扩展一个子类来实现。与Dynamic Proxy中的Proxy和InvocationHandler相对应，net.sf.cglib.proxy.Enhancer和MethodInterceptor在CGLib中负责完成代理对象创建和方法截获处理,产生的是目标类的子类而不是通过接口来实现方法拦截的，Enhancer主要是用于构造动态代理子类来实现拦截，MethodInterceptor（扩展了Callback接口）主要用于实现around advice（AOP中的概念）：<br>&nbsp;&nbsp;&nbsp;&nbsp; 1）我们的业务处理（StudentInfoServiceImpl.java）：<br>public class StudentInfoServiceImpl{<br>&nbsp;public void findInfo(String name){<br>&nbsp;&nbsp;System.out.println("你目前输入的名字是:"+name);<br>&nbsp;}<br>}<br>&nbsp;&nbsp;&nbsp;&nbsp; 2）实行一个工具来处理日志功能（AOPInstrumenter.java）：<br>import net.sf.cglib.proxy.MethodInterceptor;<br>import net.sf.cglib.proxy.Enhancer;<br>import net.sf.cglib.proxy.MethodProxy;<br>import java.lang.reflect.Method;<br>import org.apache.log4j.Logger;
<p>public class AOPInstrumenter implements MethodInterceptor{<br>&nbsp;private Logger log=Logger.getLogger(AOPInstrumenter.class);<br>&nbsp;private Enhancer enhancer=new Enhancer();<br>&nbsp;<br>&nbsp;public Object getInstrumentedClass(Class clz){<br>&nbsp;&nbsp;enhancer.setSuperclass(clz);<br>&nbsp;&nbsp;enhancer.setCallback(this);<br>&nbsp;&nbsp;return enhancer.create();<br>&nbsp;}<br>&nbsp;<br>&nbsp;public Object intercept(Object o,Method method,Object[] args,MethodProxy proxy) throws Throwable{<br>&nbsp;&nbsp;log.info("调用日志方法"+method.getName());<br>&nbsp;&nbsp;Object result=proxy.invokeSuper(o,args);<br>&nbsp;&nbsp;return result;<br>&nbsp;}<br>&nbsp;<br>}<br>&nbsp;&nbsp;&nbsp;&nbsp; 3）我们来测试一下（AOPTest.java）：<br>public class AOPTest{<br>&nbsp;public static void main(String[] args){<br>&nbsp;&nbsp;AOPInstrumenter instrumenter=new AOPInstrumenter();<br>&nbsp;&nbsp;StudentInfoServiceImpl studentInfo=(StudentInfoServiceImpl)instrumenter.getInstrumentedClass(StudentInfoServiceImpl.class);<br>&nbsp;&nbsp;studentInfo.findInfo("阿飞");<br>&nbsp;}<br>}<br>&nbsp;&nbsp; 输出结果与以上相同。<br>&nbsp;CGLib中为实现以上目的，主要提供的类<br>1)Enhancer：setCallback(Callback) ,setSuperclass(Class) ,create()返回动态子类Object<br>2)MethodInterceptor必须实现的接口：intercept(Object,Method,Object[],MethodProxy)返回的是原方法调用的结果。和Proxy原理一样。
<p>三、以上的两个简单实现AOP的方案都为你准备好了，你可以自己编写测试一下，以下简单介绍一下AOP的基本概念：<br>1）aspect（切面）：实现了cross-cutting功能，是针对切面的模块。最常见的是logging模块，这样，程序按功能被分为好几层，如果按传统的继承的话，商业模型继承日志模块的话根本没有什么意义，而通过创建一个logging切面就可以使用AOP来实现相同的功能了。<br>2）jointpoint（连接点）：连接点是切面插入应用程序的地方，该点能被方法调用，而且也会被抛出意外。连接点是应用程序提供给切面插入的地方，可以添加新的方法。比如以上我们的切点可以认为是findInfo(String)方法。<br>3）advice（处理逻辑）：advice是我们切面功能的实现，它通知程序新的行为。如在logging里，logging advice包括logging的实现代码，比如像写日志到一个文件中。advice在jointpoint处插入到应用程序中。以上我们在MyHandler.java中实现了advice的功能<br>4）pointcut（切点）：pointcut可以控制你把哪些advice应用于jointpoint上去，通常你使用pointcuts通过正则表达式来把明显的名字和模式进行匹配应用。决定了那个jointpoint会获得通知。<br>5）introduction：允许添加新的方法和属性到类中。<br>6）target（目标类）：是指那些将使用advice的类，一般是指独立的那些商务模型。比如以上的StudentInfoServiceImpl.
<p>7)proxy（代理类）：使用了proxy的模式。是指应用了advice的对象，看起来和target对象很相似。<br>8）weaving(插入）：是指应用aspects到一个target对象创建proxy对象的过程：complie time，classload time，runtime<br>
<p>&#160;</p>
</div>
<img src ="http://www.blogjava.net/hanhonl/aggbug/141397.html" width = "1" height = "1" /><br><br><div align=right><a style="text-decoration:none;" href="http://www.blogjava.net/hanhonl/" target="_blank">Alex_Han</a> 2007-08-30 15:24 <a href="http://www.blogjava.net/hanhonl/articles/141397.html#Feedback" target="_blank" style="text-decoration:none;">发表评论</a></div>]]></description></item></channel></rss>