Hello,你們好,今天來給你們講一講Spring中的AOP,面向切面編程,它在Spring的整個體系中佔有着重要地位。本文仍是以實踐爲主,註解切入注入,OK,文章結構:java
提到AspectJ,其實不少人是有誤解的,不少人只知道在Spring中使用Aspect那一套註解,覺得是Spring開發的這一套註解,這裏我以爲有責任和你們澄清一下。 AspectJ是一個AOP框架,它可以對java代碼進行AOP編譯(通常在編譯期進行),讓java代碼具備AspectJ的AOP功能(固然須要特殊的編譯器),能夠這樣說AspectJ是目前實現AOP框架中最成熟,功能最豐富的語言,更幸運的是,AspectJ與java程序徹底兼容,幾乎是無縫關聯,所以對於有java編程基礎的工程師,上手和使用都很是容易. 其實AspectJ單獨就是一門語言,它須要專門的編譯器(ajc編譯器). Spring AOP 與ApectJ的目的一致,都是爲了統一處理橫切業務,但與AspectJ不一樣的是,Spring AOP並不嘗試提供完整的AOP功能(即便它徹底能夠實現),Spring AOP 更注重的是與Spring IOC容器的結合,並結合該優點來解決橫切業務的問題,所以在AOP的功能完善方面,相對來講AspectJ具備更大的優點,同時,Spring注意到AspectJ在AOP的實現方式上依賴於特殊編譯器(ajc編譯器),所以Spring很機智迴避了這點,轉向採用動態代理技術的實現原理來構建Spring AOP的內部機制(動態織入),這是與AspectJ(靜態織入)最根本的區別。在AspectJ 1.5後,引入@Aspect形式的註解風格的開發,Spring也很是快地跟進了這種方式,所以Spring 2.0後便使用了與AspectJ同樣的註解。請注意,Spring 只是使用了與 AspectJ 5 同樣的註解,但仍然沒有使用 AspectJ 的編譯器,底層依是動態代理技術的實現,所以並不依賴於 AspectJ 的編譯器。 因此,你們要明白,Spring AOP雖然是使用了那一套註解,其實實現AOP的底層是使用了動態代理(JDK或者CGLib)來動態植入。至於AspectJ的靜態植入,不是本文重點,因此只提一提。程序員
談到Spring AOP,老程序員應該比較清楚,以前的Spring AOP沒有使用@Aspect這一套註解和aop:config這一套XML解決方案,而是開發者本身定義一些類實現一些接口,並且配置賊噁心。本文就再也不演示了,後來Spring痛下決心,把AspectJ"整合"進了Spring當中,並開啓了aop命名空間。如今的Sping AOP能夠算是朗朗乾坤。上例子以前,仍是把AOP的概念都提一提:spring
網上有不少概念,什麼鏈接點,織入,目標對象,引入什麼的。我我的以爲,在Java的Spring AOP領域,徹底不用管。別把本身繞暈了。就按照我說的這三個概念就完事了,好了,先來搞個例子: maven依賴:編程
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>4.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>4.2.3.RELEASE</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aop</artifactId>
<version>4.2.3.RELEASE</version>
</dependency>
//下面這兩個aspectj的依賴是爲了引入AspectJ的註解
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>1.6.12</version>
</dependency>
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjweaver</artifactId>
<version>1.6.12</version>
</dependency>
//Spring AOP底層會使用CGLib來作動態代理
<dependency>
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
<version>2.2</version>
</dependency>
複製代碼
小狗類,會說話:bash
public class Dog {
private String name;
public void say(){
System.out.println(name + "在汪汪叫!...");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
複製代碼
切面類:app
@Aspect //聲明本身是一個切面類
public class MyAspect {
/**
* 前置通知
*/
//@Before是加強中的方位
// @Before括號中的就是切入點了
//before()就是傳說的加強(建言):說白了,就是要幹啥事.
@Before("execution(* com.zdy..*(..))")
public void before(){
System.out.println("前置通知....");
}
}
複製代碼
這個類是重點,先用@Aspect聲明本身是切面類,而後before()爲加強,@Before(方位)+切入點能夠具體定位到具體某個類的某個方法的方位. Spring配置文件:框架
//開啓AspectJ功能.
<aop:aspectj-autoproxy />
<bean id="dog" class="com.zdy.Dog" />
<!-- 定義aspect類 -->
<bean name="myAspect" class="com.zdy.MyAspect"/>
複製代碼
而後Main方法:maven
ApplicationContext ac =new ClassPathXmlApplicationContext("applicationContext.xml");
Dog dog =(Dog) ac.getBean("dog");
System.out.println(dog.getClass());
dog.say();
複製代碼
輸出結果:函數
class com.zdy.Dog$$EnhancerBySpringCGLIB$$80a9ee5f
前置通知....
null在汪汪叫!...
複製代碼
說白了,就是把切面類丟到容器,開啓一個AdpectJ的功能,Spring AOP就會根據切面類中的(@Before+切入點)定位好具體的類的某個方法(我這裏定義的是com.zdy包下的全部類的全部方法),而後把加強before()切入進去.ui
而後說下Spring AOP支持的幾種相似於@Before的AspectJ註解:
@Before("execution(...)")
public void before(JoinPoint joinPoint){
System.out.println("...");
}
複製代碼
@AfterReturning(value="execution(...)",returning = "returnVal")
public void AfterReturning(JoinPoint joinPoint,Object returnVal){
System.out.println("我是後置通知...returnVal+"+returnVal);
}
複製代碼
@AfterThrowing(value="execution(....)",throwing = "e")
public void afterThrowable(Throwable e){
System.out.println("出現異常:msg="+e.getMessage());
}
複製代碼
@After("execution(...)")
public void after(JoinPoint joinPoint) {
System.out.println("最終通知....");
}
複製代碼
@Around("execution(...)")
public Object around(ProceedingJoinPoint joinPoint) throws Throwable {
System.out.println("我是環繞通知前....");
//執行目標函數
Object obj= (Object) joinPoint.proceed();
System.out.println("我是環繞通知後....");
return obj;
}
複製代碼
而後說下一直用"..."忽略掉的切入點表達式,這個表達式能夠不是exection(..),還有其餘的一些,我就不說了,說最經常使用的execution:
//scope :方法做用域,如public,private,protect
//returnt-type:方法返回值類型
//fully-qualified-class-name:方法所在類的徹底限定名稱
//parameters 方法參數
execution(<scope> <return-type> <fully-qualified-class-name>.*(parameters)) 複製代碼
<fully-qualified-class-name>.*(parameters)
複製代碼
注意這一塊,若是沒有精確到class-name,而是到包名就中止了,要用兩個".."來表示包下的任意類:
具體詳細語法,你們若是有需求自行google了,我最經常使用的就是這倆了。要麼按照包來定位,要麼按照具體類來定位.
在使用切入點時,還能夠抽出來一個@Pointcut來供使用:
/** * 使用Pointcut定義切點 */
@Pointcut("execution(...)")
private void myPointcut(){}
/** * 應用切入點函數 */
@After(value="myPointcut()")
public void afterDemo(){
System.out.println("最終通知....");
}
複製代碼
能夠避免重複的execution在不一樣的註解裏寫不少遍...
好了,Spring AOP 基於AspectJ註解如何實現AOP給你們分享完了,其實不是很難,由於博主比較懶,主說了主要內容,像切入點表達式要說其實有不少東西能夠說,還有加強級別的排序問題,好比一個Aspect類中定義了多個@Before,誰先調用,由於博主以爲這些知識點不是很重要,用的很是少,因此就沒提了。這一期把基於註解的分享完了,下一次準備把Spring AOP基於XML的配置講一講,而後說一些實際的運用場景。包括多個切面命中一個切點時,切面級別的調用順序等都會提一提。而後把Spring AOP底層使用的JDK動態代理和CGLib動態代理都稍微說一說。好了,這一期就到這裏了..Over,Have a good day .