spring學習筆記: Spring AOP

來源:互聯網
上載者:User

AOP的概念不好解釋, 有一大堆的術語都很拗口,還是先看一個代碼例子,在src根目錄下面建立一個package叫做aop,把這個單元所有的代碼都放在這個包裡面,有一個介面BookService,它有一個實現BookServiceImpl, 專門來處理與Book相關的商務邏輯。

package aop;<br />import model.Book;</p><p>public interface BookService {<br /> public void removeBook(Book book);<br /> public void insertBook(Book book);<br />}</p><p>package aop;<br />import model.Book;<br />public class BookServiceImpl implements BookService{<br /> TransactionManager txmanager = new TransactionManager();<br /> PerformanceMonitor monitor = new PerformanceMonitor();</p><p> public void removeBook(Book book){<br /> monitor.start();<br /> txmanager.begin();<br /> //類比刪除操作<br /> System.out.println("book removed.");<br /> txmanager.commit();<br /> monitor.end();<br /> }<br /> public void insertBook(Book book){<br /> monitor.start();<br /> txmanager.begin();<br /> //類比插入操作<br /> System.out.println("book inserted.");<br /> txmanager.commit();<br /> monitor.end();<br /> }<br />}<br />
你不需要把BookServiceImpl 這個例子拷貝出來並且測試,上面的TransactionManager和PerformanceMonitor 都是假設的類,只需要閱讀一下就可以了。 removeBook 和 insertBook 調用dao層來和資料庫互動,同時我們給這兩個方法加上了效能監測和事務控制。而效能檢測和事務控制很多時候都是必需的。而且他們緊緊地依附於商務邏輯,monitor.start();txmanager.begin(); 以及txmanager.commit();monitor.end();這些和主流商務邏輯無關的代碼無法通過其他方式消除。
AOP的目的就是要把這種和核心邏輯無關的卻又無法消除的代碼從核心代碼中剝離出來,使商務邏輯類專註於商務邏輯, 並且在適當的時候(比如運行時)再把他們植入到核心業務代碼中.說白了,就是希望把上面的BookServiceImpl變成這個樣子:
public class BookServiceImpl extends BookService{<br /> public void removeBook(Book book){<br /> //類比刪除操作<br /> System.out.println("book removed.");<br /> }<br /> public void insertBook(Book book){<br /> //類比插入操作<br /> System.out.println("book inserted.");<br /> }<br />}
那麼效能檢測和事務控制的代碼跑到哪裡去了呢? 到增強(Advice)裡面去了. 下面看幾個概念,包括Advice.
連接點(Joinpoint): 程式執行的某個特定位置, 比如方法調用前後, 類初始化前,異常拋出之後.
增強(Advice): 植入到目標類的連接點上的程式碼.
目標對象(Target): 被植入增強類, 比如上面簡化之後的BookService.
代理(Proxy): 被植入增強之後產生的結果類.
我們把效能監測和事務控制的邏輯除掉之後,如果想繼續使用效能檢測和事務控制, 則要做兩件事情:
1 建立增強(Advice),把效能監測和事務控制的代碼放到增強裡面;
2 用一種靈活的方式把增強(Advice)植入到乾淨的BookService裡面.
Spring提供了前置增強,環繞增強,後置增強,異常拋出增強等,
前置增強  假如我們的商務邏輯類就是上面的BookServiceImpl,它是一個乾淨的商務邏輯,我們利用前置增強來改善一下它的方法,希望在方法調用前做一些工作. 建立一個前置增強,繼承自MethodBeforeAdvice.他的作用很簡單, 就是列印出目標對象的方法名. 需要注意的是aopalliance.jar, cglib, spring-aop.jar要加入進來。package aop;<br />import java.lang.reflect.Method;<br />import org.springframework.aop.MethodBeforeAdvice;</p><p>public class BookServiceBeforeAdvice implements MethodBeforeAdvice{<br /> public void before(Method method, Object[] args, Object obj)throws Throwable{<br /> System.out.println("advice before method " + obj.getClass().getName() + "." + method.getName() + "().");<br /> }<br />}<br />
通過代理工廠ProxyFactory把Advice植入到目標對象BookService並且產生代理對象.
package aop;<br />import model.Book;<br />import org.springframework.aop.BeforeAdvice;<br />import org.springframework.aop.framework.ProxyFactory;</p><p>public class TestAdvice {<br /> public static void main(String[] args) {<br /> BookService target = new BookServiceImpl();<br /> BeforeAdvice advice = new BookServiceBeforeAdvice();<br /> ProxyFactory pf = new ProxyFactory();<br /> pf.setTarget(target);<br /> pf.addAdvice(advice);<br /> BookService service = (BookService)pf.getProxy();<br /> service.insertBook(new Book());<br /> service.removeBook(new Book());<br /> }<br />}</p><p>

列印出的訊息:
advice before method aop.BookService.insertBook().
book inserted.
advice before method aop.BookService.removeBook().
book removed.
說明在方法調用前,增強代碼被執行了.
後置增強(AfterReturningAdvice)   目標方法調用之後執行.
package aop;<br />import java.lang.reflect.Method;<br />import org.springframework.aop.AfterReturningAdvice;</p><p>public class BookServiceAfterRunningAdvice implements AfterReturningAdvice{<br /> public void afterReturning(Object returnObj, Method method, Object[] args, Object target) throws Throwable {<br /> System.out.println("method " + target.getClass().getName() + "." + method.getName() + "() has been executed.");<br /> }<br />}<br />
稍微修改一下TestAdvice,為service添加一個後置增強。
package aop;<br />import model.Book;<br />import org.springframework.aop.AfterAdvice;<br />import org.springframework.aop.BeforeAdvice;<br />import org.springframework.aop.framework.ProxyFactory;</p><p>public class TestAdvice {<br /> public static void main(String[] args) {<br /> BookService target = new BookServiceImpl();<br /> BeforeAdvice beforeAdvice = new BookServiceBeforeAdvice();<br /> AfterAdvice afterAdvice = new BookServiceAfterRunningAdvice();<br /> ProxyFactory pf = new ProxyFactory();<br /> pf.setTarget(target);<br /> pf.addAdvice(beforeAdvice);<br /> pf.addAdvice(afterAdvice);<br /> BookService service = (BookService)pf.getProxy();<br /> service.insertBook(new Book());<br /> service.removeBook(new Book());<br /> }<br />}</p><p>

列印的訊息:
advice before method aop.BookService.insertBook().
book inserted.
method aop.BookService.insertBook() has been executed.
advice before method aop.BookService.removeBook().
book removed.
method aop.BookService.removeBook() has been executed.
環繞增強(MethodInterceptor)   就是在方法前後都有程式碼片段執行。
package aop;<br />import org.aopalliance.intercept.MethodInterceptor;<br />import org.aopalliance.intercept.MethodInvocation;</p><p>public class BookServiceInterceptor implements MethodInterceptor{<br /> public Object invoke(MethodInvocation invocation) throws Throwable {<br /> Object[] args = invocation.getArguments();//目標方法的參數<br /> System.out.println("the method " + invocation.getThis().getClass().getName() + "."<br /> + invocation.getMethod().getName() + "() will be executed.");<br /> Object obj = invocation.proceed();//執行目標方法<br /> System.out.println("the method " + invocation.getThis().getClass().getName() + "."<br /> + invocation.getMethod().getName() + "() has been executed.");<br /> return obj;<br /> }<br />}<br />
再次修改TestAdvice,把以前的兩個Advice注釋掉,單獨使用Interceptor看效果。
package aop;<br />import model.Book;</p><p>import org.aopalliance.intercept.MethodInterceptor;<br />import org.springframework.aop.AfterAdvice;<br />import org.springframework.aop.BeforeAdvice;<br />import org.springframework.aop.framework.ProxyFactory;</p><p>public class TestAdvice {<br /> public static void main(String[] args) {<br /> BookService target = new BookServiceImpl();<br /> BeforeAdvice beforeAdvice = new BookServiceBeforeAdvice();<br /> AfterAdvice afterAdvice = new BookServiceAfterRunningAdvice();<br /> MethodInterceptor interceptor = new BookServiceInterceptor();<br /> ProxyFactory pf = new ProxyFactory();<br /> pf.setTarget(target);<br /> //pf.addAdvice(beforeAdvice);<br /> //pf.addAdvice(afterAdvice);<br /> pf.addAdvice(interceptor);<br /> BookService service = (BookService)pf.getProxy();<br /> service.insertBook(new Book());<br /> service.removeBook(new Book());<br /> }<br />}<br />
列印出的訊息:
the method aop.BookService.insertBook() will be executed.
book inserted.
the method aop.BookService.insertBook() has been executed.
the method aop.BookService.removeBook() will be executed.
book removed.
the method aop.BookService.removeBook() has been executed.
還有異常拋出增強我就不給例子了,它繼承自ThrowsAdvice,在方法拋出異常的時候來增強方法。
在Bean config中配置和使用AOP   因為Spring有強大的IOC容器, 所以通過配置來體現AOP才是Spring推薦的。配置中要配置增強(Advice),目標Bean(Target),代理Bean;
<bean id="beforeAdvice" class="aop.BookServiceBeforeAdvice" /><br /><bean id="afterAdvice" class="aop.BookServiceAfterRunningAdvice" /><br /><bean id="interceptor" class="aop.BookServiceInterceptor" /><br /><bean id="targetService" class="aop.BookServiceImpl" /><br /><bean id="bookService" class="org.springframework.aop.framework.ProxyFactoryBean"><br /><property name="proxyInterfaces"><br /><value>aop.BookService</value><br /></property><br /><property name="interceptorNames"><br /><list><br /><value>beforeAdvice</value><br /><value>afterAdvice</value><br /><value>interceptor</value><br /></list><br /></property><br /><property name="targetName"><br /><value>targetService</value><br /></property><br /></bean>

然後寫一個測試類別,看看能不能和編程式的一樣可以正常工作。

package test;<br />import model.Book;<br />import org.springframework.context.ApplicationContext;<br />import org.springframework.context.support.ClassPathXmlApplicationContext;</p><p>import aop.BookService;</p><p>public class TestSpring {<br />public static void main(String[] args) {<br />ApplicationContext context = new ClassPathXmlApplicationContext("bean-test.xml");<br />BookService service = (BookService)context.getBean("bookService");<br />service.insertBook(new Book());<br />}<br />}<br />

一切工作正常。

切面   在前面討論的增強中,他們作用於target的方法上,根據實現的介面的不同,可以作用於方法前,方法後,環繞,拋出異常之後,而且這些增強作用於該類的所有方法,也就是說增強不能定位到具體的方法,但有的時候我們只希望某些方法得到增強,比如我有一個BookService,他有insertBook,insertBookCategory,insertPublisher,getAll, findById, findByName等方法,但是我只希望以insert,update,delete開頭的方法開啟事務,那就需要用到切面。 切面和增強不同的是,他包含增強,還包括一個方法匹配器。切面有動態切面和靜態切面,我示範一下靜態Regex方法匹配切面的用法, 因為這個在後面Spring交易管理的時候你會發現他們有多麼相似。先配置一個Regex切面:
<bean id="regexpAdvisor" class="org.springframework.aop.support.RegexpMethodPointcutAdvisor"><br /> <property name="patterns"><br /> <list><br /> <value>insert*</value><br /> <value>remove*</value><br /> </list><br /> </property><br /> <property name="advice" ref="beforeAdvice" /><br /></bean> 通過這個配置應該看出,切面除了包含Advice,還有方法匹配的資訊。 修改一下先前配置的bookService的代理對象
<bean id="bookService" class="org.springframework.aop.framework.ProxyFactoryBean"><br /> <property name="proxyInterfaces"><br /> <value>aop.BookService</value><br /> </property><br /> <property name="interceptorNames"><br /> <list><br /> <!--value>beforeAdvice</value><br /> <value>afterAdvice</value><br /> <value>interceptor</value--><br /> <!--value>throwsAdvice</value--><br /> <value>regexpAdvisor</value><br /> </list><br /> </property><br /> <property name="target" ref="targetService" /><br /></bean> 測試:
public class Test {<br /> public static void main(String[] args) throws InterruptedException, SQLException{<br /> ApplicationContext c = new ClassPathXmlApplicationContext("spring-test.xml");<br /> BookService s = (BookService)c.getBean("bookService");<br /> s.insertBook(null);<br /> s.removeBook(null);<br />}} 關於AOP還有引介(Introduction),動態切面, 複合切面,基於AspectJ的AOP,因為項目中我沒有使用到過,所以不打算介紹。Aop用的最多的地方也就是交易管理中為需要開啟事務的service配置代理對象。
代理底層使用了cglib技術,或Jdk內建的代理,但是jdk內建的代理只能對介面代理,如果有興趣,可以用jdk的代理寫幾個例子看看。有一點要說的是,凡是用到代理的地方,效能都會大打折扣。

 

下一講將學習spring對dao的支援。

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.