繁体   English   中英

在 spring 启动应用程序中集成 Spring AOP 作为 maven 依赖项

[英]Integrate Spring AOP as a maven dependency in a spring boot application

应用程序1:(包含AOP代码。一个Spring启动应用程序)

LogAspect.Java

@Aspect
@Component
public class LogAspect {
    private final Logger log = LoggerFactory.getLogger(this.getClass());

    @Pointcut("within(@org.springframework.stereotype.Repository *)"
            + " || within(@org.springframework.stereotype.Service *)"
            + " || within(@org.springframework.web.bind.annotation.RestController *)")
    public void springBeanPointcut() {}

    @Pointcut("within(com.akpanda.springmain..*)")
    public void applicationPackagePointcut() {}

    @Around("applicationPackagePointcut() && springBeanPointcut()")
    public void logAround(ProceedingJoinPoint joinPoint) throws Throwable {
        CodeSignature c = (CodeSignature) joinPoint.getSignature();
        log.info("Enter: {}.{}() with ParameterName[s]= {} and argumentValue[s]= {}",
                joinPoint.getSignature().getDeclaringTypeName(), joinPoint.getSignature().getName(),
                Arrays.toString(c.getParameterNames()), Arrays.toString(joinPoint.getArgs()));
    }
}

pom.xml(应用一)

<groupId>com.akpanda</groupId>
  <artifactId>aop</artifactId>
  <version>0.0.1-SNAPSHOT</version>
  <name>aop</name>
  <description>Demo project for Spring Boot</description>
  <properties>
    <java.version>11</java.version>
  </properties>
  <dependencies>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter</artifactId>
    </dependency>

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

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

  </dependencies>

Application 2: (Another spring boot application) 它对上面的Application 1有依赖,添加为maven依赖

pom.xml(应用2)

<dependency>
    <groupId>com.akpanda</groupId>
    <artifactId>aop</artifactId>
    <version>0.0.1-SNAPSHOT</version>
</dependency>

申请2主class

@SpringBootApplication
@EnableAspectJAutoProxy(proxyTargetClass = true)
public class SpringmainApplication {
    public static void main(String[] args) {
        SpringApplication.run(SpringmainApplication.class, args);
    }
}

申请2的Controller class

@RestController
public class FeesController {
    @Autowired
    private FeesCalculator feesCalculator;

    @RequestMapping(value = "/fees/caclulate", method = RequestMethod.GET)
    public Fees calculateFees() {
        return feesCalculator.calculateFees();
    }
}

现在,当我运行应用程序 2 并命中端点时

/费用/计算

,我希望在 maven 中添加的 aop 依赖项能够启动。只要调用应用程序 2 的任何方法,就会调用方法 logAround() 。

如果我不将应用程序 1 添加为依赖项,而只是在包含方面和建议的应用程序 2 中创建 class,它就可以工作。 但在那种情况下,我有 10 个不同的项目需要复制。 我想将 AOP 代码分离为一个模块(另一个 spring 引导应用程序),并将其作为 maven 依赖项用于许多不同的 spring 引导项目。

就像我在评论中说的:

为什么你的方面模块中有整个 Spring 应用程序的依赖项? 是不是有点过分了? 每个其他想要使用你的方面库的模块都会得到大量的传递依赖。 您应该隔离方面库并在两个应用程序中使用它。

但是,如果我们暂时忽略这一点,那么应用您的方面应该没有任何问题。 也许您在项目 B 中的主要 class 是com.akpanda.springmain.SpringmainApplication ,但您的方面在另一个基础 package 中,比如com.akpanda.aop.LogAspect 在这种情况下,它不会被组件扫描拾取。 通过将其添加到您的应用程序 class 中很容易解决这个问题:

@ComponentScan(basePackages = { "com.akpanda.aop", "com.akpanda.springmain" })

然后该方面将立即工作。 但是看看你的建议方法,它有问题:

  1. 您正在使用返回void@Around建议,即只有将它应用于也返回void的方法才有意义。 但是,您的目标方法会返回Fees 为了适应环绕通知中的任何返回类型,请将返回类型更改为Object
  2. around-advice 不返回任何东西,它也不proceed()到原始目标方法,返回它的结果。

您可以这样修复它:

  @Around("applicationPackagePointcut() && springBeanPointcut()")
  public Object logAround(ProceedingJoinPoint joinPoint) throws Throwable {
    CodeSignature signature = (CodeSignature) joinPoint.getSignature();
    log.info("Enter: {}.{}() with ParameterName[s]= {} and argumentValue[s]= {}",
      signature.getDeclaringTypeName(), signature.getName(),
      Arrays.toString(signature.getParameterNames()), Arrays.toString(joinPoint.getArgs()));
    return joinPoint.proceed();
  }

或者,如果您的建议仅记录并且不修改和方法参数或返回值,也不进行任何异常处理,只需使用@Before建议,那么您将返回到void方法,无需proceed()或声明Throwable

  @Before("applicationPackagePointcut() && springBeanPointcut()")
  public void logBefore(JoinPoint joinPoint) {
    CodeSignature signature = (CodeSignature) joinPoint.getSignature();
    log.info("Enter: {}.{}() with ParameterName[s]= {} and argumentValue[s]= {}",
      signature.getDeclaringTypeName(), signature.getName(),
      Arrays.toString(signature.getParameterNames()), Arrays.toString(joinPoint.getArgs()));
  }

顺便说一句,您还可以使用@within而不是within来更简单地编写切入点:

  @Pointcut(
    "@within(org.springframework.stereotype.Repository)" +
    " || @within(org.springframework.stereotype.Service)" +
    " || @within(org.springframework.web.bind.annotation.RestController)"
  )
  public void springBeanPointcut() {}

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM