Intro to AspectJ – AspectJ介绍

最后修改: 2016年 12月 8日

中文/混合/英文(键盘快捷键:t)

1. Introduction

1.介绍

This article is a quick and practical introduction to AspectJ.

本文是对AspectJ的快速和实用的介绍。

First, we’ll show how to enable aspect-oriented programming, and then we’ll focus on the difference between compile-time, post-compile, and load-time weaving.

首先,我们将展示如何启用面向方面的编程,然后我们将重点介绍编译时、编译后和加载时编织的区别。

Let’s start with a short introduction of aspect-oriented programming (AOP) and AspectJ’s basics.

让我们先简单介绍一下面向方面的编程(AOP)和AspectJ的基础知识。

2. Overview

2.概述

AOP is a programming paradigm that aims to increase modularity by allowing the separation of cross-cutting concerns. It does so by adding additional behavior to existing code without modification of the code itself. Instead, we declare separately which code is to modify.

AOP是一种编程范式,旨在通过允许跨领域的关注点分离来提高模块化程度。它通过在不修改代码本身的情况下向现有代码添加额外的行为来实现。相反,我们分别声明哪些代码需要修改。

AspectJ implements both concerns and the weaving of crosscutting concerns using extensions of Java programming language.

AspectJ使用Java编程语言的扩展来实现关注点和交叉关注点的编织。

3. Maven Dependencies

3.Maven的依赖性

AspectJ offers different libraries depending on its usage. We can find Maven dependencies under group org.aspectj in the Maven Central repository.

AspectJ根据其用途提供不同的库。我们可以在Maven Central资源库的org.aspectj组下找到Maven依赖项。

In this article, we focus on dependencies needed to create aspects and Weaver using the compile-time, post-compile, and load-time Weavers.

在这篇文章中,我们重点讨论使用编译时、编译后和加载时Weaver创建方面和Weaver所需的依赖性。

3.1. AspectJ Runtime

3.1.AspectJ的运行时间

When running an AspectJ program, the classpath should contain the classes and aspects along with the AspectJ runtime library aspectjrt.jar:

当运行AspectJ程序时,classpath应包含类和方面以及AspectJ运行时库aspectjrt.jar

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjrt</artifactId>
    <version>1.8.9</version>
</dependency>

This dependency is available over on Maven Central.

该依赖关系可在Maven Central上找到。

3.2. AspectJWeaver

3.2.AspectJWeaver

Besides the AspectJ runtime dependency, we will also need to include the aspectjweaver.jar to introduce advice to Java class at load time:

除了AspectJ运行时依赖,我们还需要包括aspectjweaver.jar,以便在加载时向Java类引入建议。

<dependency>
    <groupId>org.aspectj</groupId>
    <artifactId>aspectjweaver</artifactId>
    <version>1.8.9</version>
</dependency>

The dependency is also available on Maven Central.

该依赖关系也可在Maven Central上找到。

4. Aspect Creation

4.方面的创造

AspectJ provides an implementation of AOP and has three core concepts:

AspectJ提供了一个AOP的实现,并有三个核心概念。

  • Join Point
  • Pointcut
  • Advice

We’ll demonstrate these concepts by creating a simple program to validate a user account balance.

我们将通过创建一个简单的程序来验证用户的账户余额来演示这些概念。

First, let’s create an Account class with a given balance and a method to withdraw:

首先,让我们创建一个Account类,有一个给定的余额和一个提款方法。

public class Account {
    int balance = 20;

    public boolean withdraw(int amount) {
        if (balance < amount) {
            return false;
        } 
        balance = balance - amount;
        return true;
    }
}

We’ll create an AccountAspect.aj file to log account information and to validate the account balance (note that AspectJ files end with a “.aj” file extension):

我们将创建一个AccountAspect.aj文件来记录账户信息并验证账户余额(注意AspectJ文件以”.aj“文件扩展名结尾)。

public aspect AccountAspect {
    final int MIN_BALANCE = 10;

    pointcut callWithDraw(int amount, Account acc) : 
     call(boolean Account.withdraw(int)) && args(amount) && target(acc);

    before(int amount, Account acc) : callWithDraw(amount, acc) {
    }

    boolean around(int amount, Account acc) : 
      callWithDraw(amount, acc) {
        if (acc.balance < amount) {
            return false;
        }
        return proceed(amount, acc);
    }

    after(int amount, Account balance) : callWithDraw(amount, balance) {
    }
}

As we can see, we’ve added a pointcut to the withdraw method and created three advises that refer to the defined pointcut.

