javascript
Spring中AOP的使用
問題:什么是AOP?
答:AOP基本概念:Aspect-Oriented Programming,面向方面編程的簡稱,Aspect是一種新的模塊化機制,用來描述分散在對象、類或方法中的橫切關注點(crosscutting concern), 從關注點中分離出橫切關注點是面向方面程序設計的核心所在。分離關注點使得解決特定領域問題的代碼從業務邏輯中獨立出來,業務邏輯的代碼中不再含有針對特 定領域問題代碼的調用,業務邏輯同特定領域問題的關系通過方面來封裝、維護,這樣原本分散在整個應用程序中的變動就可以很好地管理起來。
個人理解:所謂的AOP就是把我們的程序的執行看成是一個方塊的面包,然后切成了一片一片的吐司--這些吐司就是我們一個一個的方法。然后在這些吐司片的前面,后面、甚至是里面來做一些特定條件下發生的特定事情,比如嵌入幾個葡萄干、給前面加蘋果醬、給后面加草莓醬這樣的事情。。優勢在于你可以在自己的AOP方法中規定一個加蘋果醬的理由,滿足這個理由的話就可以加蘋果醬,而不用在每個方法中都特定的指出加蘋果醬,加草莓醬什么的··個人理解···。
 
AOP的實現有很多的方式,我這邊使用Spring中自帶的aop來做一些業務邏輯的實現。
在Spring中實現aop的方式有兩種,分別是通過xml配置和通過注解配置。下面來介紹這兩種方式。
在介紹他們之前先看一下項目的結構:
 
上圖中ServiceAspect是我們使用注解的方式來實現AOP的類,InteceptorXML是使用XML來實現AOP的類;TestAop是測試方法;TestServiceImpl;TestService是用來被AOP的動作;(需要明確的是,AOP的動作建議都發生在service層面。)application-context.xml是我的配置文件。
1.通過注解配置。
我注解AOP的代碼如下:
package test.aop;import org.aspectj.lang.JoinPoint; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.After; import org.aspectj.lang.annotation.AfterReturning; import org.aspectj.lang.annotation.AfterThrowing; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component;import test.entity.User;@Component // 聲明這是一個組件 @Aspect // 聲明這是一個切面bean public class ServiceAspect {// 配置切入點,該方法無方法體,主要是為了同類中其他方法使用此處配置的切入點 ----見(1)@Pointcut("execution(* test.service..*.*(..))")public void aspect() {}// 配置前置通知,使用在方法aspect()上注冊的切入點,同時接受JoinPoint切入點對象,可以沒有該參數 ----見(2)@Before("aspect()&&args(id)")public void before(JoinPoint joinPoint, long id) {System.out.println(id + "------------------");System.out.println("before-->" + joinPoint);}// 配置后置通知,使用在方法aspect()上注冊的切入點----見(3)@After("aspect()&&args(id)")public void after(JoinPoint joinPoint, long id) {System.out.println(id + "after----------------------");System.out.println("after-->" + joinPoint);}// 配置環繞通知----見(4)@Around("aspect()")public Object around(JoinPoint joinPoint) {long start = System.currentTimeMillis();try {Object o=((ProceedingJoinPoint) joinPoint).proceed();User user = (User)o;System.out.println("-----around======="+user.toString());long end = System.currentTimeMillis();System.out.println("around -->" + joinPoint + "\tUse time : "+ (end - start) + " ms!");return user;} catch (Throwable e) {long end = System.currentTimeMillis();System.out.println("around __>" + joinPoint + "\tUse time : "+ (end - start) + " ms with exception : " + e.getMessage());return null;}}// 配置后置返回通知,使用在方法aspect()上注冊的切入點----見(5)@AfterReturning(pointcut = "aspect()", returning = "returnVal")public void afterReturning(JoinPoint joinPoint, Object returnVal) {System.out.println("afterReturning executed, return result is "+ returnVal);}// 配置拋出異常后通知,使用在方法aspect()上注冊的切入點----見(6)@AfterThrowing(pointcut = "aspect()", throwing = "ex")public void afterThrow(JoinPoint joinPoint, Exception ex) {System.out.println("afterThrow--> " + joinPoint + "\t"+ ex.getMessage());} }
 
