AOP是軟件開發思想階段性的產物,咱們比較熟悉面向過程OPP和麪向對象OOP,AOP是OOP的延續,但不是OOP的替代,而是做爲OOP的有益補充。html
參考《Spring 實戰 (第4版)》和《精通Spring4.x 企業應用開發實戰》兩本書的AOP章節和其餘資料將其知識點整理起來。java
部分代碼實例摘自《精通Spring4.x 企業應用開發實戰》,文末我會給出兩本書的PDF下載地址!git
AOP爲Aspect Oriented Programming的縮寫,意爲:面向切面編程。那麼什麼又是面向切面呢?github
我知道面向對象的特性:封裝、繼承、多態。經過抽象出代碼中共有特性來優化編程,可是這種方式又每每不能徹底適用任何場景,沒法避免的形成代碼之間的耦合。web
以下面的代碼,運用的OOP思想將共有操做抽象了出來,抽象出了兩個處理類:性能監視 PerformanceMonitor 和事務管理 TransactionManager spring
1 package com.smart.concept; 2 3 public class ForumService { 4 private TransactionManager transManager; 5 private PerformanceMonitor pmonitor; 6 private TopicDao topicDao; 7 private ForumDao forumDao; 8 9 public void removeTopic(int topicId) { 10 pmonitor.start(); 11 transManager.beginTransaction(); 12 topicDao.removeTopic(topicId); // ① 13 transManager.commit(); 14 pmonitor.end(); 15 } 16 17 public void createForum(Forum forum) { 18 pmonitor.start(); 19 transManager.beginTransaction(); 20 forumDao.create(forum); // ② 21 transManager.commit(); 22 pmonitor.end(); 23 } 24 }
①、②處是兩個方法 removeTopic 和 createForum 獨有的業務邏輯,但它們淹沒在了重複化非業務性代碼之中。這種抽象爲縱向抽取。數據庫
將removeTopic和createForum進行橫切從新審視:express
咱們沒法經過縱向抽取的方式來消除代碼的重複性。然而切面能幫助咱們模塊化橫切關注點,橫切關注點能夠被描述爲影響應用多處的功能。例如,性能監視和事務管理分別就是一個橫切關注點。apache
AOP提供了取代繼承和委託的另外一種可選方案,那就是橫向抽取,能夠在不少場景下更清晰簡潔。在使用面向切面編程時,咱們仍然在一個地方定義通用功能,可是能夠經過聲明的方式定義這個功能要以何種方式在何處應用,而無需修改受影響的類。橫切關注點能夠被模塊化爲特殊的類,這些類被稱爲切面(aspect)。這樣作有兩個好處:首先,如今每一個關注點都集中於一個地方,而不是分散到多處代碼中;其次,服務模塊更簡潔,由於它們只包含主要關注點(或核心功能)的代碼,而次要關注點的代碼被轉移到切面中了。編程
AOP不容易理解的一方面緣由就是概念太多,而且由英語直譯過來的名稱也不統一,這裏我選擇使用較多的譯名並給出英文供你們參考。
程序執行的某個時間點,如類初始化前/後,某個方法執行前/後,拋出異常前/後。
一個程序類能夠有多個鏈接點,可是若是某一部分鏈接點須要用什麼來定位呢?那就是切點,這麼說可能有點抽象。藉助數據庫查詢的概念來理解切點和鏈接點的關係再適合不過了:鏈接點至關於數據庫中的記錄,而切點至關於查詢條件。切點和鏈接點不是一對一的關係,一個切點能夠匹配多個鏈接點。
將一段執行邏輯添加到切點,並結合切點信息定位到具體的鏈接點,經過攔截來執行邏輯。
Spring切面能夠應用5種類型的通知:
加強邏輯的織入目標對象。目標對象也被稱爲 advised object
將加強添加到目標類的具體鏈接點上的過程。在目標對象的生命週期裏有多個點能夠進行織入:
向現有的類添加新的方法或屬性。
一個類被AOP織入加強後,就產生了一個結果類,它是融合了原類和加強邏輯的代理類。根據不一樣的代理方式,代理類既多是和原類具備相同接口的類,也可能就是原類的子類,因此能夠採用與調用原類相同的方式調用代理類。
切面由切點和加強/通知組成,它既包括了橫切邏輯的定義、也包括了鏈接點的定義。
Spring提供了4種類型的AOP支持:
Spring AOP的底層原理就是動態代理。
Java實現動態代理的方式有兩種:
JDK動態代理是須要實現某個接口了,而咱們類未必所有會有接口,因而CGLib代理就有了。
那麼JDK代理和CGLib代理咱們該用哪一個呢??在《精通Spring4.x 企業應用開發實戰》給出了建議:
緣由:
看到這裏咱們就應該知道什麼是Spring AOP(面向切面編程)了:將相同邏輯的重複代碼橫向抽取出來,使用動態代理技術將這些重複代碼織入到目標對象方法中,實現和原來同樣的功能。
1 package com.spring05; 2 3 interface ForumService { 4 public void removeTopic(int topicId); 5 public void removeForum(int forumId); 6 }
1 package com.spring05; 2 3 public class ForumServiceImpl implements ForumService{ 4 @Override 5 public void removeTopic(int topicId) { 6 7 // 開始對該方法進行性能監視 8 PerformanceMonitor.begin("com.spring05.ForumServiceImpl.removeTopic"); 9 System.out.println("模擬刪除Topic記錄:"+topicId); 10 try { 11 Thread.currentThread().sleep(20); 12 } catch (InterruptedException e) { 13 e.printStackTrace(); 14 } 15 // 結束對該方法的性能監視 16 PerformanceMonitor.end(); 17 } 18 19 @Override 20 public void removeForum(int forumId) { 21 22 // 開始對該方法進行性能監視 23 PerformanceMonitor.begin("com.spring05.ForumServiceImpl.removeForum"); 24 System.out.println("模擬刪除Forum記錄:"+forumId); 25 try { 26 Thread.currentThread().sleep(40); 27 } catch (InterruptedException e) { 28 e.printStackTrace(); 29 } 30 // 結束對該方法的性能監視 31 PerformanceMonitor.end(); 32 } 33 }
1 package com.spring05; 2 3 public class MethodPerformance { 4 private long begin; 5 private long end; 6 private String serviceMethod; 7 8 public MethodPerformance(String serviceMethod) { 9 this.serviceMethod = serviceMethod; 10 this.begin = System.currentTimeMillis(); // 記錄目標類方法開始執行點的系統時間 11 } 12 13 public void printPerformance() { 14 this.end = System.currentTimeMillis(); // 獲取目標類方式執行完成後的系統時間,進而計算出目標類方法的執行時間 15 long elapse = end - begin; 16 System.out.println(serviceMethod + "花費" + elapse + "毫秒"); 17 } 18 }
1 package com.spring05; 2 3 public class PerformanceMonitor { 4 // 經過一個ThreadLocal保存與調用線程相關的性能監視信息 5 private static ThreadLocal<MethodPerformance> performanceRecord = new ThreadLocal<MethodPerformance>(); 6 7 // 啓動對某一目標方法的性能監視 8 public static void begin(String method) { 9 System.out.println("begin monitor..."); 10 MethodPerformance mp = new MethodPerformance(method); 11 performanceRecord.set(mp); 12 } 13 14 public static void end() { 15 System.out.println("end monitor..."); 16 MethodPerformance mp = performanceRecord.get(); 17 18 // 打印出方法性能監視的結果信息 19 mp.printPerformance(); 20 } 21 }
1 package com.spring05; 2 3 public class TestForumService { 4 public static void main(String[] args) { 5 ForumService forumService = new ForumServiceImpl(); 6 forumService.removeForum(10); 7 forumService.removeTopic(1012); 8 } 9 }
執行 TestForumService.main 輸出結果:
begin monitor...
模擬刪除Forum記錄:10
end monitor...
com.spring05.ForumServiceImpl.removeForum花費42毫秒
begin monitor...
模擬刪除Topic記錄:1012
end monitor...
com.spring05.ForumServiceImpl.removeTopic花費28毫秒
在 ForumServiceImpl 中的方法中仍然有非業務邏輯的性能監視代碼(每一個業務方法都有性能監視的開啓和關閉代碼),這破壞了方法的純粹性。下面經過JDK動態代理和CGLib動態代理使非業務邏輯的性能監視代碼動態的織入目標方法,以優化代碼結構。
PS:上面代碼能夠拷貝出一份,下面舉例大多都是以上面代碼爲基礎改進的。
先註釋掉 ForumServiceImpl 中非業務邏輯的代碼:
package com.spring06; public class ForumServiceImpl implements ForumService{ @Override public void removeTopic(int topicId) { // 開始對該方法進行性能監視 // PerformanceMonitor.begin("com.smart.proxy.ForumServiceImpl.removeTopic"); System.out.println("模擬刪除Topic記錄:"+topicId); try { Thread.currentThread().sleep(20); } catch (InterruptedException e) { e.printStackTrace(); } // 結束對該方法的性能監視 // PerformanceMonitor.end(); } @Override public void removeForum(int forumId) { // 開始對該方法進行性能監視 // PerformanceMonitor.begin("com.smart.proxy.ForumServiceImpl.removeForum"); System.out.println("模擬刪除Forum記錄:"+forumId); try { Thread.currentThread().sleep(40); } catch (InterruptedException e) { e.printStackTrace(); } // 結束對該方法的性能監視 // PerformanceMonitor.end(); } }
建立橫切代碼處理類:
1 package com.spring06; 2 3 import java.lang.reflect.InvocationHandler; 4 import java.lang.reflect.Method; 5 6 /** 7 * 橫切代碼處理類,實現InvocationHandler接口 8 */ 9 public class PerformanceHandler implements InvocationHandler { 10 private Object target; 11 12 public PerformanceHandler(Object target) { 13 // 設置目標業務類 14 this.target = target; 15 } 16 17 @Override 18 public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { 19 // 開始性能監視 20 PerformanceMonitor.begin(target.getClass().getName() + "." + method.getName()); 21 // 經過反射機制調用目標對象方法 22 Object obj = method.invoke(target, args); 23 // 結束性能監視 24 PerformanceMonitor.end(); 25 return obj; 26 } 27 }
修改 TestForumService 調用流程:
1 package com.spring06; 2 3 import java.lang.reflect.Proxy; 4 5 public class TestForumService { 6 public static void main(String[] args) { 7 8 // 但願被代理的目標業務類 9 ForumService target = new ForumServiceImpl(); 10 11 // 將目標業務類和橫切代碼編織到一塊兒 12 PerformanceHandler handler = new PerformanceHandler(target); 13 14 // 根據編織了目標業務邏輯和性能監視橫切邏輯的InvocationHandler實例建立代理實例 15 ForumService proxy = (ForumService) Proxy.newProxyInstance( 16 target.getClass().getClassLoader(), 17 target.getClass().getInterfaces(), 18 handler); 19 20 // 調用代理實例 21 proxy.removeForum(10); 22 proxy.removeTopic(1012); 23 } 24 }
運行 TestForumService.main() 輸出結果:
begin monitor...
模擬刪除Forum記錄:10
end monitor...
com.spring06.ForumServiceImpl.removeForum花費41毫秒
begin monitor...
模擬刪除Topic記錄:1012
end monitor...
com.spring06.ForumServiceImpl.removeTopic花費21毫秒
運行效果是一致的,橫切邏輯代碼抽取到了 PerformanceHandler 中。當其餘業務類的業務方法須要性能監視時候,只須要爲他們建立代理類就好了。
使用JDK建立代理有一個限制,即它只能爲接口建立代理實例,CGLib做爲一個替代者,填補了這項空缺。
使用CGLib以前,須要先導入CGLib的jar包:
GitHub:https://github.com/cglib/cglib
Maven:
<dependency> <groupId>cglib</groupId> <artifactId>cglib</artifactId> <version>3.2.8</version> </dependency>
1 package com.spring07; 2 3 import net.sf.cglib.proxy.Enhancer; 4 import net.sf.cglib.proxy.MethodInterceptor; 5 import net.sf.cglib.proxy.MethodProxy; 6 7 import java.lang.reflect.Method; 8 9 public class CglibProxy implements MethodInterceptor { 10 private Enhancer enhancer = new Enhancer(); 11 12 public Object getProxy(Class clazz) { 13 14 // 設置須要建立子類的類 15 enhancer.setSuperclass(clazz); 16 enhancer.setCallback(this); 17 // 經過字節碼技術動態建立子類實例 18 return enhancer.create(); 19 } 20 21 @Override 22 public Object intercept(Object o, Method method, Object[] objects, MethodProxy methodProxy) throws Throwable { 23 // 攔截父類全部的方法 24 // 開始性能監視 25 PerformanceMonitor.begin(o.getClass().getName() + "." + method.getName()); 26 // 經過代理類調用父類中的方法 27 Object result = methodProxy.invokeSuper(o, objects); 28 // 結束性能監視 29 PerformanceMonitor.end(); 30 return result; 31 } 32 }
修改 TestForumService 調用流程:
1 package com.spring07; 2 3 public class TestForumService { 4 public static void main(String[] args) { 5 CglibProxy proxy = new CglibProxy(); 6 // 經過動態生成子類的方式建立代理類 7 ForumServiceImpl forumService = (ForumServiceImpl) proxy.getProxy(ForumServiceImpl.class); 8 forumService.removeForum(10); 9 forumService.removeTopic(1023); 10 } 11 }
運行 TestForumService.main() 輸出結果:
begin monitor...
模擬刪除Forum記錄:10
end monitor...
com.spring07.ForumServiceImpl$$EnhancerByCGLIB$$3123332f.removeForum花費60毫秒
begin monitor...
模擬刪除Topic記錄:1023
end monitor...
com.spring07.ForumServiceImpl$$EnhancerByCGLIB$$3123332f.removeTopic花費21毫秒
這裏能夠看到類名變成了com.spring07.ForumServiceImpl$$EnhancerByCGLIB$$3123332f.removeTopic,這個就是CGLib動態建立的子類。
因爲CGLib採用動態建立子類的方式生成代理對象,因此不能對目標類中的 final 或 private 方法進行代理。
Spring在新版本中對AOP功能進行了加強,體如今這麼幾個方面:
Spring借鑑了AspectJ的切面,以提供註解驅動的AOP。這種AOP風格的好處在於可以不使用XML來完成功能。
爲了更好的理解,這裏我新舉個例子:
手機接口: Phone
1 package com.spring09; 2 3 public interface Phone { 4 5 // 打電話 6 public void call(String str); 7 8 // 發短信 9 public void sendMsg(String str); 10 }
手機接口實現類: BndPhone
1 package com.spring09; 2 3 public class BndPhone implements Phone { 4 @Override 5 public void call(String str) { 6 System.out.println("打電話 - " + str); 7 } 8 9 @Override 10 public void sendMsg(String str) { 11 System.out.println("發短信 - " + str); 12 } 13 }
手機擴展功能接口: PhoneExtend
1 package com.spring09; 2 3 public interface PhoneExtend { 4 5 // 聽音樂 6 public void listenMusic(String str); 7 8 // 看視頻 9 public void watchVideo(String str); 10 }
手機擴展功能接口實現類: BndPhoneExtend
1 package com.spring09; 2 3 public class BndPhoneExtend implements PhoneExtend { 4 @Override 5 public void listenMusic(String str) { 6 System.out.println("聽音樂 - " + str); 7 } 8 9 @Override 10 public void watchVideo(String str) { 11 System.out.println("看視頻 - " + str); 12 } 13 }
註解類: EnablePhoneExtendAspect 這裏面定義了切面。這裏須要先引入aspectjrt和aspectjweaver的jar包,Maven的配置代碼會在後面貼出。
1 package com.spring09; 2 3 import org.aspectj.lang.annotation.Aspect; 4 import org.aspectj.lang.annotation.DeclareParents; 5 import org.springframework.stereotype.Component; 6 7 @Component 8 @Aspect 9 public class EnablePhoneExtendAspect { 10 @DeclareParents(value = "com.spring09.BndPhone", // 指定手機具體的實現 11 defaultImpl = BndPhoneExtend.class) // 手機擴展具體的實現 12 public PhoneExtend phoneExtend; // 要實現的目標接口 13 }
Spring XML配置:
<?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.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"> <!-- 開啓註解掃描 --> <context:component-scan base-package="com.spring09"/> <!-- 開啓aop註解方式,默認爲false --> <aop:aspectj-autoproxy/> <bean id="phone" class="com.spring09.BndPhone"/> <bean id="phoneExtend" class="com.spring09.BndPhoneExtend"/> <bean class="com.spring09.EnablePhoneExtendAspect"/> </beans>
測試類: Test
1 package com.spring09; 2 3 import org.springframework.context.ApplicationContext; 4 import org.springframework.context.support.ClassPathXmlApplicationContext; 5 6 public class Test { 7 public static void main(String[] args) { 8 ApplicationContext ctx = new ClassPathXmlApplicationContext("META-INF/applicationContext.xml"); 9 10 Phone phone = (Phone) ctx.getBean("phone"); 11 12 // 調用手機原有的方法 13 phone.call("BNDong"); 14 phone.sendMsg("BNDong"); 15 16 // 經過引介/引入切面已經將phone實現了PhoneExtend接口,因此能夠強制轉換 17 PhoneExtend phoneExtend = (PhoneExtend) phone; 18 phoneExtend.listenMusic("BNDong"); 19 phoneExtend.watchVideo("BNDong"); 20 } 21 }
執行 Test,main() 輸出結果:
打電話 - BNDong 發短信 - BNDong 聽音樂 - BNDong 看視頻 - BNDong
能夠看到 BndPhone 並無實現 PhoneExtend 接口,可是經過引介/引入切面 BndPhone 擁有了 PhoneExtend 的實現。
我是經過Maven構建的Spring,這裏我附上我Maven的pom:
<?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.spring09</groupId> <artifactId>spring09-demo</artifactId> <version>1.0-SNAPSHOT</version> <name>spring09-demo</name> <!-- FIXME change it to the project's website --> <url>http://www.example.com</url> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <maven.compiler.source>1.7</maven.compiler.source> <maven.compiler.target>1.7</maven.compiler.target> <org.springframework.version>4.3.7.RELEASE</org.springframework.version> </properties> <dependencies> <dependency> <groupId>junit</groupId> <artifactId>junit</artifactId> <version>4.11</version> <scope>test</scope> </dependency> <!-- spring start --> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aop</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-aspects</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-beans</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-context-support</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-core</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-expression</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-instrument</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-instrument-tomcat</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jdbc</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-jms</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-messaging</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-orm</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-oxm</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-test</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-tx</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-webmvc-portlet</artifactId> <version>${org.springframework.version}</version> </dependency> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-websocket</artifactId> <version>${org.springframework.version}</version> </dependency> <!-- spring end --> <dependency> <groupId>aspectj</groupId> <artifactId>aspectjrt</artifactId> <version>1.5.4</version> </dependency> <dependency> <groupId>aspectj</groupId> <artifactId>aspectjweaver</artifactId> <version>1.5.4</version> </dependency> </dependencies> <build> <pluginManagement><!-- lock down plugins versions to avoid using Maven defaults (may be moved to parent pom) --> <plugins> <plugin> <artifactId>maven-clean-plugin</artifactId> <version>3.0.0</version> </plugin> <!-- see http://maven.apache.org/ref/current/maven-core/default-bindings.html#Plugin_bindings_for_jar_packaging --> <plugin> <artifactId>maven-resources-plugin</artifactId> <version>3.0.2</version> </plugin> <plugin> <artifactId>maven-compiler-plugin</artifactId> <version>3.7.0</version> </plugin> <plugin> <artifactId>maven-surefire-plugin</artifactId> <version>2.20.1</version> </plugin> <plugin> <artifactId>maven-jar-plugin</artifactId> <version>3.0.2</version> </plugin> <plugin> <artifactId>maven-install-plugin</artifactId> <version>2.5.2</version> </plugin> <plugin> <artifactId>maven-deploy-plugin</artifactId> <version>2.8.2</version> </plugin> </plugins> </pluginManagement> </build> </project>
Spring的AOP配置元素可以以非侵入性的方式聲明切面。
一樣使用上面手機的實例代碼,咱們去掉註解類經過XML配置的方式實現切面。
首先刪除註解類 EnablePhoneExtendAspect ,這時再運行 Test.main() 就會拋出異常:
打電話 - BNDong
發短信 - BNDong
Exception in thread "main" java.lang.ClassCastException: com.spring10.BndPhone cannot be cast to com.spring10.PhoneExtend
at com.spring10.Test.main(Test.java:17)
修改Spring XML配置:
<?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.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"> <!-- 開啓註解掃描 --> <context:component-scan base-package="com.spring10"/> <!-- 開啓aop註解方式,默認爲false --> <aop:aspectj-autoproxy/> <aop:config> <!-- 頂層aop配置元素 --> <aop:aspect> <!-- 定義一個切面 --> <aop:declare-parents types-matching="com.spring10.BndPhone" implement-interface="com.spring10.PhoneExtend" delegate-ref="phoneExtend"/> </aop:aspect> </aop:config> <bean id="phone" class="com.spring10.BndPhone"/> <bean id="phoneExtend" class="com.spring10.BndPhoneExtend"/> </beans>
這時運行 Test.main() 發現切面配置成功:
打電話 - BNDong
發短信 - BNDong
聽音樂 - BNDong
看視頻 - BNDong
切面類型總結圖:
《Spring實戰(第4版)》Craig Walls 著 / 張衛濱 譯 下載(密碼:8ts2)
《精通Spring 4.x 企業應用開發實戰》陳雄華 林開雄 文建國 編著 下載(密碼:my25)
https://baike.baidu.com/item/AOP/1332219?fr=aladdin
https://juejin.im/post/5b06bf2df265da0de2574ee1
https://segmentfault.com/a/1190000007469968