Spring AOP 默默地忽略切入点错误而不是升级它们

问题描述 投票:0回答:1

我有一个使用 AOP 的项目——一个示例程序——它可以工作。我创建了一个以相同的方式实现 AOP 的项目(据我所知......),但不起作用。

pom.xml

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework</groupId>
            <artifactId>spring-aspects</artifactId>
            <version>6.0.11</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>
    </dependencies>

AppConfig.java

@Configuration
@ComponentScan(basePackages = {
        "com.phil.cardgame.aspects",
        "com.phil.cardgame.service"
})
@EnableAspectJAutoProxy()
public class AppConfig {
}

EventAspect.java

@Aspect
@Component
public class EventAspect {
    @Autowired
    EventRepository eventRepository;

    //@Around("execute(* com.phil.cardgame.service.GameService.createGame())")
    //@Around("execute(* com.phil.cardgame.service.*.*(..))")
    @Around("invalid text")
    public long registerCreateGame(ProceedingJoinPoint joinPoint) throws Throwable{
        String action = joinPoint.getSignature().toString();
        long gameId = (long) joinPoint.proceed();
        eventRepository.addEvent(action,gameId,null);
        System.out.println(">>> got here");
        return gameId;
    }

在EventAspect.java中,您可以看到我尝试过的注释@Around建议。最后一个(无效)不会产生错误。如果我在工作项目中尝试相同的操作,则会收到错误:“切入点格式不正确”。所以看起来我的 EventAspect 没有作为一个方面被检查。

我检查了许多其他类似的问题,但我仍然看不出我做错了什么 - 有什么想法吗?

aop aspectj spring-aop spring-aspects
1个回答
1
投票

我克隆了你的项目。你的方面有效。尽管与您的问题无关,但只是为了好玩让我们

  • 将建议类型从
    @Around
    更改为
    @AfterReturning
    ,假设您只想记录成功的呼叫;否则,您将混合使用
    @Around
    加上 try-finally 作为第一个建议,并使用
    @After
    作为其他建议,
  • 分解一些常见功能以减少代码重复,
  • 添加一些日志记录,
package com.phil.cardgame.aspects;

import com.phil.cardgame.model.Deck;
import com.phil.cardgame.repository.EventRepository;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.annotation.Pointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class EventAspect {
  @Autowired
  EventRepository eventRepository;

  @Pointcut("within(com.phil.cardgame.service.GameService)")
  public void inGameService() {}

  @AfterReturning(value = "inGameService() && execution(* createGame())", returning = "gameId")
  public void registerCreateGame(JoinPoint joinPoint, Long gameId) {
    addEvent(joinPoint, gameId, null);
  }

  @AfterReturning("inGameService() && execution(* *(..)) && args(gameId, playerId)")
  public void registerPlayerAction(JoinPoint joinPoint, long gameId, String playerId) {
    addEvent(joinPoint, gameId, playerId);
  }

  @AfterReturning("inGameService() && execution(* *(..)) && args(gameId, deck)")
  public void registerDeckAction(JoinPoint joinPoint, long gameId, Deck deck) {
    addEvent(joinPoint, gameId, null);
  }

  @AfterReturning("inGameService() && execution(* *(..)) && args(gameId)")
  public void registerGameAction(JoinPoint joinPoint, long gameId) {
    addEvent(joinPoint, gameId, null);
  }

  private void addEvent(JoinPoint joinPoint, long gameId, String playerId) {
    System.out.println(joinPoint);
    eventRepository.addEvent(joinPoint.getSignature().toString(), gameId, playerId);
  }
}

现在,像这样更改主程序:

package com.phil.cardgame;

import com.phil.cardgame.service.GameService;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;

@SpringBootApplication
public class CardgameApplication {
  public static void main(String[] args) {
    try (ConfigurableApplicationContext context = SpringApplication.run(CardgameApplication.class, args)) {
      GameService gameService = context.getBean(GameService.class);
      gameService.createGame();
      gameService.addDeckToGame(1, gameService.createDeck());
      gameService.addPlayerToGame(1, "jane");
      gameService.findGame(1);
    }
  }
}

控制台日志将显示:

execution(Long com.phil.cardgame.service.GameService.createGame())
execution(void com.phil.cardgame.service.GameService.addDeckToGame(long,Deck))
execution(Player com.phil.cardgame.service.GameService.addPlayerToGame(long,String))
execution(Game com.phil.cardgame.service.GameService.findGame(long))

对我来说,看起来一切都很顺利。


更新:在Spring Boot中,像

@Around(crepe)
这样的无效切入点语法会被默默地忽略,但是如果您希望看到“切入点格式不正确”日志输出,您可以设置

logging.level.org.springframework.aop=debug

application.properties
。然后,在控制台上您将看到类似的内容(去掉时间戳):

DEBUG 20508 --- [cardgame] [           main] o.s.a.aspectj.AspectJExpressionPointcut  : Pointcut parser rejected expression [crepe]: java.lang.IllegalArgumentException: Pointcut is not well-formed: expecting '(' at character position 0
crepe
^

更新2:您的切入点错误在其他应用程序中升级但不在您的重现器中升级的原因是,在前者中您使用Spring版本< 6.1.8 and commit 617833b,“防御性捕获并记录切入点解析异常”。以前,Spring 并不像最新版本那样捕获和记录切入点错误。要重现该问题,只需在 POM 中切换到 Spring Boot 3.2.5 或更早版本即可。

© www.soinside.com 2019 - 2024. All rights reserved.