很多內容在注釋中已經有了,這里說一下幾個關鍵的容易讓人不明白的地方;
(1)@Pointcut("execution(* test.service..*.*(..))")就是我們定義的切入點,在括號里面的參數是指定了哪些方法是在考慮切入的范圍內的;
* test.service..*.*(..)可以這樣來解剖:
 
第一個* :表示被攔截的方法可以是任意的返回類型;
test.service:指定了要攔截的包;
..:這兩個..表示的是被指定的攔截包test.service中所有的子包中的類的方法都要考慮到攔截的范圍中;
*:表示任意的類;
.*:表示任意的方法;
(..):表示任意的方法參數;
總結起來,就是告訴我們這樣一個信息:要攔截test.service中所有子包中的所有類的所有方法,這些方法的返回值可以是任意的,參數可以是任意的。
當然,我們也可以特定許多內容,但是格式不要變;例如:
@Pointcut(“execution(* test.service..*.add*(..))”)
 
那么所要攔截的方法就必須以add來開頭了。
切入點的方法中不實現任何的操作,作用只是提供給其他的切面來使用。
 
(2)
@Before("aspect()&&args(id)")中的 aspect(),指定指定切入點的方法,就是我們定義為pointCut的aspect()方法,然后args(id),我們獲取了所攔截的方法的傳入的參數中的id;在before方法中我們可以在切入點執行以前來做一些操作。 其中的joinPoint是切入點的相關信息。
 
 
(3)
@After("aspect()&&args(id)")同(2),只是這是在切入點執行完成以后來做出一些處理。
 
 
(4)
@Around("aspect()")是環繞通知,在環繞通知中我們能切入點的很多內容進行修改;
 
其中通過Object o=((ProceedingJoinPoint) joinPoint).proceed();我們就可以讓切入點的方法完成,獲得他的運行結果;
然后User user = (User)o;我們把它轉換為User對象,如果在return user,之前我們加上user.setName("after aa");就可以改變切入點的運行結果。
這種操作對于很多的錯誤檢測以及格式檢測是很有用處的。
 
(5)
@AfterReturning(pointcut = "aspect()", returning = "returnVal")
 
這里是切入點有返回結果后做的操作,通過returning的定義我們能獲得切入點的返回結果;
 
(6)
@AfterThrowing(pointcut = "aspect()", throwing = "ex")
 
這里可以在切入點拋出異常后做一些工作,通過定義throwing我們能獲得拋出的異常對象。
 
