简体   繁体   中英

Integrate Spring AOP as a maven dependency in a spring boot application

Application 1: (Contains AOP code. A Spring boot application)

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 (Application 1)

<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) It has a dependency to the above Application 1, added as a maven dependency

pom.xml (Application 2)

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

Main class of Application 2

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

Controller class of Application 2

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

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

Now when i run Application 2 and hit endpoint

/fees/caclulate

, I expect the aop dependency added in maven to kick in. and the method logAround() to be called whenever any method of Application 2 is called.

If i do not add application 1 as a dependency but simply create class in application 2 containing aspects and advices it works. But in that case I have 10 different projects for which this needs to be duplicated. I want to separate the AOP code as a module (another spring boot application) and use it in many different spring boot projects as a maven dependency.

Like I said in my comment:

Why do you have the dependencies for a whole Spring application in your aspect module? Isn't that a bit too much? Every other module that wants to use your aspect library get tons of transitive dependencies. You should isolate the aspect library and use it in both applications.

But if we ignore this for a moment, there should not be any problem for your aspect to be applied. Maybe your main class in project B is com.akpanda.springmain.SpringmainApplication , but your aspect is in another base package, say com.akpanda.aop.LogAspect . In that case, it will not be picked up by component scan. That is easy enough to fix by adding this to your application class:

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

Then the aspect will work immediately. But looking at your advice method, there are problems with it:

  1. You are using an @Around advice which returns void , ie it only makes sense to apply it to methods which also return void . Your target method however returns Fees . In order to accomodate any return type in an around-advice, change the return type to Object .
  2. The around-advice does not return anything and it also does not proceed() to the original target method, returning its result.

You can fix it like this:

  @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();
  }

Or alternatively, if your advice only logs and does not modify and method parameters or return values and also does not do any exception handling, simply use a @Before advice instead, then you are back to a void method there is no need to proceed() or declare 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()));
  }

BTW, you could also write your pointcut a little simpler using @within instead of within :

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

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