# 精通Spring+4.x++企業開發與實踐之基於@AspectJ和Schema的AOPjava
1.保證是java5以上的版本(須要使用註解,而java5及以上才使用註解)spring
2.須要將Spring的asm(輕量級的字節碼處理框架)的模塊添加到類路徑中,由於java的反射沒法獲取入參的名字,因此Spring就是要asm處理@AspectJ中描述的方法入參名。app
3.Spring使用AspectJ提供的@AspectJ註解類庫及相應的解析類庫,須要在pom.xml文件添加aspectjweaver和aspectj的工具類aspectjrt框架
例子:函數
PreGreetingAspect.java工具
@Aspect//使用該註解定義一個切面 public class PreGreetingAspect { /** * 這段代碼包含了橫切的邏輯 * @Before 加強的類型 * "execution(* greetTo(..))"目標切點的表達式 */ @Before("execution(* greetTo(..))") public void beforeGreeting(){ System.out.println("How are you"); } }
測試代碼:測試
//建立被代理的對象 Waiter waiter = new PoliteWaiter(); //AspectJ代理工廠 AspectJProxyFactory factory = new AspectJProxyFactory(); //提娜佳目標類 factory.setTarget(waiter); //添加切面類 factory.addAspect(PreGreetingAspect.class); //生成切入的代理對象 Waiter proxy = factory.getProxy(); proxy.greetTo("張三");
執行結果: How are youflex
greet to 張三....this
使用schema的配置方式進行配置spa
<?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:aop="http://www.springframework.org/schema/aop" xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-4.0.xsd http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-4.0.xsd http://www.springframework.org/schema/aop http://www.springframework.org/schema/aop/spring-aop.xsd"> <!--基於asjectj的切面驅動器--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> <bean id="waiter" class="com.flexible.aspectj.PoliteWaiter"></bean> <bean class="com.flexible.aspectj.PreGreetingAspect"></bean> </beans>
測試代碼:
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:beans.xml"); Waiter waiter = (Waiter) context.getBean("waiter"); waiter.greetTo("zhangsan");
@AspectJ使用Java5.0註解和正規的AspectJ的切點表達式語言描述切面,因爲Spring只支持方法的連接,因此Spring僅支持部分AspectJ的切點語言。
組成:1.關鍵字和操做參數execution(* greetTo(..)),execution是關鍵字,"* greetTo(..)"位操做參數。execution表明目標類執行某一方法,"* greetTo(..)"描述目標方法的匹配模式串,兩者聯合起來表示目標類的greetTo()方法的鏈接點。execution()稱爲函數,"* greet(..)"稱爲函數入參。
Spring支持9個@AdpectJ切點表達式函數,它用不一樣的方式藐視目標類的鏈接點。根據描述的不一樣能夠分爲4種: 1.方法切點函數:經過描述目標類方法的信息定義鏈接點。 2.方法入參切點函數:經過描述目標類的方法入參的信息定義鏈接點。 3.目標類切點函數:經過描述目標類類型的信心定義鏈接點。 4.代理切點函數:經過描述目標類的代理類的信息定義鏈接點。
*:匹配任意字符,但它只能匹配上下文中的一個元素。
..:匹配任意字符,能夠匹配上下文中的多個元素,但在表示類時,必須和*聯合使用,而在表示入參時則單獨使用。
+:表示按類型匹配指定的類的全部類,必須跟在類名後面,如com.flexible.Car+表示繼承或者拓展了制定類的全部類,同時還包括指定類自己。
@AaspectJ函數按其是否支持通配符及支持的程度,能夠分爲三類:
1.支持通配符:execution()和within(),如 within(com.flexible.),within(com.flexible.service...*.Service).
2.僅支持"+"通配符:args(),this()和target(),如args(com.flexible.Waiter+),target(java.util.List+),可是其實這幾個函數是否是有這個通配符都時同樣的。
3.不支持通配符的:@args,@within(),@target()和@annotation().
1.@Before 前置加強,至關於BeforeAdvice。Before註解類用於兩個成員
2.@AfterReturning 後置加強,至關於AfterReturningAdvice。AfterReturning註解類擁有4個成員。
3.@Around 環繞加強,至關於MethodInterceptor。Around註釋類用於兩個成員。
4.@AfterThrowing 拋出加強,至關於ThrowsAdvice。AfterThrowing註解有4個成員。
value:指定切點
pointcut:表示切點的信息,若是顯示指定pointcut值,那麼它將覆蓋value值得設置值,能夠將pointcut成員看做value得同義詞。
throwing:將拋出得異常綁定到加強方法中。
argNames:如上所述。
5.@After Final加強,無論時拋出異常仍是正常退出,該加強都會執行,該加強沒有對應得加強接口,能夠把它當作ThrowsAdvice和AfterReturningAdvice得混合物,議案用於釋放資源,至關於try{}finally{}的控制流程。它有兩個成員;
value:該成員用於定義切點。
argNames:如上所述。
6.@DeclareParents 引介加強,至關於IntroductionInterceptor.DeclareParents註解類擁有兩個成員
value:定義切點,表示在那個木堡壘上添加引介加強。
defaultImpl:默認接口實現類。
經過引介加強將waiter也具備seller的功能
EnableSellerAspect.java
@Aspect public class EnableSellerAspect { //1.爲PoliteWaiter太你家接口實現 2.默認接口實現類 3.要是西安的目標接口。 @DeclareParents(value = "com.flexible.inroductionofdeclareparent.PoliteWaiter",defaultImpl = SmartSeller.class) public Seller seller; }
beans.xml
<!--基於asjectj的切面驅動器--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> <bean id="waiter_2" class="com.flexible.inroductionofdeclareparent.PoliteWaiter"></bean> <bean class="com.flexible.inroductionofdeclareparent.EnableSellerAspect"></bean>
測試代碼:
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:beans.xml"); Waiter waiter = (Waiter) context.getBean("waiter_2"); waiter.greetTo("zhangsan"); Seller seller = (Seller) waiter; seller.sell("apple");
執行結果:
直接將切點聲明在加強的方式是匿名加強,而若是但願在其餘地方重用一個切點,能夠經過@Pointcut註解及切面類方法對切點進行命名。
例子:
import org.aspectj.lang.annotation.Pointcut; public class NamedPointcut { //經過註解方法inpackage()對該切點進行命名,方法可視域修飾符爲private //代表該命名切點只能在本切面類中使用 @Pointcut("within(com.flexible.*)") private void inpackage(){} //經過註解方法greetTo()對該切點進行命名,方法能夠視域修飾符爲protected //代表該命名切點能夠在當前包中的切面類,自切脈你類只要。 @Pointcut("execution(* greetTo(..))") protected void greetTo(){} //引用命名切點定義的切點,本切點也是命名切點,它對應的可視域爲public @Pointcut("inpackage() && greetTo()") public void inPkgGreetTo(){} }
命名切點的使用類方法做爲切點的名稱,此方法的訪問修飾符還控制了切點的可引用性。命名切點定義好以後咱們就能夠在定義切面的時經過名稱引用切點。
例子:
import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Before; @Aspect public class NamedAspect { @Before("NamedPointcut.inPkgGreetTo()") public void pkgGreetTo(){ System.out.println("----------pkgGreetTo() executed!---"); } @Before("!target(com.flexible.pointcutbreakdown.annotation.PoliteWaiter) && NamedPointcut.inPkgGreetTo()") public void pkgGreetToNotNaivewaiter(){ System.out.println("----------pkgGreetToNotNaivewaiter() executed!---"); } }
一個鏈接點可u哦同時匹配多個其欸但,切點對應的加強在鏈接點上的植入順序有三種狀況須要討論
1.若是加強在同一個切面類中聲明,則依照加強在切面類中定義的順序織入
2.若是加強位於不一樣的切面類中,且這些切面類都實現了org.springframework.core.Ordered接口,則由接口方法的順序好決定(順序號小的先織入)
3.若是加強位於不一樣的切面類中,且這些切面類沒有實現org.springframework.core.Ordered接口,則織入順序是不肯定的。
例子: 若是有切面A和切面B,並且這兩個切面都實現了org.springframework.core.Ordered接口,A的順序是1,而得順序是2,並且A定義了三個切點,B定義兩個切點。那麼訪問順序以下圖所示:
AspectJ使用org.aspectj.lang.JoinPoint接口表示目標類鏈接點對象。若是是環繞加強,則使用org.aspectj.lang.ProceedingJoinPoint表示連繫欸但對象,該類是JoinPoint得子接口。任何加強方法均可以經過將第一個入參聲明爲JoinPoint訪問連繫欸但上下文信息。 1.JoinPoint
2.ProceedingJoinPoint
ProceedingJoinPoint繼承於JoinPoint子接口,它新增了兩個用於執行鏈接點得方法。
例子:
TestAspect.java
@Aspect public class TestAspect { @Around("execution(* com.flexible.obtainproceedingpointinfo..*(..))") public void joinPointAccess(ProceedingJoinPoint proceedingJoinPoint) throws Throwable { System.out.println("------joinPointAccess----"); System.out.println("args[0]:" + proceedingJoinPoint.getArgs()[0]); System.out.println("signature:" + proceedingJoinPoint.getTarget().getClass()); proceedingJoinPoint.proceed(); System.out.println("------joinPointAccess----"); } }
beans.xml
<!--基於asjectj的切面驅動器--> <aop:aspectj-autoproxy></aop:aspectj-autoproxy> <!--獲取鏈接點的信息--> <bean id="waiter_4" class="com.flexible.obtainproceedingpointinfo.PoliteWaiter"></bean> <bean class="com.flexible.obtainproceedingpointinfo.TestAspect"></bean>
測試代碼:
ApplicationContext context = new ClassPathXmlApplicationContext("classpath:beans.xml"); Waiter waiter = (Waiter) context.getBean("waiter_4"); waiter.greetTo("zhangsan");
執行結果: