Как предотвратить ложные ложные предупреждения указателя нулевой точки при использовании CGLIB/Spring AOP?

Я использую Spring AOP и, следовательно, косвенно CGLIB в моем MVC-контроллере Spring. Поскольку 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 может быть нулевым.

Мой вопрос:

Как предотвратить эти ложноположительные предупреждения с нулевым указателем? Одним из решений было бы добавить assert this.exampleService != null или, возможно, использовать Guava Preconditions.checkNotNull(this.exampleService).

Однако это должно быть добавлено к каждому методу для каждого поля, используемого в этом методе. Я бы предпочел решение, которое я мог бы добавить в одном месте. Может быть, аннотация для конструктора по умолчанию или что-то еще?

EDIT:

Кажется, что исправлено с помощью Spring 4, однако в настоящее время я использую Spring 3: http://blog.codeleak.pl/2014/07/spring-4-cglib-based-proxy-classes-with-no-default-ctor.html

Ответ 1

Вы можете аннотировать ваше поле (если вы уверены, что оно действительно не будет равно null) с помощью

//import org.jetbrains.annotations.NotNull;
@NotNull
private final ExampleService exampleService;

Это даст указание Idea предположить, что это поле не является нулевым во всех случаях. В этом случае ваш реальный конструктор также будет автоматически аннотирован Idea:

public ExampleController(@NotNull ExampleService exampleService){
    this.exampleService = exampleService;
}

Ответ 2

Анализ статического кода IntelliJ IDEA сообщает о потенциальном 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);
}

Ответ 3

Вы можете создать новый экземпляр ExampleService по умолчанию и назначить его в конструкторе по умолчанию, а не присваивать ему значение null:

public ExampleController(){
    this.exampleService = new ExampleService();
}

или

public ExampleController(){
    this.exampleService = ExampleServiceFactory.Create();
}

Поскольку этот объект никогда не должен использоваться при нормальной работе, он не будет иметь никакого эффекта, но если объект используется каркасом или случайно используется напрямую из-за более поздних изменений кода, это даст вам больше информации в стека, чем исключение нулевого указателя, и это также решит ошибку, что this.exampleService может быть нулевым.

Это может потребовать некоторых изменений класса ExampleService, чтобы разрешить создание нового экземпляра с параметрами по умолчанию или разрешить создание нового экземпляра, который по сути является оболочкой, которая ничего не делает. Если он наследуется от базового типа интерфейса, то нефункциональный класс может наследовать от того же базового типа, что и владелец места. Этот шаблон также позволит вам ввести код обработки ошибок, чтобы обеспечить ясное предупреждение, если приложение пытается использовать нерабочий экземпляр по умолчанию.

Я обнаружил, что в таких языках, как Java и С#, где почти все есть указатель, полагаясь на нулевые указатели даже в тех областях, где их никогда не следует использовать, упрощение обслуживания становится более сложным, чем это может быть, поскольку их часто можно использовать случайно. Подходящие виртуальные машины предназначены для того, чтобы иметь эквивалент панической атаки, когда код пытается использовать нулевой указатель. Я подозреваю, что это связано с наследием C, где нулевые указатели могут действительно испортить всю запущенную программу. Из-за этой виртуальной атаки паники они не предоставляют никакой полезной информации, которая помогла бы диагностировать проблему, тем более, что значение (null) совершенно бесполезно для определения того, что произошло. Не обращая внимания на нулевые указатели, а вместо этого специально разрабатывая иерархии классов, чтобы определить, должен ли экземпляр объекта выполнять какую-либо реальную работу, вы можете избежать потенциальных проблем с нулевыми указателями, а также сделать ваш код более легким и безопасным для поддержания.