2016-02-09 9 views
10

Я использую Spring AOP и поэтому косвенно CGLIB в моем контроллере Spring MVC. Поскольку CGLIB нужен конструктор по умолчанию на меня включен один и мой контроллер теперь выглядит следующим образом:Как предотвратить ложные положительные предупреждения указателя нулевой точки при использовании CGLIB/Spring AOP?

@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's Preconditions.checkNotNull(this.exampleService).

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

EDIT:

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

+1

Должен ли этот конструктор быть вызываемым, или он просто должен существовать? Не могли бы вы исключить из него исключение? – chrylis

+0

Одним из вариантов является использование искателя AspectJ во время компиляции или времени выполнения, а не cglib. Весенние документы объясняют, как это сделать. Одним из преимуществ этого подхода является то, что он позволяет осуществлять инъекцию зависимостей и инструментальные средства объектов домена, которые обычно бывают «анемичными». Другим является обратный класс, который будет оснащаться интерфейсом и, следовательно, использовать динамический прокси, а не cglib - в зависимости от характера класса это может быть хорошей практикой в ​​любом случае. –

+0

Проблема существует, если присутствует 'setter' для' exampleService'? – 11thdimension

ответ

3

Вы можете аннотировать поле (если вы уверены, что это будет на самом деле не be null) с:

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

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

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

Я думаю, что это так хорошо, как и получается. Я надеялся на что-то вроде аннотации @ @ IgnoreNotUsed, чтобы добавить к конструктору без аргументов, но в конце концов это не так уж плохо ... –

0
статический анализ кода

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); 
} 
+0

Потенциальные ошибки NullPointerExceptions - весьма полезны, и я не хочу полностью отключать или подавлять его –

+0

Вы можете отключить его для определенной строки вашего кода, а не для всего класса исключений –

+0

Как я писал в своем вопросе, я мог бы использовать 'assert this.exampleService! = Null', который гораздо более выразителен, чем' @ SuppressWarnings'. Однако я не хочу добавлять утверждения повсюду. –

0

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

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

или

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

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

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

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