相關代碼:
application-context.xml
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"xmlns:context="http://www.springframework.org/schema/context"xmlns:util="http://www.springframework.org/schema/util" xmlns:jee="http://www.springframework.org/schema/jee"xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/mvc"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/utilhttp://www.springframework.org/schema/util/spring-util-3.0.xsdhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsdhttp://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc-3.0.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <context:component-scan base-package="test"> <!--開啟spring自定義的包掃描,我定義的為掃描test包下所有內容--> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/><!--這里不掃描controller,在mvc中掃描,安全又可靠--> </context:component-scan> <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy><!--開啟注釋方式的spring aop--> <bean id="userService" class="test.service.impl.UserService"></bean><!--注入userService--> </beans>UserService package test.service.impl;import org.apache.commons.logging.Log; import org.apache.commons.logging.LogFactory;import test.entity.User;public class UserService {private final static Log log = LogFactory.getLog(UserService.class);public User get(long id) {if (log.isInfoEnabled()) {log.info("getUser method . . .");}User user = new User(1, "test");return user;}public void save(User user) {if (log.isInfoEnabled()) {log.info("saveUser method . . .");}}public boolean delete(long id) throws Exception {if (log.isInfoEnabled()) {log.info("delete method . . .");throw new Exception("spring aop ThrowAdvice演示");}return false;} }
test方法
package demo;import org.springframework.context.ApplicationContext; import org.springframework.context.support.ClassPathXmlApplicationContext;import test.entity.User; import test.service.impl.UserService;public class TestAop {public static void main(String[] args) {ApplicationContext aContext = new ClassPathXmlApplicationContext("application-context.xml");//加載spring文件UserService userService = (UserService) aContext.getBean("userService");//獲得userserviceUser user =userService.get(1L);//調用get方法;System.out.println(user);try {userService.delete(1L);//調用delete方法;} catch (Exception e) {System.out.println("Delete user : " + e.getMessage());}} }測試結果:
 
 
 
2.通過XML來配置
通過xml來配置AOP,操作都在xml文件中完成
在xml中做如下配置
<?xml version="1.0" encoding="UTF-8"?> <beans xmlns="http://www.springframework.org/schema/beans"xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:p="http://www.springframework.org/schema/p"xmlns:context="http://www.springframework.org/schema/context"xmlns:util="http://www.springframework.org/schema/util" xmlns:jee="http://www.springframework.org/schema/jee"xmlns:tx="http://www.springframework.org/schema/tx" xmlns:mvc="http://www.springframework.org/schema/mvc"xmlns:aop="http://www.springframework.org/schema/aop"xsi:schemaLocation="http://www.springframework.org/schema/utilhttp://www.springframework.org/schema/util/spring-util-3.0.xsdhttp://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsdhttp://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsdhttp://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsdhttp://www.springframework.org/schema/jee http://www.springframework.org/schema/jee/spring-jee-3.0.xsdhttp://www.springframework.org/schema/mvchttp://www.springframework.org/schema/mvc/spring-mvc-3.0.xsdhttp://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd"> <!-- <context:component-scan base-package="test"> <context:exclude-filter type="annotation" expression="org.springframework.stereotype.Controller"/> </context:component-scan> <aop:aspectj-autoproxy proxy-target-class="true"></aop:aspectj-autoproxy> --> <bean id="userService" class="test.service.impl.UserService"></bean><bean id="inteceptorXML" class="test.aop.InteceptorXML"></bean> <aop:config> <aop:aspect id="aspectd" ref="inteceptorXML"> <aop:pointcut expression="execution(* test.service..*.*(..))" id="mypointCutMethod"/> <aop:before method="doAccessCheck" pointcut-ref="mypointCutMethod" /> </aop:aspect> </aop:config> </beans>
 
 
在xml中我們指定了用來作為攔截器的bean----inteceptorXML,類似的指定了切入點
<aop:aspect id="aspectd" ref="inteceptorXML">指定了攔截器為interceptorXML。
 execution(* test.service..*.*(..)),并指定了id為mypointCutMethod,然后定義了
 <aop:before method="doAccessCheck" pointcut-ref="mypointCutMethod" />
 指定了調用doAccessCheck來做before攔截,其他攔截我沒有指定,這里都可以指定的。
 
inteceptorXml
package test.aop;import org.aspectj.lang.ProceedingJoinPoint;public class InteceptorXML {public void doAccessCheck() {System.out.println("before advice");}public void doWriteLog() {System.out.println("after advice");}public void doWriteErrorLog() {System.out.println("Exception advice");}public Object doAroundMethod(ProceedingJoinPoint pjp) throws Throwable {System.out.println("enter around advice method.");Object obj = pjp.proceed();System.out.println("exit around advice method.");return obj;} }運行上面的測試方法,得到的結果如下:
 
 
 
總結
以上是生活随笔為你收集整理的Spring中AOP的使用的全部內容,希望文章能夠幫你解決所遇到的問題。
 
                            
                        - 上一篇: Trident State译文
- 下一篇: 读取串口 :javax.comm 2.0
