时间:2017-03-29 11:52:41

标签: java spring spring-boot aspectj spring-aop

我希望能够跟踪使用某个注释注释的@RequestMapping所覆盖的方法(例如@LooseController)。

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

如果使用@RequestMapping注释的方法位于具有@LooseController的类中,但是如果@LooseController位于子类中,则该方法很有效

例如,当在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>

1 个答案:

答案 0 :(得分:1)

正如我在评论中所说,我必须推测:

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

我现在的假设是

  • 您使用基于代理的Spring AOP和
  • 所有类和方面都是@Component或在配置中声明为Spring bean。

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

<强>注释:

package de.scrum_master.app;

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

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

抽象基类:

package de.scrum_master.app;

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

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

子类+ 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可以拦截给定切入点的几个连接点,但Spring AOP根据documentation不支持callstaticinitialization

因此,您需要切换到AspectJ with LTW或设计另一个切入点策略。

要继续,我必须在这里打断答案一段时间,因为我有预约,但稍后会再添加一些信息。

更新:好的,这是您的问题和解决方案:@within(de.scrum_master.app.LooseController)查看带有@LooseController注释的类,但是您正在尝试使用带注释方法的父类拦截没有那个注释。因此,@within()不是适合您的切入点类型。您想要表达的是实例,即调用该方法的当前target对象,属于带注释的类。因此,您需要@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()是支持的切入点类型。