正如我们所看到的,我们已经在提款方法中添加了一个pointcut,并创建了三个advises,这些建议都是参考定义的pointcut

In order to understand the following, we introduce the following definitions:

为了理解下文,我们介绍以下定义。

  • Aspect: A modularization of a concern that cuts across multiple objects. Each aspect focuses on a specific crosscutting functionality
  • Join point: A point during the execution of a script, such as the execution of a method or property access
  • Advice: Action taken by an aspect at a particular join point
  • Pointcut: A regular expression that matches join points. An advice is associated with a pointcut expression and runs at any join point that matches the pointcut

For more details on these concepts and their specific semantics, we may want to check out the following link.

关于这些概念及其具体语义的更多细节,我们不妨看看下面的链接>。

Next, we need to weave the aspects into our code. The sections below address three different types of weaving: compile-time weaving, post-compile weaving, and load-time weaving in AspectJ.

接下来,我们需要将这些方面编织到我们的代码中。下面的章节涉及三种不同类型的编织:编译时编织、编译后编织和AspectJ的加载时编织。

5. Compile-Time Weaving

5.编译时编织

The simplest approach of weaving is compile-time weaving. When we have both the source code of the aspect and the code that we are using aspects in, the AspectJ compiler will compile from source and produce a woven class files as output. Afterward, upon execution of your code, the weaving process output class is loaded into JVM as a normal Java class.

最简单的编织方法是编译时编织。当我们同时拥有方面的源代码和使用方面的代码时,AspectJ编译器将从源头进行编译,并产生一个编织的类文件作为输出。之后,在执行你的代码时,编织过程中输出的类会作为一个普通的Java类加载到JVM中。

We can download the AspectJ Development Tools since it includes a bundled AspectJ compiler. One of AJDT’s most important features is a tool for the visualization of crosscutting concerns, which is helpful for debugging a pointcut specification. We may visualize the combined effect even before the code is deployed.

我们可以下载AspectJ开发工具,因为它包括一个绑定的AspectJ编译器。AJDT最重要的功能之一是用于可视化交叉关注点的工具,这对于调试一个点切规范很有帮助。我们甚至可以在代码部署之前就将综合效果可视化。

We use Mojo’s AspectJ Maven Plugin to weave AspectJ aspects into our classes using the AspectJ compiler.

我们使用Mojo的AspectJ Maven插件,利用AspectJ编译器将AspectJ方面编织到我们的类中。

<plugin>
    <groupId>org.codehaus.mojo</groupId>
    <artifactId>aspectj-maven-plugin</artifactId>
    <version>1.7</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>
            <goals>
                <!-- use this goal to weave all your main classes -->
                <goal>compile</goal>
                <!-- use this goal to weave all your test classes -->
                <goal>test-compile</goal>
            </goals>
        </execution>
    </executions>
</plugin>

For more details on option reference of the AspectJ compiler, we may want to check out the following link.

关于AspectJ编译器的选项参考的更多细节,我们可能要查看以下链接

Let’s add some test cases for our Account class:

让我们为我们的帐户类添加一些测试案例。

public class AccountTest {
    private Account account;

    @Before
    public void before() {
        account = new Account();
    }

    @Test
    public void given20AndMin10_whenWithdraw5_thenSuccess() {
        assertTrue(account.withdraw(5));
    }

    @Test
    public void given20AndMin10_whenWithdraw100_thenFail() {
        assertFalse(account.withdraw(100));
    }
}

When we run the test cases, the below text that is shown in the console means that we successfully weaved the source code:

当我们运行测试案例时,控制台中显示的以下文字意味着我们成功地编织了源代码。

[INFO] Join point 'method-call
(boolean com.baeldung.aspectj.Account.withdraw(int))' in Type
'com.baeldung.aspectj.test.AccountTest' (AccountTest.java:20)
advised by around advice from 'com.baeldung.aspectj.AccountAspect'
(AccountAspect.class:18(from AccountAspect.aj))

[INFO] Join point 'method-call
(boolean com.baeldung.aspectj.Account.withdraw(int))' in Type 
'com.baeldung.aspectj.test.AccountTest' (AccountTest.java:20) 
advised by before advice from 'com.baeldung.aspectj.AccountAspect' 
(AccountAspect.class:13(from AccountAspect.aj))

[INFO] Join point 'method-call
(boolean com.baeldung.aspectj.Account.withdraw(int))' in Type 
'com.baeldung.aspectj.test.AccountTest' (AccountTest.java:20) 
advised by after advice from 'com.baeldung.aspectj.AccountAspect'
(AccountAspect.class:26(from AccountAspect.aj))

