spring aop 与 aspectj

官网如此描述:

  • 对Java编程语言中的AOP进行无缝扩展(这点很重要)
  • 适用Java平台
  • 易学易用

aspectj如何使用

aspectj 有三种使用方式:

  1. 编译时织入,利用ajc编译器直接将切面织入到源代码中并编译为class
  2. 编译后织入,利用ajc编译器向编译后的class或jar织入切面代码
  3. 运行时织入,不使用ajc编译器,而是利用java agent的能力,在类加载时将切面织入目标代码。

下面我们就看看这三种方式具体如何使用:

编译时织入

现在都是使用Maven管理项目,必然后对应的Maven插件来满足编译时织入的目的:

aspectj-maven-plugin 插件配置如下:

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>aspectj-maven-plugin</artifactId>
    <version>1.11</version>
    <configuration>
        <complianceLevel>${source.version}</complianceLevel>
        <source>${source.version}</source>
        <target>${source.version}</target>
        <showWeaveInfo>true</showWeaveInfo>
        <verbose>true</verbose>
        <encoding>UTF-8</encoding>
        <outputDirectory>${build.outputDirectory}</outputDirectory>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>compile</goal>
                <goal>test-compile</goal>
            </goals>
        </execution>
    </executions>
</plugin>

要增强的目标类:

/**
 * @author jiexiu
 * created 2020/5/31 - 16:45
 */
public class HelloApp {

    public void say() {
        System.out.println("Hello Java!");
    }

    public static void main(String[] args) {
        HelloApp app = new HelloApp();
        app.say();
    }
}

aspectj文件:

/**
 * @author jiexiu
 * created 2020/5/31 - 16:49
 */
public aspect AjAspect {

    pointcut say():
            execution(* HelloApp.say(..));


    before(): say() {
        System.out.println("before say");
    }

    after(): say() {
        System.out.println("after say");
    }
}

mvn compile 后,观察增强后的class文件和切面类文件:

AjAspect.class

@Aspect
public class AjAspect {
    static {
        try {
            ajc$postClinit();
        } catch (Throwable var1) {
            ajc$initFailureCause = var1;
        }

    }

    public AjAspect() {
    }

    @Before(
        value = "say()",
        argNames = ""
    )
    public void ajc$before$com_leokongwq_springlearn_aop_AjAspect$1$682722c() {
        System.out.println("before say");
    }

    @After(
        value = "say()",
        argNames = ""
    )
    public void ajc$after$com_leokongwq_springlearn_aop_AjAspect$2$682722c() {
        System.out.println("after say");
    }

    public static AjAspect aspectOf() {
        if (ajc$perSingletonInstance == null) {
            throw new NoAspectBoundException("com_leokongwq_springlearn_aop_AjAspect", ajc$initFailureCause);
        } else {
            return ajc$perSingletonInstance;
        }
    }

    public static boolean hasAspect() {
        return ajc$perSingletonInstance != null;
    }
}

可见切面描述文件 AjAspect.aj 被编译为一个单例类,增强的功能对应一个静态方法。

HelloApp.class

package com.leokongwq.springlearn.aop;

public class HelloApp {
    public HelloApp() {
    }

    public void say() {
        try {
            AjAspect.aspectOf().ajc$before$com_leokongwq_springlearn_aop_AjAspect$1$682722c();
            System.out.println("Hello Java!");
        } catch (Throwable var2) {
            AjAspect.aspectOf().ajc$after$com_leokongwq_springlearn_aop_AjAspect$2$682722c();
            throw var2;
        }

        AjAspect.aspectOf().ajc$after$com_leokongwq_springlearn_aop_AjAspect$2$682722c();
    }

    public static void main(String[] args) {
        HelloApp app = new HelloApp();
        app.say();
    }
}

被增强类的方法中直接包含了增强逻辑。

基于注解增强

上面展示了如何基于 *.aj 文件对目标类进行增强。其实也可以通过基于Aspectj注解的Java类来描述切面和增强逻辑等相关信息,进而对目标类进行源代码级别的增强。

/**
 * @author jiexiu
 */
@Aspect
@Component
public class AopAspect {
    @Around("execution(* com.leokongwq.springlearn.aop.HelloApp.sayHi(..))")
    public Object watchPerform(ProceedingJoinPoint joinPoint) throws Throwable {
        System.out.println("########### around before invoke ###############");
        Object result = joinPoint.proceed();
        System.out.println("########### around after invoke ###############");
        return result;
    }
}

增强后的class文件内容如下:

package com.leokongwq.springlearn.aop;

import org.aspectj.runtime.reflect.*;
import com.leokongwq.springlearn.component.*;
import org.aspectj.lang.*;

public class HelloApp
{
    private static final /* synthetic */ JoinPoint$StaticPart ajc$tjp_0;
    
    public void say() {
        try {
            AjAspect.aspectOf().ajc$before$com_leokongwq_springlearn_aop_AjAspect$1$682722c();
            System.out.println("Hello Java!");
        }
        catch (Throwable t) {
            AjAspect.aspectOf().ajc$after$com_leokongwq_springlearn_aop_AjAspect$2$682722c();
            throw t;
        }
        AjAspect.aspectOf().ajc$after$com_leokongwq_springlearn_aop_AjAspect$2$682722c();
    }
    
