当前位置: 首页 > 工具软件 > AspectJ > 使用案例 >

aspectj切面织入

江新
2023-12-01

一、介绍

AspectJ 是一个 AOP 的具体实现框架。AOP(Aspect Oriented Programming)即面向切面编程,可以通过预编译方式和运行期动态代理实现在不修改源代码的情况下给程序动态统一添加功能的一种技术。AspectJ不但可以通过预编译方式(CTW)和运行期动态代理的方式织入切面,还可以在载入(Load Time Weaving, LTW)时织入。AspectJ 扩展了Java,定义了一些专门的AOP语法。

静态代理唯一的缺点就是我们需要对每一个方法编写我们的代理逻辑,造成了工作的繁琐和复杂。AspectJ就是为了解决这个问题,在编译成class字节码的时候在方法周围加上业务逻辑。复杂的工作由特定的编译器帮我们做,而 aspectj-maven-plugin 插件即可帮我们完成静态编译代理。

aspectj-maven-plugin 的相关介绍 aspectj-maven-plugin 的相关介绍, 对应的GitHub 地址 GitHub地址

aspectj-maven-plugin 插件是在 编译阶段 compile 对文件进行增强,可以从 生成的.class 文件可以看出.

二、静态编译代理

静态编译代理依赖ajc编译器,通过maven插件支持。

  • 静态代理所需要的配置:aspectj-maven-plugin插件
  • 并且如果切面来源于三方jar包并且需要对当前项目代码做代理,那么需要通过配置指定插件中的AspectLibrary属性来指定三方包来达到当前项目的增强,切面就是定义在当前项目则无需此操作
  • 依赖AspectjTool 工具包

maven插件如下所示:

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>aspectj-maven-plugin</artifactId>
    <version>1.10</version>
    <configuration>
        <source>1.8</source>
        <target>1.8</target>
        <encoding>UTF-8</encoding>
        <complianceLevel>1.8</complianceLevel>
        <verbose>true</verbose>
        <showWeaveInfo>true</showWeaveInfo>

        <aspectLibraries>
            <aspectLibrary>
                <groupId>com.example.pastor</groupId>
                <artifactId>metric-reportor</artifactId>
            </aspectLibrary>
        </aspectLibraries>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>compile</goal>
                <goal>test-compile</goal>
            </goals>
        </execution>
    </executions>
</plugin>

1、案例一

(1)pom.xml

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjrt -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.8.13</version>
        </dependency>
        <!-- 这个包不用引入,否则切面织入失败 -->
        <!--<dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjtools</artifactId>
            <version>1.8.13</version>
        </dependency>-->
    </dependencies>

    <!--<build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>-->

    <build>
        <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>
                    <Xlint>ignore</Xlint>
                    <encoding>UTF-8</encoding>
                    <!-- 注意:IDEA下这个值要设置为false,否则运行程序时IDEA会再次编译,导致aspectj-maven-plugin编译的结果被覆盖 -->
                    <skip>false</skip>
                </configuration>
                <executions>
                    <execution>
                        <configuration>
                            <skip>false</skip>
                        </configuration>
                        <goals>
                            <goal>compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

(2)业务方法

package com.scy.example.aspectj;

public class UserServiceEnd {
    public void printLog() {
        System.out.println(" no param.....");
    }
}

(3)切面代码

package com.scy.example.aspectj;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;

@Aspect
public class MethodEndAspect {

    @Before("execution(* com.scy.example.aspectj.UserServiceEnd.printLog())")
    public void setStartTimeInThreadLocal(JoinPoint joinPoint) {
        System.out.println("before ...");
    }

}

(4)编译

输入 编译命令,mvn clean compile

