日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

AspectJ使用

發布時間:2023/12/20 编程问答 29 豆豆
生活随笔 收集整理的這篇文章主要介紹了 AspectJ使用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、AOP介紹

AOP:Aspect-Oriented Programming,面向切面編程,是一種新的方法論(編程范式),是對傳統 OOP(Object-Oriented Programming,面向對象編程)的補充。旨在通過允許橫切關注點的分離,提高模塊化。如在方法執行前、或執行后、或是在執行中出現異常后這些地方進行攔截處理或叫做增強處理。主要應用于:日志收集、事務管理、安全檢查、緩存、對象池管理等。

AOP實現的關鍵就在于AOP框架自動創建的AOP代理,AOP代理則可分為靜態代理(例如:原生AspectJ)和動態代理(例如:spring aop)兩大類,其中靜態代理是指使用AOP框架提供的命令進行編譯,從而在編譯階段就可生成 AOP 代理類,因此也稱為編譯時增強;而動態代理則在運行時借助于JDK動態代理、CGLIB等在內存中“臨時”生成AOP動態代理類,因此也被稱為運行時增強。

AOP基本概念:

  • 切入點(pointcut):在哪些類、哪些方法上切入,通常是一個正則表達式
  • 執行點(JoinPoint):通過pointcut選取出來的集合中的具體的一個執行點,我們就叫JoinPoint
  • 通知(advice):在方法前、方法后、方法前后、異常等做什么。
  • 切面(aspect):切面 = pointcut + advice。即在什么時機、什么地方、做什么。
  • 織入(weaving):把切面加入對象,并創建出代理對象的過程。

二、AspectJ介紹

AspectJ:全稱Eclipse AspectJ,官網The AspectJ Project | The Eclipse Foundation 是Java社區里最完整最流行的AOP框架,即AOP的java版實現,它定義了AOP語法,它有一個專門的編譯器用來生成遵守Java字節編碼規范的Class文件。(除了AspectJ外,還有很多AOP實現,例如ASMDex)

aspectJ可以單獨使用,也可以整合到其它框架中。單獨使用AspectJ時需要使用專門的編譯器ajc。java的編譯器是javac,AspectJ的編譯器是ajc,aj是首字母縮寫,c即compiler。

1、AspectJ原理:

