精通Spring+4.x++企業開發與實踐之基於@AspectJ和Schema的AOP

#  精通Spring+4.x++企業開發與實踐之基於@AspectJ和Schema的AOPjava

使用@AspectJ的條件

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語法基礎

@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.代理切點函數:經過描述目標類的代理類的信息定義鏈接點。

入參函數的通配符

  1. *:匹配任意字符,但它只能匹配上下文中的一個元素。

  2. ..:匹配任意字符,能夠匹配上下文中的多個元素,但在表示類時,必須和*聯合使用,而在表示入參時則單獨使用。

  3. +:表示按類型匹配指定的類的全部類,必須跟在類名後面,如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註解類用於兩個成員

  • value:該成員用於定義切點。
  • argsNames:因爲沒法經過Java反射機制獲取方法入參名,因此若是在Java編譯時未啓用調式信息,或者須要在運行期間解析切點,就必須經過這個成員指定直接所標註加強的方法的參數名(注意兩者名字必須徹底相同),多個參數名用逗號分開。

2.@AfterReturning 後置加強,至關於AfterReturningAdvice。AfterReturning註解類擁有4個成員。

  • value:該成員用於定義切點
  • pointcut:表示切點的信息。若是顯式地指定pointcut值,那麼它將覆蓋value的設置值,能夠將pointcut成員看做value同義詞。
  • returning:將目標對象方法的返回值綁定給加強的方法。
  • argNames:如上所述

3.@Around 環繞加強,至關於MethodInterceptor。Around註釋類用於兩個成員。

  • value:該成員用於定義切點
  • argNames:如上所述

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

  • java.lang.Object[] getArgs():獲取連繫欸但方法運行時得入參列表。
  • Signature getSignature():獲取鏈接點得方法簽名對象。
  • java.lang.Object getTarget():獲取鏈接點所在目標對象
  • java.lang.Object getThis():獲取代理對象自己。

2.ProceedingJoinPoint

ProceedingJoinPoint繼承於JoinPoint子接口,它新增了兩個用於執行鏈接點得方法。

  • java.lang.Object.proceed() throws java.lang.Throwable:經過反射執行目標對象得鏈接點處得方法。
  • java.lang.Object proceed(java.lang.Object[] args)throws java.lang.Throwable:經過反射執行目標對象鏈接點處得方法,不過使用新得入倉替換原來得入參。

例子:

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");

執行結果:

綁定鏈接點方法入參

相關文章
相關標籤/搜索