为什么在 AOP @pointcut 切入 FunctionalInterface 时引发 NullPointException?

Why NullPointException raised when AOP @pointcut cut into FunctionalInterface?

今天,当我尝试使用 java 中的 AOP 记录器打印包含 BiPredicate(FunctionalInterface) 的 class 的日志时,我遇到了 NullPointExcepiton。

问题:

Is there any wrong usage of FunctionalInterface with AOP?

When I set the pointCut to BiPredicateExample.java, the exception will be raised by biPredicateExample.cmp(FuntionalInterface), while the method in this class works fun(biPredicateExample.cmp1()).

可以使用以下代码重现 NullPointExcepiton:

Github repository link here

com/test/BiPredicateExample.java

import org.springframework.stereotype.Component;
import java.util.function.BiPredicate;

@Component
public class BiPredicateExample {

    public BiPredicate<Integer,Integer> cmp = (x,y) -> (x>y);

    public boolean cmp1(Integer x, Integer y){
        return x>y;
    }
}

com/logger/BiPredicateExample.java

package com.logger;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LogAspect {

    /**
     * BiPredicateExample works fine.
     */
//    @Pointcut("execution(* com.empty.*.*(..) )")

    /**
     *  It'll raise NullPointException When this PointCut cut
     *  into the FunctionalInterface.
     */
    @Pointcut("execution(* com.test.*.*(..) )")
    public void logPointCut(){}

    @Before("logPointCut()")
    public void printBeforeMethodInvoke(JoinPoint joinPoint){

    }

}

com/Application.java

package com;

import com.test.BiPredicateExample;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;


@SpringBootApplication
public class Application implements CommandLineRunner{
    @Autowired
    BiPredicateExample biPredicateExample;

    public static void main(String[]args){
        SpringApplication.run(Application.class,args);
    }

    @Override
    public void run(String... strings) throws Exception {

        boolean w=biPredicateExample.cmp1(10,2);
        System.out.println(w);

        boolean f=biPredicateExample.cmp.test(10,2);
        System.out.println(f);
    }
}

谢谢!

我的pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
         xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <groupId>groupId</groupId>
    <artifactId>AOP_FunctionalInterface_Test</artifactId>
    <version>1.0-SNAPSHOT</version>

    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starter-parent</artifactId>
        <version>2.0.3.RELEASE</version>
    </parent>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
            <version>2.0.3.RELEASE</version>
        </dependency>
    </dependencies>>


</project>

问题是成员变量cmp在原来的class中只有一个有意义的值,但是一使用SpringAOP,实际上是在和一个交互动态代理对象。 JDK 动态代理,但是,仅代理 public 方法调用(CGLIB 也受保护和包范围的方法)。没有成员变量的代理机制,您需要确保您不会通过外部成员访问直接访问内部状态,例如通过 getter 方法。

即在这种情况下,您将 biPredicateExample.cmp 更改为 biPredicateExample.getCmp(),仅此而已:

package com.test;

import org.springframework.stereotype.Component;

import java.util.function.BiPredicate;

@Component
public class BiPredicateExample {
  private BiPredicate<Integer, Integer> cmp = (x, y) -> (x > y);

  public boolean cmp1(Integer x, Integer y) {
    return x > y;
  }

  public BiPredicate<Integer, Integer> getCmp() {
    return cmp;
  }
}
package com;

import com.test.BiPredicateExample;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class Application implements CommandLineRunner {
  @Autowired
  BiPredicateExample biPredicateExample;

  public static void main(String[] args) {
    SpringApplication.run(Application.class, args);
  }

  @Override
  public void run(String... strings) throws Exception {
    System.out.println(biPredicateExample.cmp1(10, 2));
    System.out.println(biPredicateExample.getCmp().test(10, 2));
  }
}
package com.logger;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.*;
import org.springframework.stereotype.Component;

@Aspect
@Component
public class LogAspect {
  @Pointcut("execution(* com.test..*(..) )")
  public void logPointCut() {}

  @Before("logPointCut()")
  public void printBeforeMethodInvoke(JoinPoint joinPoint) {
    System.out.println(joinPoint);
  }
}

然后控制台日志变为:

  .   ____          _            __ _ _
 /\ / ___'_ __ _ _(_)_ __  __ _ \ \ \ \
( ( )\___ | '_ | '_| | '_ \/ _` | \ \ \ \
 \/  ___)| |_)| | | | | || (_| |  ) ) ) )
  '  |____| .__|_| |_|_| |_\__, | / / / /
 =========|_|==============|___/=/_/_/_/
 :: Spring Boot ::        (v2.0.3.RELEASE)

2018-06-16 14:15:10.040  INFO 12456 --- [           main] com.Application                          : Starting Application on Xander-Ultrabook with PID 12456 (C:\Users\alexa\Documents\java-src\AOP_FunctionalInterface_Test\target\classes started by alexa in C:\Users\alexa\Documents\java-src\AOP_FunctionalInterface_Test)
2018-06-16 14:15:10.056  INFO 12456 --- [           main] com.Application                          : No active profile set, falling back to default profiles: default
2018-06-16 14:15:10.118  INFO 12456 --- [           main] s.c.a.AnnotationConfigApplicationContext : Refreshing org.springframework.context.annotation.AnnotationConfigApplicationContext@3c87521: startup date [Sat Jun 16 14:15:10 ICT 2018]; root of context hierarchy
2018-06-16 14:15:11.437  INFO 12456 --- [           main] o.s.j.e.a.AnnotationMBeanExporter        : Registering beans for JMX exposure on startup
2018-06-16 14:15:11.452  INFO 12456 --- [           main] com.Application                          : Started Application in 1.724 seconds (JVM running for 2.524)
execution(boolean com.test.BiPredicateExample.cmp1(Integer,Integer))
true
execution(BiPredicate com.test.BiPredicateExample.getCmp())
true
2018-06-16 14:15:11.452  INFO 12456 --- [       Thread-5] s.c.a.AnnotationConfigApplicationContext : Closing org.springframework.context.annotation.AnnotationConfigApplicationContext@3c87521: startup date [Sat Jun 16 14:15:10 ICT 2018]; root of context hierarchy
2018-06-16 14:15:11.531  INFO 12456 --- [       Thread-5] o.s.j.e.a.AnnotationMBeanExporter        : Unregistering JMX-exposed beans on shutdown

Process finished with exit code 0