2016-11-15 22:53:51 [main] INFO  com.baeldung.aspectj.AccountAspect 
-  Balance before withdrawal: 20
2016-11-15 22:53:51 [main] INFO  com.baeldung.aspectj.AccountAspect 
-  Withdraw ammout: 5
2016-11-15 22:53:51 [main] INFO  com.baeldung.aspectj.AccountAspect 
- Balance after withdrawal : 15
2016-11-15 22:53:51 [main] INFO  com.baeldung.aspectj.AccountAspect 
-  Balance before withdrawal: 20
2016-11-15 22:53:51 [main] INFO  com.baeldung.aspectj.AccountAspect 
-  Withdraw ammout: 100
2016-11-15 22:53:51 [main] INFO  com.baeldung.aspectj.AccountAspect 
- Withdrawal Rejected!
2016-11-15 22:53:51 [main] INFO  com.baeldung.aspectj.AccountAspect 
- Balance after withdrawal : 20

6. Post-Compile Weaving

6.编译后的编织

Post-compile weaving (also sometimes called binary weaving) is used to weave existing class files and JAR files. As with compile-time weaving, the aspects used for weaving may be in source or binary form, and may themselves be woven by aspects.

编译后编织(有时也称为二进制编织)是用来编织现有的类文件和JAR文件的。与编译时织入一样,用于织入的方面可以是源码或二进制形式,而且本身可以由方面织入。

In order to do this with Mojo’s AspectJ Maven Plugin we need do setup all the JAR files we would like to weave in the plugin configuration:

为了用Mojo的AspectJ Maven插件做到这一点,我们需要在插件配置中设置所有我们想要编织的JAR文件。

<configuration>
    <weaveDependencies>
        <weaveDependency>  
            <groupId>org.agroup</groupId>
            <artifactId>to-weave</artifactId>
        </weaveDependency>
        <weaveDependency>
            <groupId>org.anothergroup</groupId>
            <artifactId>gen</artifactId>
        </weaveDependency>
    </weaveDependencies>
</configuration>

The JAR files containing the classes to weave must be listed as <dependencies/> in the Maven project and listed as <weaveDependencies/> in the <configuration> of the AspectJ Maven Plugin.

包含要编织的类的JAR文件必须在Maven项目中列为<dependencies/>,并在AspectJ Maven插件的<weaveDependencies/>中列为<figuration>

7. Load-Time Weaving

7.负载时间的编织

Load-time weaving is simply binary weaving deferred until the point that a class loader loads a class file and defines the class to the JVM.

加载时编织是简单的二进制编织,推迟到类加载器加载类文件并将类定义给JVM的时候。

To support this, one or more “weaving class loaders” are required. These are either provided explicitly by the run-time environment or enabled using a “weaving agent”.

为了支持这一点,需要一个或多个 “织造类加载器”。这些都是由运行时环境明确提供的,或者通过 “织造代理 “启用。

7.1. Enabling Load-Time Weaving

7.1.启用负载时间织网

AspectJ load-time weaving can be enabled using AspectJ agent that can get involved in the class loading process and weave any types before they are defined in the VM. We specify the javaagent option to the JVM -javaagent:pathto/aspectjweaver.jar or using Maven plugin to configure the javaagent :

AspectJ加载时编织可以通过AspectJ代理启用,它可以参与类的加载过程,并在虚拟机中定义任何类型之前进行编织。我们在JVM中指定javaagent选项-javaagent:pathto/aspectjweaver.jar或使用Maven插件来配置javaagent

<plugin>
    <groupId>org.apache.maven.plugins</groupId>
    <artifactId>maven-surefire-plugin</artifactId>
    <version>2.22.2</version>
    <configuration>
        <argLine>
            -javaagent:"${settings.localRepository}"/org/aspectj/
            aspectjweaver/${aspectj.version}/
            aspectjweaver-${aspectj.version}.jar
        </argLine>
        <useSystemClassLoader>true</useSystemClassLoader>
        <forkMode>always</forkMode>
    </configuration>
</plugin>

7.2. Configuration Weaver

7.2.组态织工

AspectJ’s load-time weaving agent is configured by the use of aop.xml files. It looks for one or more aop.xml files on the classpath in the META-INF directory and aggregates the contents to determine the weaver configuration.

AspectJ的加载时织网代理是通过使用aop.xml文件来配置的。它在META-INF目录下的classpath上寻找一个或多个aop.xml文件,并汇总其内容以确定织补配置。

An aop.xml file contains two key sections:

一个 aop.xml文件包含两个关键部分。

  • Aspects: defines one or more aspects to the weaver and controls which aspects are to be used in the weaving process. The aspects element may optionally contain one or more include and exclude elements (by default, all defined aspects are used for weaving)
  • Weaver: defines weaver options to the weaver and specifies the set of types that should be woven. If no include elements are specified then all types visible to the weaver will be woven

Let’s configure an aspect to the weaver:

让我们把一个方面配置给织工。

<aspectj>
    <aspects>
        <aspect name="com.baeldung.aspectj.AccountAspect"/>
        <weaver options="-verbose -showWeaveInfo">
            <include within="com.baeldung.aspectj.*"/>
        </weaver>
    </aspects>
</aspectj>

As we can see, we have configured an aspect that points to the AccountAspect, and only the source code in the com.baeldung.aspectj package will be woven by AspectJ.

我们可以看到,我们配置了一个指向AccountAspect的方面,只有com.baeldung.aspectj包中的源代码会被AspectJ编织。

8. Annotating Aspects

8.注释的方面

In addition to the familiar AspectJ code-based style of aspect declaration, AspectJ 5 also supports an annotation-based style of aspect declaration. We informally call the set of annotations that support this development style the “@AspectJ” annotations.

除了我们熟悉的AspectJ基于代码的方面声明风格外,AspectJ 5还支持基于注解的方面声明风格。我们非正式地将支持这种开发风格的注释集称为”@AspectJ“注释。

Let’s create an annotation:

我们来创建一个注释。

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
public @interface Secured {
    public boolean isLocked() default false; 
}

We use the @Secured annotation to enable or disable a method:

我们使用@Secured注解来启用或禁用一个方法。

public class SecuredMethod {

    @Secured(isLocked = true)
    public void lockedMethod() {
    }

    @Secured(isLocked = false)
    public void unlockedMethod() {
    }
}

Next, we add an aspect using AspectJ annotation-style, and check the permission based on the attribute of the @Secured annotation:

接下来,我们使用AspectJ注解式添加一个方面,并根据@Secured注解的属性检查权限。

@Aspect
public class SecuredMethodAspect {
    @Pointcut("@annotation(secured)")
    public void callAt(Secured secured) {
    }

    @Around("callAt(secured)")
    public Object around(ProceedingJoinPoint pjp, 
      Secured secured) throws Throwable {
        return secured.isLocked() ? null : pjp.proceed();
    }
}

For more detail on AspectJ annotation-style, we can check out the following link.

关于AspectJ注解风格的更多细节,我们可以查看以下链接

Next, we weave our class and aspect using load-time weaver and put aop.xml under META-INF folder:

接下来,我们使用load-time weaver来编织我们的类和方面,并将aop.xml放在META-INF文件夹下。

<aspectj>
    <aspects>
        <aspect name="com.baeldung.aspectj.SecuredMethodAspect"/>
        <weaver options="-verbose -showWeaveInfo">
            <include within="com.baeldung.aspectj.*"/>
        </weaver>
    </aspects>
</aspectj>

Finally, we add unit test and check the result:

最后,我们添加单元测试并检查结果。

@Test
public void testMethod() throws Exception {
	SecuredMethod service = new SecuredMethod();
	service.unlockedMethod();
	service.lockedMethod();
}

When we run the test cases, we may check the console output to verify that we successfully weaved our aspect and class in the source code:

当我们运行测试用例时,我们可以检查控制台的输出,以验证我们是否成功地在源代码中编织了我们的方面和类。

[INFO] Join point 'method-call
(void com.baeldung.aspectj.SecuredMethod.unlockedMethod())'
in Type 'com.baeldung.aspectj.test.SecuredMethodTest'
(SecuredMethodTest.java:11)
advised by around advice from 'com.baeldung.aspectj.SecuredMethodAspect'
(SecuredMethodAspect.class(from SecuredMethodAspect.java))

2016-11-15 22:53:51 [main] INFO com.baeldung.aspectj.SecuredMethod 
- unlockedMethod
2016-11-15 22:53:51 [main] INFO c.b.aspectj.SecuredMethodAspect - 
public void com.baeldung.aspectj.SecuredMethod.lockedMethod() is locked

9. Conclusion

9.结论

In this article, we covered introductory concepts about AspectJ. For details, you can take a look at the AspectJ home page.

在这篇文章中,我们介绍了关于AspectJ的介绍性概念。有关细节,你可以看一下AspectJ主页。

You can find the source code for this article over on GitHub.

你可以在GitHub上找到这篇文章的源代码