最近在翻《Spring In Action》Spring 實戰這本書,從新瞭解了一下AOP的概念和思想並寫了一個小Demo示例,記錄在這裏:java
環境:intelliJ IDEA 201八、MAVEN管理依賴spring
一:項目的POM文件內容:apache
<?xml version="1.0" encoding="UTF-8"?> <project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> <modelVersion>4.0.0</modelVersion> <groupId>com.cmhit.springinaction</groupId> <artifactId>Chapter3</artifactId> <version>1.0-SNAPSHOT</version> <dependencies> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.8.9</version> </dependency> <dependency> <groupId>org.aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.8.9</version> </dependency> <dependency> <groupId>log4j</groupId> <artifactId>log4j</artifactId> <version>1.2.17</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>4.3.5.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>4.3.5.RELEASE</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>4.3.5.RELEASE</version> </dependency> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.12</version> </dependency> </dependencies> </project>
二:建立包來容納用到的類文件:app
三:建立接口類Performance,該類僅有一個perform方法:maven
package concert; public interface Performance { public void perform(); }
四:建立接口的實現類Movieide
package concert; import org.springframework.stereotype.Component; @Component public class Movie implements Performance { public void perform(){ System.out.println("The movie is showing"); }; }
五:定義一個「觀衆」切面:測試
package concert; import org.aspectj.lang.annotation.*; @Aspect public class Audience { @Pointcut("execution( * concert.Performance.perform(..))") public void performance(){} @Before("performance()") public void silenceCellPhones(){ System.out.println("Silencing cell phones"); } @Before("performance()") public void takeSeats(){ System.out.println("Taking seats"); } @AfterReturning("performance()") public void applause(){ System.out.println("CLAP CLAP CLAP"); } @AfterThrowing("performance()") public void demandRefund(){ System.out.println("Demanding a refund"); } }
六:至此,基本的類都已經創建完畢了,下面建立配置類將它們裝配起來:spa
package concert; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.ComponentScan; import org.springframework.context.annotation.Configuration; import org.springframework.context.annotation.EnableAspectJAutoProxy; @Configuration @EnableAspectJAutoProxy @ComponentScan(basePackages = "concert") public class appConfig { @Bean public Performance movie() { return new Movie(); } @Bean public Audience audience(){ return new Audience(); } }
七:在test目錄下面創建相同的package,並建立測試類:3d
package concert; import org.junit.Test; import org.junit.runner.RunWith; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.test.context.ContextConfiguration; import org.springframework.test.context.junit4.SpringJUnit4ClassRunner; @RunWith(SpringJUnit4ClassRunner.class) @ContextConfiguration(classes = appConfig.class) public class TestConcert { @Autowired private Performance perform; @Test public void test01(){ perform.perform(); } }
八:測試運行結果:代理
九:根據上面的demo,再次加深理解AOP的幾個概念:通知、鏈接點、切點、切面、織入
通知:通知定義了切面要完成的工做以及在何時使用,上面的demo中,表演前的手機靜音,表演前觀衆入座,表演成功結束後的鼓掌,表演失敗後觀衆要求退票,這其中的每一項都是通知,通知能夠分爲前置通知(目標方法調用前執行),後置通知(方法完成後執行,且無論方法的輸出是什麼),返回通知(方法成功執行後調用通知),異常通知(方法執行發生異常時調用通知),環繞通知(通知包裹了被通知的方法,效果相似方法調用先後均調用,可是環繞通知能夠將全部的通知方法放在一塊兒);
鏈接點:鏈接點是應用執行過程當中能插入到切面的點,這個點能夠是方法被調用,拋出異常或者是屬性被修改,切面的代碼就是利用這些鏈接點將代碼插入到應用程序的正常流程中,上面demo中的perform方法就是一個鏈接點;
切點:應用程序中存在數以千計的鏈接點,咱們的切面程序代碼不必定要插入到全部的鏈接點中,那些切面織入的鏈接點集合就是切點。
切面:切面是通知和切點的集合,即切面描述了須要作什麼工做(手機靜音、就坐、鼓掌、退票),在何時工做(perform方法執行前仍是執行後,仍是發生異常時),在哪些地方執行工做。
織入:將切面在指定的鏈接點織入到目標對象中的過程就是織入,織入的基本原理是建立原對象的代理對象,全部在原對象上的方法調用均會被代理對象攔截(代理類封裝了目標類),在執行完切面邏輯代碼後,再將調用轉給真正的目標bean。
補充:可使用環繞通知重寫上面的audience類:
package concert; import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.*; @Aspect public class Audience { @Pointcut("execution( * concert.Performance.perform(..))") public void performance(){} @Around("performance()") public void watchPerformance(ProceedingJoinPoint jp){ try { System.out.println("Silencing cell phones"); System.out.println("Taking seats"); jp.proceed(); System.out.println("CLAP CLAP CLAP"); }catch (Throwable e){ System.out.println("Demanding a refund"); } } }
執行的效果和上面是同樣的,代碼中的ProceedingJoinPoint對象用來控制被通知方法的調用:jp.proceed(),若是不調用這個方法,能夠實現阻塞被通知方法(performance)的調用,並且能夠反覆調用jp.proceed(),至關於反覆調用perform()方法。