class 或 subclass 的方面注释为

Aspect for class or subclass annotated with

我希望能够跟踪使用@RequestMapping 进行注释的方法,这些方法使用特定注释(例如@LooseController)进行注释。

我有两个切入点:requestMappings() 和 looseController()

如果使用@RequestMapping 注释的方法位于具有@LooseController 的class 中,但如果@LooseController 位于子class

中,则此方法效果很好

例如,当在 Controller1 上调用 update(id) 时,它不会被这方面捕获


更新以包含更多信息:

package de.scrum_master.app;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class MyAspect {

    @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
    private static void requestMapping() {}

    @Pointcut("@within(de.scrum_master.app.LooseController)")
    private static void looseController() {}

//    @Pointcut("@this(de.scrum_master.app.LooseController)")
//    private static void looseController() {}


    @Before("requestMapping() && looseController()")
    public void myAdvice(JoinPoint thisJoinPoint) {
        System.out.println(thisJoinPoint);
    }

}

package de.scrum_master.app;

import org.springframework.web.bind.annotation.RequestMapping;

//@LooseController
public abstract class PutController {

    @RequestMapping("/{id}")
    public void update(String id) {
    }

}

package de.scrum_master.app;

import java.lang.annotation.Retention;

import static java.lang.annotation.RetentionPolicy.RUNTIME;

@Retention(RUNTIME)
public @interface LooseController {
}

package de.scrum_master.app;

import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;

@Component
@LooseController
@RequestMapping("/something")
public class Controller1 extends PutController {
}

package de.scrum_master.app;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class AspectApplication implements CommandLineRunner {

    @Autowired
    private Controller1 controller1;

    @Autowired
    private ConfigurableApplicationContext context;

    public static void main(String[] args) {
        SpringApplication.run(AspectApplication.class, "--logging.level.root=WARN", "--spring.main.banner-mode=off");
    }

    @Override
    public void run(String... strings) throws Exception {
        controller1.update("test");
        context.close();
    }

}

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>1.5.2.RELEASE</version>
        <relativePath/> <!-- lookup parent from repository -->
    </parent>

    <groupId>aspects</groupId>
    <artifactId>aspects</artifactId>
    <version>1.0-SNAPSHOT</version>

    <properties>
        <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding>
        <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding>
        <java.version>1.8</java.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>

    </dependencies>

</project>

正如我在评论中所说,我不得不推测:

  • 它以您的确切切入点(您没有显示)开始
  • 并继续询问您是使用 Spring AOP 还是通过 LTW(加载时织入)的完整 AspectJ。
    • 在前一种情况下,您不解释您的目标 class 是否是实际的 Spring bean,因为 Spring AOP 只能代理 Spring beans/components .
    • 在后一种情况下,您有更多选择,但必须以不同的方式配置您的应用程序。
  • 您也不会显示您自己的注释的实现,尤其是如果它具有所需的运行时保留。

我现在的假设是

  • 您使用基于代理的 Spring AOP 和
  • 所有 class 元素和方面都是 @Component 或以其他方式在您的配置中声明为 Spring bean。

但是为了向您展示发生了什么,我将使用 AspectJ 的独立 Java 示例,而不是 Spring 应用程序。我只将 spring-web.jarsprint-context.jar 放在我的 class 路径上,以便解析 Spring 注释。

译注:

package de.scrum_master.app;

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

@Retention(RetentionPolicy.RUNTIME)
public @interface LooseController {}

抽象基础class:

package de.scrum_master.app;

import org.springframework.web.bind.annotation.RequestMapping;

public abstract class PutController {
  @RequestMapping("/{id}")
  public void update(String id) {}
}

Subclass + main 方法:

package de.scrum_master.app;

import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.RequestMapping;

@Component
@LooseController
@RequestMapping("/something")
public class Controller1 extends PutController {
  public static void main(String[] args) {
    new Controller1().update("foo");
  }
}

我猜你可能会用到的方面:

请注意,您自己的切入点 within(@blabla.LooseController) 在语法上无效,这就是我将其更改为 @within(blabla.LooseController) 的原因。

package de.scrum_master.aspect;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Before;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class MyAspect {
  @Pointcut("@annotation(org.springframework.web.bind.annotation.RequestMapping)")
  private static void requestMapping() {}

  @Pointcut("@within(de.scrum_master.app.LooseController)")
  private static void looseController() {}

  @Before("requestMapping() && looseController()")
  public void myAdvice(JoinPoint thisJoinPoint) {
    System.out.println(thisJoinPoint);
  }
}

当 运行 Controller1.main:

时的控制台日志
staticinitialization(de.scrum_master.app.Controller1.<clinit>)
call(void de.scrum_master.app.Controller1.update(String))

现在你看到问题了:AspectJ 可以拦截给定切入点的几个连接点,但是根据 [=33],Spring AOP 不支持 callstaticinitialization =].

所以要么你需要切换到 AspectJ with LTW 要么设计另一个切入点策略。

未完待续,因为我有预约,我不得不在这里打断一下回答,但稍后会添加更多信息。


更新: 好的,这是您的问题和解决方案:@within(de.scrum_master.app.LooseController) 使用 @LooseController 注释查看 classes,但是带有您要拦截的注释方法的父 class 没有该注释。因此,@within() 不是适合您的切入点类型。您想表达的是实例,即调用该方法的当前 target 对象属于带注释的 class。因此,你需要一个 @target() 切入点:

@Pointcut("@target(de.scrum_master.app.LooseController)")
private static void looseController() {}

或者,您也可以使用此解决方法(有点难看,但也有效):

@Pointcut("target(@de.scrum_master.app.LooseController Object)")
private static void looseController() {}

现在控制台日志显示:

execution(void de.scrum_master.app.PutController.update(String))

这也应该在 Spring AOP 中工作,因为 execution() 是那里支持的切入点类型。