開發過程中很多時候會用到日志、事務等操作,這些操作如果要寫在業務代碼中會相當麻煩,這時就會用到面向切面編程(AOP),AOP作為一種編程思想,和OOP有著不同的側重點,面向對象側重於萬事萬物皆對象,而面向切面編程則側重於事務的一個方面。在面向切面編程過程中有幾個比較重要的概念:切面、切點、連接點、通知,
通知:描述了切面要完成的工作,例如,要向某個方法注入日志功能,這裡的日志功能就是通知;通知分為5種:Before、After、After-returning、After-throwing、Around
切點:定義了通知被應用的地方,如,某個類上的某個方法;例如,在test類的print方法;
切面:橫切關注點被模塊化的類,由通知和切點組成,換句話來說切面定義了要向目標程序注入的全部內容;規定了在目標方法上執行什麼樣的動作;
連接點:程序執行過程中切面可以被插入的一個點,如方法調用、成員變量初始化,在spring中只支持方法調用;
面向切面編程有兩種實現方式,一種是預編譯,另一種是動態代理,AspectJ屬於預編譯,springAOP屬於運行期動態代理方式。spring實現了對AspectJ的支持。下面看springAOP和AspectJ的使用方式,
springAOP
這裡使用配置文件的方式,實現springAOP,看配置文件,
<aop:config> <!--聲明一個切面類 可以有多個--> <aop:aspect id="myAspect" ref="aspect"> <!--聲明一個切點--> <aop:pointcut id="point1" expression="execution(* com.cn.study.day6.Test.*(..))"/> <!--通知--> <aop:before method="before" pointcut-ref="point1" /> <aop:around method="around" pointcut-ref="point1"/> </aop:aspect> </aop:config> <!--配置切面類--> <bean id="aspect" class="com.cn.study.day6.Aspect"></bean>
要配置AOP,在spring中使用了<aop:config>標簽,在這個標簽中可以配置多個切面標簽(<aop:aspect>),每個切面代表了一種橫切關注點,從前面的闡述中可以知道一個切面包含兩部分,一個是切點,另一個是通知,在<aop:aspect>標簽中定義切點(<aop:pointcut>)和通知(aop:before 前置通知),在切點中配置了通知被應用的位置,即匹配Test類的任何方法(execution(* com.cn.study.day6.Test.*(..))),在通知標簽中配置了執行目標方法前通知,切點是point1;下面看具體的切面類,
package com.cn.study.day6; import org.aspectj.lang.ProceedingJoinPoint; //切面類
public class Aspect { //前置通知調用的方法 public void before(){ System.out.println("我是前置通知!"); } public Object around(ProceedingJoinPoint jp){ System.out.println("我是環繞通知!"); Object object=null; try { System.out.println("方法執行前"); object = jp.proceed(); System.out.println("方法執行之後"); } catch (Throwable e) { // TODO Auto-generated catch block e.printStackTrace(); } return object; } }
切面類中的方法要和通知中method屬性的方法名字保持一致,下面是目標方法,
package com.cn.study.day6; import org.springframework.cache.annotation.Cacheable; import org.springframework.stereotype.Component; @Component public class Test { public void method1(){ System.out.println("業務方法!"); } }
上面是一個簡單的業務方法,
總結下這個切面類的含義,即在執行com.cn.study.day6.Test類中的任何方法之前都會首先執行com.cn.study.day6.Aspect切面類中的before方法,看打印結果,
我是前置通知! 業務方法!
結果顯示首先執行了切面類中的before方法,然後執行的是目標類中的method1方法。
上面就完成了一個面向切面編程的例子,我們實現的是在方法被調用之前實現橫切,那麼面向切面編程到底有什麼用處呢?我們在最後進行說明,接著看AspectJ
AspectJ
AspectJ是AOP的另一種實現,要使用AspectJ必須打開自動代理,如下
<aop:aspectj-autoproxy></aop:aspectj-autoproxy>
這是對AspectJ的自動代理,然後就可以使用AspectJ了,看下面的切面類
package com.cn.study.day7; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; import org.aspectj.lang.annotation.Pointcut; import org.springframework.stereotype.Component; @Component @Aspect public class AspectTest { //前置通知調用的方法 通知 @Before("pointCut1()") public void before(){ System.out.println("我是前置通知!"); } //切點 @Pointcut("execution(* com.cn.study.day7.Test.*(..))") public void pointCut1(){ } }
由於加了@AspectJ的類無法被自動識別,所以需要加載@Component注解,有了這兩個注解之後,此類便是一個切面類了,在切面類裡可以聲明切點和通知,我們定義了一個pointCut1的切點,此方法的返回值必須是void,此切點對com.cn.study.day7.Test下的所有方法有效,接著使用@Before定義了一個前置通知,且使用的切點為pointCut1(),打印結果如下,
我是前置通知! 業務方法!
上面便完成了使用AspectJ的切面編程;另,在定義通知的之後也可以直接使用表達式(execution(* com.cn.study.day7.Test.*(..)))而不必引用某個切點,引用某個切點的好處是,在定義了切點之後可以復用。
綜上,是兩種實現AOP的配置,AOP的使用場景是在不破壞原有代碼的基礎上,增加新的功能,比如日志、事務控制,使用AOP可以很好地減少代碼的侵入,在原有代碼不變的基礎上輕松實現日志、事務控制、權限控制等。這兩種方式在實際開發過程當中使用的都很廣泛,具體使用哪種方式可根據自己的情況而定。
有不足之處,歡迎指出!