java—不能为同一方面类的方法设置通知

zqdjd7g9  于 2021-07-06  发布在  Java
关注(0)|答案(2)|浏览(238)

我正在尝试用aspectj学习springaop,通过构建一个小型银行事务模拟。但是我无法向aspect类本身的方法添加建议(@before,@after,@afterthrowing)。
这就是模型
银行.java

@Component
public class Bank {
    private int balance;
    private int pinCode;
    private int tempPin;

    public int getBalance() {
        return balance;
    }

    @Value("10000")
    public void setBalance(int balance) {
        this.balance = balance;
    }

    public int getPinCode() {
        return pinCode;
    }

    @Value("6789")
    public void setPinCode(int pinCode) {
        this.pinCode = pinCode;
    }

    public int getTempPin() {
        return tempPin;
    }

    public void setTempPin(int tempPin) {
        this.tempPin = tempPin;
    }

    public void withDraw(int amount) {
        if (amount <= balance) {
            balance -= amount;
            System.out.println("Successful Withdraw");
        } else {
            System.out.println("Insufficient Balance");
        }
    }
}

这是aspect类
bankaspect.java版本

@Component
@Aspect
public class BankAspect {
    private Bank bank;

    public Bank getBank() {
        return bank;
    }

    @Autowired
    public void setBank(Bank bank) {
        this.bank = bank;
    }

    @Before("execution(public void dev.ritam.model.Bank.withDraw(..))")
    public void validatePin() {
        if (bank.getPinCode() != bank.getTempPin()) {
            throw new RuntimeException();
        }
    }

    @AfterThrowing("execution(public void dev.ritam.aspect.BankAspect.validatePin(..))")
    public void logException() {
        System.out.println("Wrong Pin");
    }
}

这是配置类
appconfig.java文件

@Configuration
@EnableAspectJAutoProxy
@ComponentScan("dev.ritam")
public class AppConfig {
    @Bean
    Bank bank() {
        return new Bank();
    }

    @Bean
    BankAspect bankAspect() {
        return new BankAspect();
    }
}

这是主要的方法
应用程序.java

public class App {
    public static void main(String[] args) {
        ApplicationContext context = new AnnotationConfigApplicationContext(AppConfig.class);
        Bank bank = context.getBean(Bank.class);

        try {
            bank.setTempPin(1234);
            bank.withDraw(1000000);
        } catch (Exception ignore) {
        }
    }
}

仅限
validatePin() @Before 建议正在被执行。我想得到'错误的引脚'作为输出,但 @AfterThrowing 建议不被认可。

xxls0lw8

xxls0lw81#

参考文件:
建议其他方面?在springaop中,方面本身不能成为来自其他方面的建议的目标。类上的@aspect注解将其标记为方面,因此将其从自动代理中排除。
您不能在springaop中建议一个方面,这就是 @AfterThrowing 不执行。
要抛出异常并记录消息,可以建议使用相同的方法 Bank.withDraw()@AfterThrowing . 那样的话 validatePin() 建议将在 Bank.withDraw() 以及 logException() 如果验证引发异常,则将执行通知。这是@dreamcash的答案中提到的第二个选项。
示例代码

@Before("execution(public void dev.ritam.model.Bank.withDraw(..))")
public void validatePin() {
    if (bank.getPinCode() != bank.getTempPin()) {
        throw new RuntimeException();
    }
}

@AfterThrowing("execution(public void dev.ritam.model.Bank.withDraw(..))")
public void logException() {
    System.out.println("Wrong Pin");
}

这将导致以下控制台输出序列

Wrong Pin
java.lang.RuntimeException

几个要点要考虑。 Bank 以及 BankAspect 用注解 @Component 以及 @ComponentScan 将它们注册为bean。也就是说豆子注册了 @Bean 在appconfig中不需要。两者中的任何一个都是必需的。
@dreamcrash的答案中提到的最后一个选项在springaop中不受支持:请参阅从其他切入点类型开始的部分
更新:
这是一个棘手的问题,我建议的答案是基于springboot版本:2.2.6.release,它使用版本:5.2.5.release的spring库
op共享的代码基于spring版本:5.3.1,建议没有按预期执行。
共享代码中的spring aop建议按预期工作,spring版本应该是<=5.2.6.release
请从更新pom.xml条目

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>5.3.1</version>
</dependency>

<dependency>
  <groupId>org.springframework</groupId>
  <artifactId>spring-context</artifactId>
  <version>5.2.6.RELEASE</version>
</dependency>

为了快速参考,下面是op的代码,它不适用于这个版本 5.3.1 ```
@After("execution(public void dev.ritam.model.Bank.setTempPin(..))")
public void validatePin() {
if (bank.getPinCode() != bank.getTempPin()) {
throw new RuntimeException("Wrong Pin");
} else {
System.out.println("Correct Pin");
}
}

@AfterThrowing(value = "execution(public void dev.ritam.model.Bank.setTempPin(..))", throwing = "e")
public void logException(Exception e) {
System.out.println(e.getMessage());
}

不确定是否记录了这种行为变化,或者这是一个bug。任何进一步的信息,这将更新,如果我发现任何。
信息:
这似乎是一个框架问题。更多详情请参见:
https://github.com/spring-projects/spring-framework/issues/26202
参考文件中的相关位,
从springframework5.2.7开始,在同一@aspect类中定义的需要在同一连接点上运行的advice方法根据其advice类型按以下顺序分配优先级:从最高到最低优先级:@about、@before、@after、@afterreturning、@afterthrowing。但是请注意,由于spring的aspectjafteradvice中的实现风格,在同一方面中的任何@afterreturning或@afterhrowing advice方法之后,@after advice方法将被有效地调用。
r7knjye2

r7knjye22#

方法 public void validatePin() (在课堂上) BankAspect )作为建议;因此,如果你使用 AspectJ 单凭你自己,你不可能以你所做的方式截取它(例如,你需要使用切入点) adviceexecution ). 如果是 Spring AOP 事情更加严格; Spring AOP 不允许方面成为建议的目标。
要解决您的问题,您可以截取与切入点之前截取的方法相同的方法,即:

@AfterThrowing("execution(public void dev.ritam.model.Bank.withDraw(..))")
public void logException() {
    System.out.println("Wrong Pin");
}

因为异常将在方法的范围内引发 Bank.withDraw 你的 @AfterThrowing 切入点将能够捕获该异常。
仅适用于aspectj
一个简单的解决方案是 Package 以下代码:

if (bank.getPinCode() != bank.getTempPin()) {
        throw new RuntimeException();
    }

到方法中,例如:

public void check_pin_code() {
    if (bank.getPinCode() != bank.getTempPin()) {
        throw new RuntimeException();
    }
}

现在截取该方法:

@AfterThrowing("execution(public void dev.ritam.aspect.BankAspect.check_pin_code(..))")
public void logException() {
    System.out.println("Wrong Pin");
}

另一种可供选择的方法是使用 adviceexecution() ```
@AfterThrowing("within(BankAspect) && adviceexecution()")
public void logException() {
System.out.println("Wrong Pin");
}

这将截获在 `BankAspect` 结果引发了一个异常。

相关问题