AspectJ實現AOP
上一篇文章Spring框架(4)---AOP講解鋪墊,講了一些基礎AOP理解性的東西,那么這篇文章真正開始講解AOP
通過AspectJ實現AOP要比普通的實現Aop要方便的多,所以第五篇文章有關SpringAOP我暫且不寫,后面整理好了再補充上;
那我們首先還是講一些有的沒的的東西:
什么是Spring的AspectJ的AOP
AspectJ是一個面向切面的框架,它擴展了Java語言。AspectJ定義了AOP語法所以它有一個專門的編譯器用來生成遵守Java字節編碼規范的Class文件。
Spring2.0以后新增了對AspectJ切點表達式支持,@AspectJ 是AspectJ1.5新增功能,通過JDK5注解技術,允許直接在Bean類中定義切面
新版本Spring框架,建議使用AspectJ方式來開發AOP
那我們先來一個小案例,還是以前面狗的案例舉例:
1.導入相關架包
在原來的基礎上導入下面四個架包:
- spring-aop-4.2.0.RELEASE.jar
- com.springsource.org.aopalliance-1.0.0.jar
- spring-aspects-4.2.0.RELEASE.jar
- com.springsource.org.aspectj.weaver-1.6.8.RELEASE.jar
2.編寫配置文件applicationContext.xml 導入aop的命名空間
<?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:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- bean definitions here --> <!-- 配置注解bean所在的包 --> <context:annotation-config/> <context:component-scan base-package="com.study.spring.d_advisor"></context:component-scan> <!-- 打開自動代理 --> <aop:aspectj-autoproxy/>
</beans>
3.編寫代理對象
1 public class Dog { 2 3 public void run() { 4 System.out.println("狗會跑"); 5 } 6 7 public void jump() { 8 9 System.out.println("狗會跳"); 10 11 } 12 13 public void eat() { 14 15 System.out.println("狗能吃"); 16 } 17 }
4)編寫切面類
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; //@Aspect聲明當前類是一個切面 @Aspect public class MyAspectJ { //@Before代表在執行方法之前執行增強代碼 @Before("execution(* com.study.dog.Dog.*(..))") public void before1() { System.out.println("飼養員叫你你猜能動"); } }
5編寫配置文件
<?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:aop="http://www.springframework.org/schema/aop" xmlns:context="http://www.springframework.org/schema/context" xsi:schemaLocation=" http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!-- bean definitions here --> <!-- 配置注解bean所在的包 --> <context:annotation-config/> <context:component-scan base-package="com.study.spring.d_advisor"></context:component-scan> <!-- 打開自動代理 --> <aop:aspectj-autoproxy/> <!-- 被代理對象 --> <bean id="Dog" class="com.study.dog.Dog"></bean> <!-- 切面 --> <bean id="MyAspectJ" class="com.study.dog.MyAspectJ"></bean> </beans>
6.編寫測試類
1 import org.junit.Test; 2 import org.junit.runner.RunWith; 3 import org.springframework.beans.factory.annotation.Autowired; 4 import org.springframework.beans.factory.annotation.Qualifier; 5 import org.springframework.test.context.ContextConfiguration; 6 import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; 7 8 //完成配置文件的加載 9 @RunWith(SpringJUnit4ClassRunner.class) 10 @ContextConfiguration(locations="classpath:applicationContext.xml") 11 public class AspectJTest { 12 // 得到dog對象 13 @Autowired 14 @Qualifier("Dog") 15 private Dog dog; 16 17 @Test 18 public void Demo1() { 19 dog.eat(); 20 dog.run(); 21 dog.jump(); 22 } 23 /* 24 * 輸出結果: 25 * 飼養員叫你你猜能動 26 * 狗能吃 27 * 飼養員叫你你猜能動 28 * 狗會跑 29 * 飼養員叫你你猜能動 30 * 狗會跳 31 */ 32 }
總結:上面是一個最簡單的AspectJ的AOP,我們這樣就可以通過輸入一個”飼養員叫你你猜能動",就可以在多個方法中存在,在以后開發非常需要
那么下來來說一些非常重要的干貨!
AspectJ表達式:
語法:execution(表達式) 通過execution函數,可以定義切點的方法切入
execution(<訪問修飾符>?<返回類型><方法名>(<參數>)<異常>)
例如
execution(public * *(..)) 匹配所有類public方法
execution(* cn.study.dao..*(..)) 匹配指定包下所有類方法,不包含子包
execution(* cn.study.dao..*(..)) ..*表示包、子孫包下所有類
execution(* cn.study.service.UserService.*(..)) 匹配指定類所有方法
execution(* cn.study.dao.GenericDAO+.*(..)) 匹配實現特定接口所有類方法
execution(* save*(..)) 匹配所有save開頭的方法
AspectJ增強
@Before 前置通知,相當於BeforeAdvice
@AfterReturning 后置通知,相當於AfterReturningAdvice
@Around 環繞通知,相當於MethodInterceptor
@AfterThrowing拋出通知,相當於ThrowAdvice
@After 最終final通知,不管是否異常,該通知都會執行
@DeclareParents 引介通知,相當於IntroductionInterceptor (不要求掌握)
那現在我們通過上面的列子,來全面貫徹上面的干貨!
對上面MyAspectJ的進行全面增強:
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; //@Aspect聲明當前類是一個切面 @Aspect public class MyAspectJ { //@Before代表在執行方法之前執行增強代碼 @Before("execution(* com.study.dog.Dog.run(..))") public void before(JoinPoint joinPoint){ System.out.println("前置增強...."+joinPoint); } //后置通知 @AfterReturning (value="execution(* com.study.dog.Dog.jump(..))",returning="returnVal") public void afterReturin(Object returnVal){ System.out.println("后置增強....方法的返回值:"+returnVal); } //環繞通知 @Around(value="MyAspectJ.myPointcut()") public Object around(ProceedingJoinPoint proceedingJoinPoint) throws Throwable{ System.out.println("環繞前增強...."); Object obj = proceedingJoinPoint.proceed(); System.out.println("環繞后增強...."); return obj; } //拋出通知 @AfterThrowing(value="MyAspectJ.myPointcut()",throwing="e") public void afterThrowing(Throwable e){ System.out.println("不好了 出異常了!!!"+e.getMessage()); } //最終final通知,不管是否異常,該通知都會執行 @After("MyAspectJ.myPointcut()") public void after(){ System.out.println("最終通知..."); } //切點的定義 @Pointcut("execution(* com.study.dog.Dog.eat(..))") private void myPointcut(){} }
編寫測試類AspectJTest 和上面一點不變
import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; //完成配置文件的加載 @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(locations="classpath:applicationContext.xml") public class AspectJTest { // 得到dog對象 @Autowired @Qualifier("Dog") private Dog dog; @Test public void Demo1() { dog.eat(); dog.run(); dog.jump(); } }
運行結果:
通過這個例子,相信大家對於AspectJ的AOP有了一定的了解了。
最后補充一個面試題:
Advisor和Aspect的區別?
Advisor:Spring傳統意義上的切面:支持一個切點和一個通知的組合.
Aspect:可以支持多個切點和多個通知的組合.
這篇文章就講到這里,有不足之處,歡迎大家指出,謝謝!