上兩節提到Spring的裝配bean還有高級裝配,這一節就是Spring的另一個核心內容-AOP
AOP的基本概念
AOP稱為面向切面編程,在程序開發中主要用來解決一些系統層面上的問題,比如日志,事務,權限等通用的輔助模塊,在系統的很多模塊都要添加。
日志,事務,權限等就是切面,使用這種面向切面編程時,我們仍然可以在一個地方聲明通用功能(切面),然后聲明這個功能在何時通過何種方式使用,而無需修改應用這個切面的模塊。這樣做的好處是我們的關注點可以集中在模塊,而不是分散的通用功能,其次,模塊的代碼因此會變得整潔。
AOP的組成
(1)Aspect(切面):通常是一個類,里面可以定義切入點和通知
(2)JointPoint(連接點):程序執行過程中明確的點,一般是方法的調用
(3)Advice(通知):AOP在特定的切入點上執行的增強處理,有before,after,afterReturning,afterThrowing,around
(4)Pointcut(切入點):就是帶有通知的連接點,在程序中主要體現為書寫切入點表達式
(5)AOP代理:AOP框架創建的對象,代理就是目標對象的加強。Spring中的AOP代理可以使JDK動態代理,也可以是CGLIB代理,前者基于接口,后者基于子類
例如:
@Aspect
@Component
public class Audience { @Pointcut(
"execution(* com.hsb.beans.Perform.show(..))")
public void performance() { }
@Before(
"performance()")
public void beforeShow() { System.out.println(
"message from beforeShow"); }
Audience就是一個切面
@Pointcut("execution(* com.hsb.beans.Perform.show(..))") 是切點
beforeShow()是連接點
@Before(“performance()”) 就是通知
下面是兩種常用的aop方法:
1.在XML中聲明切面
package com.hsb.beans;
import org.springframework.stereotype.Repository;
@Repository public class Perform { public void show(){ System.out.println(
"message from Perform.show()"); } }
package com.hsb.aop; import org.aspectj.lang.ProceedingJoinPoint;
public class Audience {
public void beforeShow(){ System.
out.println(
"message from beforeShow"); }
public void afterShow(){ System.
out.println(
"message from afterShow"); }
public void around(ProceedingJoinPoint joinpoint){ System.
out.println(
"message from Start around");
long start = System.currentTimeMillis();
try { joinpoint.proceed(); }
catch (Throwable e) { e.printStackTrace(); }
long end = System.currentTimeMillis(); System.
out.println(
"message from End around total : "+(end-start)+
" ms"); } }
代碼中的joinpoint.proceed(),這句話就是實際調用切點方法,也就是本例中的show()。如果切面類中定義了around通知,通知一定要加上這句話,否則要切點方法不會被調用!
- c.配置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:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd" default-lazy-init="true"> <context:component-scan base-package="com.hsb.beans" /> <bean id="audience" class="com.hsb.aop.Audience" /> <aop:config> <aop:aspect ref="audience"> <aop:pointcut expression="execution(* com.hsb.beans.Perform.show(..))" id="performance" /> <aop:before method="beforeShow" pointcut-ref="performance" /> <aop:after method="afterShow" pointcut-ref="performance" /> <aop:around method="around" pointcut-ref="performance" /> </aop:aspect> </aop:config> </beans>
首先將audience聲明為一個bean,
<aop:aspect ref="audience">定義切面。
<aop:pointcut>定義切點。
<aop:before>等定義通知。
這樣就實現了在xml中實現aop,原理是,一個代理類封裝了需要切面的這個模塊,并攔截了被通知方法的調用,再把調用方法轉發給真正的bean。
當攔截到方法調用時,先執行切面邏輯,再調用目標方法。
2.注解切面
package com.hsb.beans;
import org.springframework.stereotype.Repository;
@Repository public class Perform { public void show(){ System.out.println(
"message from Perform.show()"); } }
package
com.hsb.aopimport org
.aspectj.lang.ProceedingJoinPointimport org
.aspectj.lang.annotation.Afterimport org
.aspectj.lang.annotation.Aroundimport org
.aspectj.lang.annotation.Aspectimport org
.aspectj.lang.annotation.Beforeimport org
.aspectj.lang.annotation.Pointcutimport org
.springframework.stereotype.Component@Aspect @Component public class Audience { @Pointcut(
"execution(* com.hsb.beans.Perform.show(..))") public void performance() { } @Before(
"performance()") public void beforeShow() { System
.out.println(
"message from beforeShow")} @After(
"performance()") public void afterShow() { System
.out.println(
"message from afterShow")} @Around(
"performance()") public void around(ProceedingJoinPoint joinpoint) { System
.out.println(
"message from Start around")long start = System
.currentTimeMillis()try { joinpoint
.proceed()} catch (Throwable e) { e
.printStackTrace()} long end = System
.currentTimeMillis()System
.out.println(
"message from End around total : " + (end - start) +
" ms")} }
@Aspect將此類聲明為切面類,@Component將此類聲明為bean放到spring容器中,@Pointcut將下面的performance聲明為一個切點,并將Perform中的show()方法進行了關聯。execution(* com.hsb.beans.Perform.show(..))的意思是,執行此方法時,忽略返回值,參數類型和個數忽略。還可以更加簡寫,用于匹配合適的方法,譬如對Perform類的全部方法進行匹配就是com.hsb.beans.Perform.*。
<?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:aop="http://www.springframework.org/schema/aop" xmlns:tx="http://www.springframework.org/schema/tx" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop-3.0.xsd http://www.springframework.org/schema/tx http://www.springframework.org/schema/tx/spring-tx-3.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd" default-lazy-init="true"> <context:component-scan base-package="com.hsb.*" /> <aop:aspectj-autoproxy proxy-target-class="true"/>
</beans>
可以看見此處就沒有使用<aop:config />聲明,而是使用<aop:aspectj-autoproxy proxy-target-class="true" />這句話的意思是自動在spring上下文中創建一個AnnotationAwareAspectJAutoProxyCreator類,它會自動代理一些bean,這些bean的方法需要與使用@Aspect注解的bean中所定義的切點相匹配,而這些切點又是使用@Pointcut注解定義出來的。proxy-target-class=”true”的意思是使用基于類的代理使用cglib庫,如果為false則使用jdk自帶的基于接口的代理
如上所示,完全是一樣的效果??梢钥闯鍪褂米⒔鉁p少了很多工作,不容易出錯,各個組件間的耦合度降低了。試想,如果一個大型項目中有很多切面,切點,如果全部去xml中配置,將會是一項極為艱苦的工作,但是如果使用注解就可以做很少的工作完成這一切。
總結
以上是生活随笔為你收集整理的Spring之面向切面编程AOP(三)的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。