    public String sayHi() {
        final JoinPoint jp = Factory.makeJP(HelloApp.ajc$tjp_0, (Object)this, (Object)this);
        return (String)sayHi_aroundBody1$advice(this, jp, AopAspect.aspectOf(), (ProceedingJoinPoint)jp);
    }
    
    public static void main(final String[] args) {
        final HelloApp app = new HelloApp();
        app.say();
        app.sayHi();
        System.out.println(Math.abs(-10));
    }
    
    static {
        ajc$preClinit();
    }
    
    private static final /* synthetic */ String sayHi_aroundBody0(final HelloApp ajc$this, final JoinPoint joinPoint) {
        System.out.println("Hi java");
        return "Hi";
    }
    
    private static final /* synthetic */ Object sayHi_aroundBody1$advice(final HelloApp ajc$this, final JoinPoint thisJoinPoint, final AopAspect ajc$aspectInstance, final ProceedingJoinPoint joinPoint) {
        System.out.println("########### around before invoke ###############");
        final Object result = sayHi_aroundBody0(ajc$this, (JoinPoint)joinPoint);
        System.out.println("########### around after invoke ###############");
        return result;
    }
    
    private static /* synthetic */ void ajc$preClinit() {
        final Factory factory = new Factory("HelloApp.java", (Class)HelloApp.class);
        ajc$tjp_0 = factory.makeSJP("method-execution", (Signature)factory.makeMethodSig("1", "sayHi", "com.leokongwq.springlearn.aop.HelloApp", "", "", "", "java.lang.String"), 13);
    }
}

可以class文件的内容可以看到 sayHi 方法被增强了。

编译后织入

这个功能主要是用来面向依赖的第三方类库的。因为通常我们不能修改源代码,但是还有对它进行增强的需求。那么只能依赖于编译后织入,或者在类加载时transform。

maven配置

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>aspectj-maven-plugin</artifactId>
    <version>1.11</version>
    <configuration>
        <complianceLevel>${source.version}</complianceLevel>
        <source>${source.version}</source>
        <target>${source.version}</target>
        <showWeaveInfo>true</showWeaveInfo>
        <verbose>true</verbose>
        <encoding>UTF-8</encoding>
        <!--<aspectDirectory>src/main/java/com/leokongwq/springlearn/aop</aspectDirectory>-->
        <outputDirectory>${build.outputDirectory}</outputDirectory>
        <!-- 对依赖的包进行织入 -->
        <weaveDependencies>
            <weaveDependency>
                <groupId>com.google.code.gson</groupId>
                <artifactId>gson</artifactId>
            </weaveDependency>
        </weaveDependencies>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>compile</goal>
                <goal>test-compile</goal>
            </goals>
        </execution>
    </executions>
</plugin>

示例代码:

@Around("execution(* com.google.gson.Gson.toJson(..))")
public Object watchtoJson(ProceedingJoinPoint joinPoint) throws Throwable {
    System.out.println("########### around before toJson ###############");
    Object result = joinPoint.proceed();
    System.out.println("########### around after toJson ###############");
    return result;
}

测试代码:

public static void main(String[] args) {
    Gson gson = new Gson();
    Map<String, String> result = new HashMap<>();
    result.put("code", "SUCCESS");
    System.out.println(gson.toJson(result));
}

输出:

########### around before toJson ###############
########### around before toJson ###############
########### around before toJson ###############
########### around before toJson ###############
########### around after toJson ###############
########### around after toJson ###############
########### around after toJson ###############
########### around after toJson ###############
{"code":"SUCCESS"}

为啥有多行呢?答案留给读者朋友。

加载时增强

加载时增强和编译后增强本质上是一样的。不同点在于将增强的时机延迟到JVM加载类的时候。

maven配置:

 <plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.10</version>
    <configuration>
        <argLine>
            -javaagent:${user.home}/.m2/repository/org/aspectj/aspectjweaver/${aspectj.version}/aspectjweaver-${aspectj.version}.jar
        </argLine>
        <useSystemClassLoader>true</useSystemClassLoader>
        <forkMode>always</forkMode>
    </configuration>
</plugin>

这个配置主要是用来添加命令行参数。当然了,你也可以在idea的 VM options 配置框里面填写参数,或者在项目启动脚本中配置JVM参数。 这里需要指定的参数是 -javaagent:/path/to/agent.jar

只是指定了javaagent还不够,还需要指定需要对那些类进行什么样的增强。

在将项目的resources目录下面建一个子目录 META-INF ,在该目录中创建配置文件 aop.xml ,文件内如下:

<aspectj>
    <aspects>
        <aspect name="com.leokongwq.springlearn.component.GsonAspect"/>
        <weaver options="-verbose -showWeaveInfo">
            <include within="com.google.gson.*"/>
        </weaver>
    </aspects>
</aspectj>

到此就配置好了。启动单元测试,就能看到结果。

aspectj 与Spring AOP的关系

从上一篇文章和上面的内容可知:

  1. 在日常的Spring AOP使用过程中我们并没有用到Aspectj的 织入 能力,使用到的大都是Aspectj提供的注解和切面相关的语法。
  2. Spring AOP的实现底层是基于JDK代理和CGLIB代理实现的。
  3. AOP的实现可以基于织入,也可以基于代理。
我来评几句
登录后评论

已发表评论数()

相关站点

+订阅
热门文章