PS E:\IdeaProjectSun\example> mvn clean compile
[INFO] Scanning for projects...
[INFO]
[INFO] --------------------------< com.scy:example >---------------------------
[INFO] Building example 0.0.1-SNAPSHOT
[INFO] --------------------------------[ jar ]---------------------------------
[INFO]
[INFO] --- maven-clean-plugin:3.1.0:clean (default-clean) @ example ---
[INFO] Deleting E:\IdeaProjectSun\example\target
[INFO]
[INFO] --- maven-resources-plugin:3.1.0:resources (default-resources) @ example ---
[INFO] Using 'UTF-8' encoding to copy filtered resources.
[INFO] Copying 1 resource
[INFO] Copying 16 resources
[INFO]
[INFO] --- maven-compiler-plugin:3.8.1:compile (default-compile) @ example ---
[INFO] Changes detected - recompiling the module!
[INFO] Compiling 8 source files to E:\IdeaProjectSun\example\target\classes
[INFO]
[INFO] --- aspectj-maven-plugin:1.11:compile (default) @ example ---
[INFO] Showing AJC message detail for messages of types: [error, warning, fail]
[INFO] Join point 'method-execution(void com.scy.example.aspectj.UserServiceEnd.printLog())' in Type 'com.scy.example.aspectj.UserServiceEnd' (UserServiceEnd.java:4
) advised by before advice from 'com.scy.example.aspectj.MethodEndAspect' (MethodEndAspect.java:11)
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  2.306 s
[INFO] Finished at: 2022-09-02T14:41:07+08:00
[INFO] ------------------------------------------------------------------------

(5)查看反编译的class文件

//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//

package com.scy.example.aspectj;

import org.aspectj.lang.JoinPoint;
import org.aspectj.runtime.reflect.Factory;

public class UserServiceEnd {
    public UserServiceEnd() {
    }

    public void printLog() {
        JoinPoint var1 = Factory.makeJP(ajc$tjp_0, this, this);
        MethodEndAspect.aspectOf().setStartTimeInThreadLocal(var1);
        System.out.println(" no param.....");
    }

    static {
        ajc$preClinit();
    }
}

(6)运行结果

before ...
 no param.....

2、案例二

对pom.xml 进行优化,其他不变。

    <dependencies>
        <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjrt -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.8.13</version>
        </dependency>
        <!-- https://mvnrepository.com/artifact/org.aspectj/aspectjtools -->
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjtools</artifactId>
            <version>1.8.13</version>
        </dependency>
    </dependencies>

    <!--<build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>-->

    <build>
        <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>
                </configuration>
                <executions>
                    <execution>
                        <goals>
                            <goal>compile</goal>
                            <goal>test-compile</goal>
                        </goals>
                    </execution>
                </executions>
            </plugin>
        </plugins>
    </build>

三、LoadTimeWeave加载期织入增强

加载类文件时通过instrument包去修改字节码来插入增强逻辑

  • 首先要通过@EnableLoadTimeWeaving(aspectjWeaving = EnableLoadTimeWeaving.AspectJWeaving.AUTODETECT)
  • 其次要在项目启动参数上要通过探针指定instrument包来动态修改织入
  • 在spring boot应用加上此配置来让应用去读取META-INF目录下的aop.xml读取切面已经要织入的路径配置(目前此配置在监控的sdk中我已经定义配置好)

工程配置类开启LTW

@EnableLoadTimeWeaving(aspectjWeaving = EnableLoadTimeWeaving.AspectJWeaving.AUTODETECT)
1

java探针启动参数

 -javaagent:spring-instrument-5.2.5.RELEASE.jar
1

然后在类路径下META-INF下面提供aop描述文件aop.xml,描述待织入的类路径和切面类所在位置

<!DOCTYPE aspectj PUBLIC "-//AspectJ//DTD//EN" "http://www.eclipse.org/aspectj/dtd/aspectj.dtd">
<aspectj>
	<weaver options="-showWeaveInfo -verbose">
		<include within="com.example.ltw.entity.*"/>
		<include within="com.example.ltw.service.*"/>
	</weaver>
	<aspects>
		<aspect name="com.example.ltw.ProfilingAspect"/>
		<aspect name="com.qiyi.pastor.metricreportor.report.PastorReportAop"/>
	</aspects>
</aspectj>

四、小结

  • Aspectj并不是动态的在运行时生成代理类,而是在编译的时候就植入代码到class文件
  • 由于是静态织入的,所以性能相对来说比较好
  • Aspectj不受类的特殊限制,不管方法是private、或者static、或者final的,都可以代理
  • Aspectj不会代理除了限定方法之外任何其他诸如toString(),clone()等方法

五、参考

https://blog.csdn.net/healist/article/details/108824428

基于使用AspectJ实现AOP,注解AOP开发(基于xml文件、基于注解)_杭州小哥哥的博客-CSDN博客

 类似资料: