春季AOP:方面@Around不起作用



我已经使用spring boot和spring initileizr制作了一个简单的Web应用程序,并尝试用 @ASPECT 使用 @AROUND 建议。

当我添加自定义注释 @retryonfailure 到控制器的端点方法 - 它可以使用,但是当我将此注释添加到控制器端点执行的控制器方法时 - 工作。我花了很多时间来理解这种行为的原因,但没有任何结果。所以请帮助。

项目:https://github.com/zalizko/spring-aop-playground

@Aspect
@Component
public final class MethodRepeater {
    @Around("execution(* *(..)) && @annotation(RetryOnFailure)")
    public Object wrap(final ProceedingJoinPoint joinPoint) throws Throwable {
        // code is here
    }
}

所以,我的目标是:

@RequestMapping
public String index() {
    inTry();
    return "OK";
}

@RetryOnFailure(attempts = 3, delay = 2, unit = TimeUnit.SECONDS)
public void inTry() {
    throw new RuntimeException("Exception in try " + ++counter);
}

您犯了一个典型的春季AOP初学者的错误:您忘记了基于代理的AOP仅在从外部调用代理方法而不是通过this调用(避免代理)时起作用。但是内部呼叫inTry()this.inTry()相同。因此,该方面永远不会触发inTry,您必须像这样重新排列您的代码:

package spring.aop;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.TimeUnit;
@RestController("/")
public class HomeController {
    static int counter = 0;
    @RequestMapping
    @RetryOnFailure(attempts = 3, delay = 2, unit = TimeUnit.SECONDS)
    public String index() {
        throw new RuntimeException("Exception in try " + ++counter);
    }
}

我也更改了一些方面,以便

  • 避免反射并将注释直接通过@annotation()
  • 绑定到建议参数
  • 在触发建议和
  • 时记录JoinPoint
  • 在尝试#3上返回"确定"(只是为了娱乐,不是必需的)。
package spring.aop;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;
@Aspect
@Component
public final class MethodRepeater {
    @Around("execution(* spring.aop..*(..)) && @annotation(retryOnFailure)")
    public Object wrap(final ProceedingJoinPoint joinPoint, RetryOnFailure retryOnFailure) throws Throwable {
        System.out.println(joinPoint);
        return proceed(joinPoint, retryOnFailure);
    }
    private Object proceed(ProceedingJoinPoint joinPoint, RetryOnFailure retryOnFailure) throws Throwable {
        int attempt = 1;
        while (true) {
            try {
                return joinPoint.proceed();
            } catch (final Throwable ex) {
                System.out.println("Try #" + attempt + " failed: " + ex);
                if (++attempt >= retryOnFailure.attempts())
                    return "OK";
                if (retryOnFailure.delay() > 0L)
                    retryOnFailure.unit().sleep(retryOnFailure.delay());
            }
        }
    }
}

现在可以正常工作,并且控制台日志说:

execution(String spring.aop.HomeController.index())
Try #1 failed: java.lang.RuntimeException: Exception in try 1
Try #2 failed: java.lang.RuntimeException: Exception in try 2

我遇到了类似的问题,我设法使用facteactj:

来解决它。

https://github.com/mdanetzky/tour-of-heroes-java

另外 - 我花了一些时间来找出我的想法没有正确地重建方面,因此在尝试一些更严厉的措施之前,可能值得尝试清洁/重建项目。

5/2023 for AspectJ

myApplication.java

@SpringBootApplication
public class AopjApplication {
  public static void main(String[] args) {
    ConfigurableApplicationContext cntx = SpringApplication.run(AopjApplication.class, args);
    A a=cntx.getBean(A.class);
    a.chirp();      
  }
}

将FactactJ添加到 build.gradle

dependencies {
  implementation 'org.springframework.boot:spring-boot-starter'
  implementation 'org.aspectj:aspectjweaver:1.9.19'  //currently last version
  testImplementation 'org.springframework.boot:spring-boot-starter-test'
}

A.Java:

public class A {
  public void chirp() {
    System.out.print(11111);
  }
}

myconfig.java

@Configuration
@EnableAspectJAutoProxy
public class MyConfig {
  @Bean
  public A a() {
    return new A();
  }
  @Bean
  public B b() {
    return new B();
  }
. . .
  @Bean
  public MyAspect myAspect() {
    return new MyAspect();
  }
}

myaspect.java

@Aspect
public class MyAspect {
  @Before("execution(* mypackage.A.chirp())")
  public void aa() {
    System.out.print(2222);
    }   
}

我可以运行它,现在控制台显示:

2023-05-21T22:47:34.035+05:00  INFO 9332 --- [           main] mypackage.AopjApplication                    : Starting AopjApplication using Java 17.0.6 with PID 9332 (C:Usersericseclipse-workspaceaopjbinmain started by erics in C:Usersericseclipse-workspaceaopj)
2023-05-21T22:47:34.045+05:00  INFO 9332 --- [           main] mypackage.AopjApplication                    : No active profile set, falling back to 1 default profile: "default"
2023-05-21T22:47:35.566+05:00  INFO 9332 --- [           main] mypackage.AopjApplication                    : Started AopjApplication in 2.33 seconds (process running for 3.303)
2222
11111

最新更新