AspectJ屬于靜態織入,通過修改代碼來實現,有如下幾個織入的時機:

  • ?編譯期織入(Compile-time weaving): 如類 A 使用 AspectJ 添加了一個屬性,類 B 引用了它,這個場景就需要編譯期的時候就進行織入,否則沒法編譯類 B。
  • 編譯后織入(Post-compile weaving): 也就是已經生成了 .class 文件,或已經打成 jar 包了,這種情況我們需要增強處理的話,就要用到編譯后織入。
  • 類加載后織入(Load-time weaving): 指的是在加載類的時候進行織入,要實現這個時期的織入,有幾種常見的方法。1、自定義類加載器來干這個,這個應該是最容易想到的辦法,在被織入類加載到 JVM 前去對它進行加載,這樣就可以在加載的時候定義行為了。2、在 JVM 啟動的時候指定 AspectJ 提供的 agent:-javaagent:xxx/xxx/aspectjweaver.jar。
  • AspectJ可以做Spring AOP干不了的事情,它是AOP編程的完全解決方案,Spring AOP則致力于解決企業級開發中最普遍的AOP(方法織入)。而不是成為像AspectJ一樣的AOP方案。因為AspectJ在實際運行之前就完成了織入,所以說它生成的類是沒有額外運行時開銷的。

    Spring AOP

    AspectJ

    在純 Java 中實現

    使用 Java 編程語言的擴展實現

    不需要單獨的編譯過程

    除非設置 LTW,否則需要 AspectJ 編譯器 (ajc)

    只能使用運行時織入

    運行時織入不可用。支持編譯時、編譯后和加載時織入

    功能不強-僅支持方法級編織

    更強大 - 可以編織字段、方法、構造函數、靜態初始值設定項、最終類/方法等......。

    只能在由 Spring 容器管理的 bean 上實現

    可以在所有域對象上實現

    僅支持方法執行切入點

    支持所有切入點

    代理是由目標對象創建的, 并且切面應用在這些代理上

    在執行應用程序之前 (在運行時) 前, 各方面直接在代碼中進行織入

    比 AspectJ 慢多了

    更好的性能

    易于學習和應用

    相對于 Spring AOP 來說更復雜

    在實際生產中,我們用得最多的還是 Spring AOP。

    2、AspectJ的使用:https://javadoop.com/post/aspectj

    前面介紹了 Spring AOP 的各種用法,包括隨著 Spring 的演進而發展出來的幾種配置方式。但是我們始終沒有使用到 AspectJ,即使是在基于注解的 @AspectJ 的配置方式中,Spring 也僅僅是使用了 AspectJ 包中的一些注解而已,并沒有依賴于 AspectJ 實現具體的功能,接下來將介紹AspectJ的使用

    2.1)代碼如下

    1)maven依賴:

    <!-- aspectj --> <dependency><groupId>org.aspectj</groupId><artifactId>aspectjrt</artifactId><version>1.8.13</version> </dependency> <dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.13</version> </dependency>

    2)定義一個類,對其進行weaving:

    package a.b.c.tftest.model;public class Account {public int balance = 20;public boolean pay(int amount) {if (balance < amount) {return false;}balance -= amount;return true;} }

    3)接下來,我們定義兩個Aspect來進行weaving演示:

    A、AccountAspect:

    用 AspectJ 的語法來寫,對交易進行攔截,如此次交易超過余額,直接拒絕。AccountAspect 需要以 .aj 結尾,如我們在a.b.c.tftest.aspectj 下新建文件 AccountAspect.aj,內容如下:

    package a.b.c.tftest.aspectj;import a.b.c.tftest.model.Account;public aspect AccountAspect {pointcut callPay(int amount, Account account):call(boolean a.b.c.tftest.model.Account.pay(int)) && args(amount) && target(account);before(int amount, Account account): callPay(amount, account) {System.out.println("[AccountAspect]付款前總金額: " + account.balance);System.out.println("[AccountAspect]需要付款: " + amount);}boolean around(int amount, Account account): callPay(amount, account) {if (account.balance < amount) {System.out.println("[AccountAspect]拒絕付款!");return false;}return proceed(amount, account);}after(int amount, Account balance): callPay(amount, balance) {System.out.println("[AccountAspect]付款后,剩余:" + balance.balance);} }

    ?B、ProfilingAspect:用 Java 來寫,用于記錄方法的執行時間

    package a.b.c.tftest.aspectj;import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut;@Aspect public class ProfilingAspect {@Pointcut("execution(* a.b.c.tftest.model.*.*(..))")public void modelLayer() {}@Around("modelLayer()")public Object logProfile(ProceedingJoinPoint joinPoint) throws Throwable {long start = System.currentTimeMillis();Object result = joinPoint.proceed();System.out.println("[ProfilingAspect]方法: 【" + joinPoint.getSignature() + "】結束,用時: " + (System.currentTimeMillis() - start));return result;} }

    4)定一個主類:

    package a.b.c.tftest;import a.b.c.tftest.model.Account; import cn.edu.nuc.test.User;public class App {public static void main(String[] args) {testCompileTime();}public static void testCompileTime() {Account account = new Account();System.out.println("==================");account.pay(10);account.pay(50);System.out.println("==================");} }

    可以看到:AspectJ的語法比較難理解,使用AspectJ提供的注解來寫還是比較方便的(Spring AOP也是使用了AspectJ提供的注解)。代碼結構如下:

    接下來,我們討論怎么樣將定義好的兩個 Aspects 織入到我們的 Account 的付款方法 pay(amount) 中,也就是三種織入時機分別是怎么實現的。

    2.2【Complie-Time Weaving】示例

    這是最簡單的使用方式,在編譯期的時候進行織入,這樣編譯出來的 .class 文件已經織入了我們的代碼,在 JVM 運行的時候其實就是加載了一個普通的被織入了代碼的類。

    1)aspject-maven插件:

    采用 maven 進行管理,可以在 <build> 中加入以下的插件:

    <build><finalName>tftest</finalName><plugins><!-- 編譯期織入 --><plugin><groupId>org.codehaus.mojo</groupId><artifactId>aspectj-maven-plugin</artifactId><version>1.11</version><configuration><complianceLevel>1.8</complianceLevel><source>1.8</source><target>1.8</target><showWeaveInfo>true</showWeaveInfo><verbose>true</verbose><Xlint>ignore</Xlint><encoding>UTF-8</encoding></configuration><executions><execution><configuration><skip>false</skip></configuration><goals><goal>compile</goal></goals></execution></executions></plugin><!--打包插件--><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-assembly-plugin</artifactId><version>2.4</version><configuration><archive><manifest><mainClass>a.b.c.tftest.Test</mainClass></manifest></archive><descriptors><descriptor>assembly/assembly.xml</descriptor></descriptors><descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> </configuration><executions><execution><id>make-assembly</id><phase>package</phase><goals><goal>single</goal></goals></execution></executions></plugin> </plugins><pluginManagement><plugins><!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself. --><plugin><groupId>org.eclipse.m2e</groupId><artifactId>lifecycle-mapping</artifactId><version>1.0.0</version><configuration><lifecycleMappingMetadata><pluginExecutions><pluginExecution><pluginExecutionFilter><groupId>org.codehaus.mojo</groupId><artifactId>aspectj-maven-plugin</artifactId><versionRange>[1.0,)</versionRange><goals><goal>test-compile</goal><goal>compile</goal></goals></pluginExecutionFilter><action><execute /></action></pluginExecution></pluginExecutions></lifecycleMappingMetadata></configuration></plugin></plugins> </pluginManagement> </build>

    說明:如果是eclipse,使用這個插件可能會報如下錯誤:Plugin execution not covered by lifecycle configuration: org.codehaus.mojo:aspectj-maven-plugin:1.11:compile (execution: default, phase: compile) ,解決方法就是按照上面方式添加一個<pluginManagement>

    2)然后通過mvn package打包:

    編譯后,可以看到AccountAspect.aj已經被編譯成了class,如下:

    ?此外,App代碼也被修改了:

    Account類也被修改了:

    ?執行App后,輸出:

    ?2.3 【Post-Compile Weaving】示例:

    Post-Compile Weaving 和 Compile-Time Weaving 非常類似,我們也是直接用場景來說。

    假設上面的 Account 類在test.jar 包中,我們的工程 tftest.jar依賴了這個 jar 包。由于 Account 這個類已經被編譯出來了,我們要對它的方法進行織入,就需要用到編譯后織入。

    為了方便測試,新建一個test工程,里面就一個類 User,代碼和 Account 一樣,mvn package打成test.jar包,里面就這一個User類。同時在tftest工程里引入test.jar,然后也復制 AccountAspect 一份出來,命名為 UserAspect,稍微修改修改就可以用來處理 User 類了。

    1)test工程代碼:

    mvn信息如下:

    <?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>cn.edu.nuc</groupId><artifactId>test</artifactId><version>0.0.1-SNAPSHOT</version><name>test</name><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target></properties><dependencies></dependencies><build><pluginManagement><plugins><plugin><artifactId>maven-clean-plugin</artifactId><version>3.1.0</version></plugin><plugin><artifactId>maven-compiler-plugin</artifactId><version>3.8.0</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>

    代碼如下:?

    package cn.edu.nuc.test;public class User {public int balance = 20;public boolean pay(int amount) {if (balance < amount) {return false;}balance -= amount;return true;} }

    然后使用mvn install 將test.jar安裝到本地倉庫。

    2)tftest工程:

    首先引入test依賴

    <dependency><groupId>cn.edu.nuc</groupId><artifactId>test</artifactId><version>0.0.1-SNAPSHOT</version> </dependency>

    然后,添加UserAspect類:

    package a.b.c.tftest.aspectj;import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut;@Aspect public class UserAspect {@Pointcut("execution(* cn.edu.nuc.test.*.*(..))")public void modelLayer() {}@Around("modelLayer()")public Object logProfile(ProceedingJoinPoint joinPoint) throws Throwable {long start = System.currentTimeMillis();Object result = joinPoint.proceed();System.out.println("[userAspect]方法: 【" + joinPoint.getSignature() + "】結束,用時: " + (System.currentTimeMillis() - start));return result;} }

    最后修改App主類:

    public class App {public static void main(String[] args) {testCompileTime();}public static void testCompileTime() {User user = new User();user.pay(10);} }

    3)修改插件:

    <build><finalName>tftest</finalName><plugins><!--編譯后織入--><plugin><groupId>org.codehaus.mojo</groupId><artifactId>aspectj-maven-plugin</artifactId><version>1.11</version><configuration><complianceLevel>1.8</complianceLevel><weaveDependencies><weaveDependency><groupId>cn.edu.nuc</groupId><artifactId>test</artifactId></weaveDependency></weaveDependencies></configuration><executions><execution><goals><goal>compile</goal></goals></execution></executions></plugin><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-assembly-plugin</artifactId><version>2.4</version><configuration><archive><manifest><mainClass>a.b.c.tftest.Test</mainClass></manifest></archive><descriptors><descriptor>assembly/assembly.xml</descriptor></descriptors><descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> </configuration><executions><execution><id>make-assembly</id><phase>package</phase><goals><goal>single</goal></goals></execution></executions></plugin> </plugins><pluginManagement><plugins><!--This plugin's configuration is used to store Eclipse m2e settings only. It has no influence on the Maven build itself. --><plugin><groupId>org.eclipse.m2e</groupId><artifactId>lifecycle-mapping</artifactId><version>1.0.0</version><configuration><lifecycleMappingMetadata><pluginExecutions><pluginExecution><pluginExecutionFilter><groupId>org.codehaus.mojo</groupId><artifactId>aspectj-maven-plugin</artifactId><versionRange>[1.0,)</versionRange><goals><goal>test-compile</goal><goal>compile</goal></goals></pluginExecutionFilter><action><execute /></action></pluginExecution></pluginExecutions></lifecycleMappingMetadata></configuration></plugin></plugins> </pluginManagement> </build> </project>

    然后執行,mvn clean package 最后,運行App主類,輸出:

    [userAspect]方法: 【boolean cn.edu.nuc.test.User.pay(int)】結束,用時: 1

    從輸出上看,UserAspect 對 User 進行了織入。而且是在User已經編譯后進行的織入。

    2.4 【Load-Time Weaving】示例:

    最后,我們要介紹的是 LTW 織入,正如 Load-Time 的名字所示,它是在 JVM 加載類的時候做的織入。AspectJ 允許我們在啟動的時候指定 agent 來實現這個功能。

    這里還是用到了最初的tftest工程,首先,要注釋掉之前在 pom.xml 中用于編譯期和編譯后織入使用的插件,免得影響我們的測試。(一旦我們去掉了 aspectj 的編譯插件,那么 .aj 的文件是不會被編譯的)

    1)main/resource/META-INF下建立aop.xml文件

    <?xml version="1.0" encoding="UTF-8"?> <aspectj><aspects><aspect name="a.b.c.tftest.aspectj.ProfilingAspect"/><weaver options="-verbose -showWeaveInfo"><include within="a.b.c.tftest..*"/></weaver></aspects> </aspectj>

    定義了切面類和要織入的目標類范圍。

    2)切面類:

    package a.b.c.tftest.aspectj;import org.aspectj.lang.ProceedingJoinPoint; import org.aspectj.lang.annotation.Around; import org.aspectj.lang.annotation.Aspect; import org.aspectj.lang.annotation.Pointcut;@Aspect public class ProfilingAspect {@Pointcut("execution(* a.b.c.tftest.model.*.*(..))")public void modelLayer() {}@Around("modelLayer()")public Object logProfile(ProceedingJoinPoint joinPoint) throws Throwable {long start = System.currentTimeMillis();Object result = joinPoint.proceed();System.out.println("[ProfilingAspect]方法: 【" + joinPoint.getSignature() + "】結束,用時: " + (System.currentTimeMillis() - start));return result;} }

    目標類:Account.java 同上

    ?3)App主類:

    package a.b.c.tftest;import a.b.c.tftest.model.Account;public class App {public static void main(String[] args) {testCompileTime();}public static void testCompileTime() {Account account = new Account();System.out.println("==================");account.pay(10);account.pay(50);System.out.println("==================");} }

    4)pom.xml文件:

    <?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>a.b.c</groupId><artifactId>tftest</artifactId><version>0.0.1-SNAPSHOT</version><name>tftest</name><properties><project.build.sourceEncoding>UTF-8</project.build.sourceEncoding><maven.compiler.source>1.8</maven.compiler.source><maven.compiler.target>1.8</maven.compiler.target></properties><dependencies><!-- aspectj --><dependency><groupId>org.aspectj</groupId><artifactId>aspectjrt</artifactId><version>1.8.13</version></dependency><dependency><groupId>org.aspectj</groupId><artifactId>aspectjweaver</artifactId><version>1.8.13</version></dependency></dependencies><build><finalName>tftest</finalName><plugins><plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-assembly-plugin</artifactId><version>2.4</version><configuration><archive><manifest><mainClass>a.b.c.tftest.App</mainClass></manifest></archive><descriptors><descriptor>assembly/assembly.xml</descriptor></descriptors><descriptorRefs> <descriptorRef>jar-with-dependencies</descriptorRef> </descriptorRefs> </configuration><executions><execution><id>make-assembly</id><phase>package</phase><goals><goal>single</goal></goals></execution></executions></plugin> </plugins></build> </project>

    5) 編譯/打包:

    代碼結構如下,

    ?使用mvn package打包后,反編譯結構如下:

    可以看到,編譯后沒有進行織入。

    然后執行:

    java -jar ./target/tftest-jar-with-dependencies.jar

    輸出:

    ==================

    ==================

    然后執行:

    java -javaagent:/Users/knowliu/Documents/mvn_repo/org/aspectj/aspectjweaver/1.8.10/aspectjweaver-1.8.10.jar -jar ./target/tftest-jar-with-dependencies.jar[AppClassLoader@18b4aac2] info AspectJ Weaver Version 1.8.13 built on Wednesday Nov 15, 2017 at 19:26:44 GMT[AppClassLoader@18b4aac2] info register classloader sun.misc.Launcher$AppClassLoader@18b4aac2[AppClassLoader@18b4aac2] info using configuration file:/Users/knowliu/Documents/workspace1/tftest/target/tftest-jar-with-dependencies.jar!/META-INF/aop.xml[AppClassLoader@18b4aac2] info register aspect a.b.c.tftest.aspectj.ProfilingAspect[AppClassLoader@18b4aac2] weaveinfo Join point 'method-execution(boolean a.b.c.tftest.model.Account.pay(int))' in Type 'a.b.c.tftest.model.Account' (Account.java:11) advised by around advice from 'a.b.c.tftest.aspectj.ProfilingAspect' (ProfilingAspect.java)

    輸出:

    ==================

    [ProfilingAspect]方法: 【boolean a.b.c.tftest.model.Account.pay(int)】結束,用時: 1

    [ProfilingAspect]方法: 【boolean a.b.c.tftest.model.Account.pay(int)】結束,用時: 0

    ==================

    到這里,就要結束這一小節了,這里順便再介紹下如果用 maven 跑測試的話怎么搞。

    首先,我們往 surefire 插件中加上 javaagent:

    <plugin><groupId>org.apache.maven.plugins</groupId><artifactId>maven-surefire-plugin</artifactId><version>2.10</version><configuration><argLine>-javaagent:/xxx/aspectjweaver-1.8.13.jar</argLine><useSystemClassLoader>true</useSystemClassLoader><forkMode>always</forkMode></configuration> </plugin>

    然后,我們就可以用 mvn test 看到織入效果了。還是那句話,不要用 IDE 進行測試,因為 IDE 太“智能”了。

    參考:https://javadoop.com/post/aspectj?

    總結

    以上是生活随笔為你收集整理的AspectJ使用的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。