原生AspectJ实现非spring框架下的AOP

发表于 2020-07-08  59 次阅读


网上比较常见的AOP实现都是基于spring框架,通过spring Aop实现面向切面编程。但是在很多非spring框架的项目中,如果想要实现AOP,就可以选用AspectJ去实现。

AspectJ 简介

AspectJ是一个面向切面的框架,简单的说就是它是AOP的一种实现框架。

AspectJ基于Java进行了扩展,所以可以完全兼容java,因此使用AspectJ有两种方式:

  1. 完全使用AspectJ的语言。这语言一点也不难,和Java几乎一样,也能在AspectJ中调用Java的任何类库。AspectJ只是多了一些关键词罢了。(基于aspect文件)
  2. 使用纯Java语言开发,然后使用AspectJ注解,简称@AspectJ。(基于java注解)

注:由于AspectJ对Java进行了扩展,用java原生的编辑器编译是行不通,必须使用AspectJ特有的编辑器进行编译。本文仅介绍基于java注解的方式实现AOP

AspectJ Jar包介绍

重点jar包有三个:

  • aspectjrt.jar:提供运行时的一些注解,静态方法等;
  • aspectjtools.jar:赫赫有名的ajc编辑器,可以在编译器java文件获取class文件或者aspect文件定义的切面织入到业务代码中;(一般会封装进IDE插件或者自动化插件中)
  • aspectjweaver.jar:提供一个java agent,用于在类加载期间织入切面;

aspectJ的三种织入方式:

  • 编译时织入:利用ajc编辑器代替java编辑器,直接将 java/aspect 文件编译成class文件并且织入切面。
  • 编译后织入:利用ajc编辑器向java编辑器编译生产的class文件中织入切面。
  • 加载时织入:不适用ajc编辑器,利用aspectjweaver.jar工具,使用java agent代理在类加载期间将切面织入。

基于java注解的AspectJ

一般情况我们大都会选择原生的Java环境进行开发,所有这里主要讲解基于Java注解的AspectJ。

新建maven项目并添加依赖如下:

<!-- Aspectj 依赖 -->
<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.9.5</version>
</dependency>

注意指定JDK的版本为1.8

<properties>
    <java.version>1.8</java.version>
    <maven.compiler.source>1.8</maven.compiler.source>
    <maven.compiler.target>1.8</maven.compiler.target>
</properties>

<build>
    <plugins>
        <plugin>
            <groupId>org.apache.maven.plugins</groupId>
            <artifactId>maven-compiler-plugin</artifactId>
            <version>3.6.1</version>
            <configuration>
                <source>${maven.compiler.source}</source>
                <target>${maven.compiler.target}</target>
            </configuration>
        </plugin>
    </plugins>
</build>

添加maven自动化构建插件:aspectj-maven-plugin;该插件会绑定到编译期,采用编译后织入的方式,在maven-compiler-plugin插件执行之后运行。

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>aspectj-maven-plugin</artifactId>
    <version>1.11</version>
    <configuration>
        <source>1.8</source>
        <target>1.8</target>
        <complianceLevel>1.8</complianceLevel>
    </configuration>
    <executions>
        <execution>
            <goals>
                <goal>compile</goal>
            </goals>
        </execution>
    </executions>
</plugin>

创建业务类App类

package org.dylan.demo.aspectj;

public class App {

    public static void main(String[] args) {
        System.out.println(new App().say());
    }

    public String say() {
        return "World";
    }
}

创建切面类AspectDemo

package org.dylan.demo.aspectj;

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

@Aspect //标识该类是一个切面类
public class AspectDemo {

    @Pointcut("execution(* org.dylan.demo.aspectj.App.say())") // 定义切入点
    private void pointcut() {
    }

    @Before("pointcut()") // 定义通知(增强方法的代码)
    public void before() {
        System.out.println("Hello");
    }
}

编译运行 App.main():

使用自定义注解标记切入点

一般实际的开发场景下,切入点会比较混杂,如果通过路径的方式列举会很复杂,而通过自定义注解的方式则可以简洁不少,下面我们以记录日志为例:

创建自定义注解@Log

package org.dylan.demo.aspectj;

import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Log {
    String operation() default "日志注解注入测试";
}

创建切面类 LogAspect

package org.dylan.demo.aspectj;

import org.aspectj.lang.annotation.After;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;

@Aspect
public class LogAspect {

    @Pointcut("execution(@org.dylan.demo.aspectj.Log  * *..*.*(..)) && @annotation(logAnno)")
    public void pointCut(Log logAnno) {

    }

    @Before("pointCut(logAnno)")
    public void before(Log logAnno) {
        System.out.println("运行前 执行增强代码" + logAnno.operation());
    }

    @After("pointCut(logAnno)")
    public void after(Log logAnno) {
        System.out.println("运行后 执行增强代码" + logAnno.operation());
    }

}

新增测试类 LogDemo

package org.dylan.demo.aspectj;

public class LogDemo {
    public static void main(String[] args) {
        new LogDemo().test();
    }

    @Log
    public void test() {
        System.out.println("Hello World!");
    }
}

编译运行 LogDemo.main()


参考资料

AspectJ切入点@Pointcut语法详解

原生AspectJ用法分析以及Spring-AOP原理分析

本站文章基于国际协议BY-NA-SA 4.0协议共享;
如未特殊说明,本站文章皆为原创文章,请规范转载。

0

从业时长3年半的佛系码农,并不会唱跳、rap和篮球