使用 CGLIB / Spring AOP 时如何防止误报空指针警告?
How to prevent false positive null pointer warnings, when using CGLIB / Spring AOP?
我正在使用 Spring AOP,因此在我的 Spring MVC 控制器中间接使用了 CGLIB。因为 CGLIB 需要一个默认构造函数,所以我包含了一个,我的控制器现在看起来像这样:
@Controller
public class ExampleController {
private final ExampleService exampleService;
public ExampleController(){
this.exampleService = null;
}
@Autowired
public ExampleController(ExampleService exampleService){
this.exampleService = exampleService;
}
@Transactional
@ResponseBody
@RequestMapping(value = "/example/foo")
public ExampleResponse profilePicture(){
return this.exampleService.foo(); // IntelliJ reports potential NPE here
}
}
现在的问题是,IntelliJ IDEA 的静态代码分析报告潜在的 NullPointerException,因为 this.exampleService
可能为 null。
我的问题是:
如何防止这些误报空指针警告?一种解决方案是添加 assert this.exampleService != null
或者使用 Guava 的 Preconditions.checkNotNull(this.exampleService)
。
但是,对于此方法中使用的每个字段,必须将其添加到每个方法中。我更喜欢可以在一个地方添加的解决方案。也许是默认构造函数上的注释或其他东西?
编辑:
似乎已通过 Spring 4 修复,但我目前使用的是 Spring 3:
http://blog.codeleak.pl/2014/07/spring-4-cglib-based-proxy-classes-with-no-default-ctor.html
您可以使用以下方式注释您的字段(如果您确定它真的不会为空):
//import org.jetbrains.annotations.NotNull;
@NotNull
private final ExampleService exampleService;
这将指示 Idea 在所有情况下都假定此字段为非空。在这种情况下,您的真实构造函数也会被 Idea 自动注释:
public ExampleController(@NotNull ExampleService exampleService){
this.exampleService = exampleService;
}
IntelliJ IDEA's static code analysis reports a potential NullPointerException
您可以使用@SuppressWarnings({"unchecked", "UnusedDeclaration"}) 或评论关闭特定字段、变量、方法等的这些报告。实际上,IDEA 本身可以建议您这个解决方案。参见 https://www.jetbrains.com/idea/help/suppressing-inspections.html
您可以为单行代码切换警告:
void foo(java.util.Set set) {
@SuppressWarnings("unchecked")
java.util.Set<String> strings = set;
System.out.println(strings);
}
您可以创建 ExampleService 的默认新实例并在默认构造函数中分配它而不是将其分配给 null:
public ExampleController(){
this.exampleService = new ExampleService();
}
或
public ExampleController(){
this.exampleService = ExampleServiceFactory.Create();
}
由于这个对象在正常运行中应该永远不会被使用,所以不会有任何影响,但是如果这个对象被框架使用,或者因为后期修改代码不小心直接使用了,这会给你更多的信息在堆栈跟踪中而不是空指针异常,这也将解决 this.exampleService can be null.
的错误
这可能需要对 ExampleService class 进行一些更改,以允许使用默认参数创建新实例,或者允许创建本质上是 shell 什么都不做的新实例。如果它继承自基本接口类型,那么非功能性 class 可以从相同的基本类型继承,特别是作为占位符。如果应用程序尝试使用默认的非功能性实例,此模式还允许您注入错误处理代码以提供明确的警告。
我发现在像 Java 和 C# 这样几乎所有东西都是指针的语言中,即使在不应该使用它们的地方依赖空指针也会使维护变得比应有的困难,因为它们经常会被意外使用。底层虚拟机被设计为在代码尝试使用空指针时产生相当于恐慌的攻击——我怀疑这是因为 C 的遗留问题,其中空指针真的会搞砸整个 运行 程序。由于这种虚拟的恐慌攻击,他们没有提供任何有助于诊断问题的有用信息,特别是因为值 (null) 对于识别发生的事情完全没有用。通过避免空指针,而是专门设计 class 层次结构来确定实例化对象是否应该执行任何实际工作,您可以避免空指针的潜在问题,并使您的代码更容易和更安全地维护。
我正在使用 Spring AOP,因此在我的 Spring MVC 控制器中间接使用了 CGLIB。因为 CGLIB 需要一个默认构造函数,所以我包含了一个,我的控制器现在看起来像这样:
@Controller
public class ExampleController {
private final ExampleService exampleService;
public ExampleController(){
this.exampleService = null;
}
@Autowired
public ExampleController(ExampleService exampleService){
this.exampleService = exampleService;
}
@Transactional
@ResponseBody
@RequestMapping(value = "/example/foo")
public ExampleResponse profilePicture(){
return this.exampleService.foo(); // IntelliJ reports potential NPE here
}
}
现在的问题是,IntelliJ IDEA 的静态代码分析报告潜在的 NullPointerException,因为 this.exampleService
可能为 null。
我的问题是:
如何防止这些误报空指针警告?一种解决方案是添加 assert this.exampleService != null
或者使用 Guava 的 Preconditions.checkNotNull(this.exampleService)
。
但是,对于此方法中使用的每个字段,必须将其添加到每个方法中。我更喜欢可以在一个地方添加的解决方案。也许是默认构造函数上的注释或其他东西?
编辑:
似乎已通过 Spring 4 修复,但我目前使用的是 Spring 3: http://blog.codeleak.pl/2014/07/spring-4-cglib-based-proxy-classes-with-no-default-ctor.html
您可以使用以下方式注释您的字段(如果您确定它真的不会为空):
//import org.jetbrains.annotations.NotNull;
@NotNull
private final ExampleService exampleService;
这将指示 Idea 在所有情况下都假定此字段为非空。在这种情况下,您的真实构造函数也会被 Idea 自动注释:
public ExampleController(@NotNull ExampleService exampleService){
this.exampleService = exampleService;
}
IntelliJ IDEA's static code analysis reports a potential NullPointerException
您可以使用@SuppressWarnings({"unchecked", "UnusedDeclaration"}) 或评论关闭特定字段、变量、方法等的这些报告。实际上,IDEA 本身可以建议您这个解决方案。参见 https://www.jetbrains.com/idea/help/suppressing-inspections.html
您可以为单行代码切换警告:
void foo(java.util.Set set) {
@SuppressWarnings("unchecked")
java.util.Set<String> strings = set;
System.out.println(strings);
}
您可以创建 ExampleService 的默认新实例并在默认构造函数中分配它而不是将其分配给 null:
public ExampleController(){
this.exampleService = new ExampleService();
}
或
public ExampleController(){
this.exampleService = ExampleServiceFactory.Create();
}
由于这个对象在正常运行中应该永远不会被使用,所以不会有任何影响,但是如果这个对象被框架使用,或者因为后期修改代码不小心直接使用了,这会给你更多的信息在堆栈跟踪中而不是空指针异常,这也将解决 this.exampleService can be null.
的错误这可能需要对 ExampleService class 进行一些更改,以允许使用默认参数创建新实例,或者允许创建本质上是 shell 什么都不做的新实例。如果它继承自基本接口类型,那么非功能性 class 可以从相同的基本类型继承,特别是作为占位符。如果应用程序尝试使用默认的非功能性实例,此模式还允许您注入错误处理代码以提供明确的警告。
我发现在像 Java 和 C# 这样几乎所有东西都是指针的语言中,即使在不应该使用它们的地方依赖空指针也会使维护变得比应有的困难,因为它们经常会被意外使用。底层虚拟机被设计为在代码尝试使用空指针时产生相当于恐慌的攻击——我怀疑这是因为 C 的遗留问题,其中空指针真的会搞砸整个 运行 程序。由于这种虚拟的恐慌攻击,他们没有提供任何有助于诊断问题的有用信息,特别是因为值 (null) 对于识别发生的事情完全没有用。通过避免空指针,而是专门设计 class 层次结构来确定实例化对象是否应该执行任何实际工作,您可以避免空指针的潜在问题,并使您的代码更容易和更安全地维护。