为什么在 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:
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
今天,当我尝试使用 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:
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