spring 启动前的 AspectJ

AspectJ before spring boot starts

假设我有一个 spring 启动应用程序

@SpringBootApplication
public abstract class AbstractMicroServer {

    public static void main(final String[] args) {
        // here some asppect should start
        final SpringApplication app = new SpringApplication(AbstractMicroServer.class);
        app.run(args);
    }
}

我的方面Jclass

@Aspect
public class AOP{

    @Pointcut("execution(* org.springframework.context.annotation.CommonAnnotationBeanPostProcessor.ignoreResourceType(..)")
    public void intercept() {
    }

    @Around("intercept()")
    public Object intercept(final ProceedingJoinPoint joinPoint) throws Throwable {
        return joinPoint.getArgs();
    }
}

我希望这个方面在 spring 引导开始之前就绪。这可能是我想做的吗?由于某种我不知道的原因,该方面没有被拦截。

POM

        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjrt</artifactId>
            <version>1.8.12</version><!--$NO-MVN-MAN-VER$ -->
        </dependency>
        <dependency>
            <groupId>org.aspectj</groupId>
            <artifactId>aspectjweaver</artifactId>
            <version>1.8.12</version><!--$NO-MVN-MAN-VER$-->
        </dependency>

      <build>
        <pluginManagement>
            <plugins>
                <plugin>
                    <groupId>org.codehaus.mojo</groupId>
                    <artifactId>aspectj-maven-plugin</artifactId>
                    <version>1.10</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>
                        <includes>
                            <include>**/*.java</include>
                            <include>**/*.aj</include>
                        </includes>
                    </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>
            </plugins>
        </pluginManagement>
    </build>

我没有使用 spring boot starter aop。以上是我对 aop 的所有配置和代码。

在我看来,您想拦截 Spring 类 中的方法执行。为此,您有两个选择:

  • 使用加载时间编织(-javaagent:/path/to/aspectjweaver-<version>.jar 作为 JVM 参数)
  • 在构建时编织您的 Spring 库 类 以创建库的特殊编织版本 (see relevant aspectj-maven-plugin documentation),并使用生成的编织 类 而不是原来的 spring jar 文件。您生成的配置类似于:

    <plugin>
        <groupId>org.codehaus.mojo</groupId>
        <artifactId>aspectj-maven-plugin</artifactId>
        <configuration>
            <weaveDependencies>
                <weaveDependency>
                    <groupId>org.agroup</groupId>
                    <artifactId>to-weave</artifactId>
                </weaveDependency>
                <weaveDependency>
                    <groupId>org.anothergroup</groupId>
                    <artifactId>gen</artifactId>
                </weaveDependency>
            </weaveDependencies>
        </configuration>
    </plugin>
    

Spring AOP

运行时编织

由于 Spring 框架基于代理的性质,Spring AOP 基于 运行时编织 。这意味着目标 class 在 Spring 运行时变成了代理。

在大多数情况下,Spring 框架不会代理其框架 classes。您感兴趣的 class CommonAnnotationBeanPostProcessor 未被代理。因此,Spring AOP 无法拦截对 CommonAnnotationBeanPostProcessor.

ignoreResourceType 方法的任何调用

然而,这并不意味着你运气不好。您仍然可以利用 AspectJ 的 二进制编织.

AspectJ

二进制编织

在二进制编织中,目标和方面源代码(*.java)分别编译成二进制classes(.class)。然后将二进制 classes 与 AspectJ 编译器 (ajc) 编织在一起。

在您的情况下,方面源代码 (AOP.java) 将使用 AspectJ 编译器编译成二进制 class (AOP.class)。 AOP.class和现有的Spring、class、CommonAnnotationBeanPostProcessor.class将被编织到一起成为一个新编织的CommonAnnotationBeanPostProcessor.class

编织前的代码

这里是ignoreResourceType方法的代码片段 class CommonAnnotationBeanPostProcessor,

public void ignoreResourceType(String resourceType) {
        Assert.notNull(resourceType, "Ignored resource type must not be null");
        this.ignoredResourceTypes.add(resourceType);
}

编织后的代码

现在,注意方法被AspectJ编织后的变化。

public void ignoreResourceType(String resourceType) {
        JoinPoint var3 = Factory.makeJP(ajc$tjp_0, this, this, resourceType);
        SpringFrameworkClassAspect var10000 = SpringFrameworkClassAspect.aspectOf();
        Object[] var4 = new Object[]{this, resourceType, var3};
        var10000.adviceAround((new CommonAnnotationBeanPostProcessor$AjcClosure1(var4)).linkClosureAndJoinPoint(69648));
}

如何实现AspectJ二进制编织?

  • 您可以在Mojo's AspectJ Maven plugin的帮助下生成二进制编织。
  • 你仍然需要你的方面class。
  • 确保在 weaveDependencies 下包含 spring-context 依赖项。

以下是您项目 pom.xml 的插件部分的摘录。您可以找到完整的工作示例 here.

        <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>aspectj-maven-plugin</artifactId>
            <version>1.10</version>
            <configuration>
                <showWeaveInfo/>
                <source>1.8</source>
                <target>1.8</target>
                <complianceLevel>${java.version}</complianceLevel>
                <Xlint>ignore</Xlint>
                <forceAjcCompile>true</forceAjcCompile>
                <sources/>
                <weaveDirectories>
                    <weaveDirectory>${project.build.directory}/classes</weaveDirectory>
                </weaveDirectories>
                <weaveDependencies>
                    <dependency>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring-context</artifactId>
                    </dependency>
                </weaveDependencies>
                <aspectLibraries>
                    <aspectLibrary>
                        <groupId>org.springframework</groupId>
                        <artifactId>spring-aspects</artifactId>
                    </aspectLibrary>
                </aspectLibraries>
            </configuration>
            <executions>
                <execution>
                    <goals>
                        <goal>compile</goal>
                        <goal>test-compile</goal>
                    </goals>
                </execution>
            </executions>
            <dependencies>
                <dependency>
                    <groupId>org.aspectj</groupId>
                    <artifactId>aspectjrt</artifactId>
                    <version>${aspectj.version}</version>
                </dependency>
                <dependency>
                    <groupId>org.aspectj</groupId>
                    <artifactId>aspectjtools</artifactId>
                    <version>${aspectj.version}</version>
                </dependency>
            </dependencies>
        </plugin>