Статьи

FindBugs и JSR-305

Предположим, что группа разработчиков работает параллельно над частями большого проекта — некоторые разработчики работают над реализацией сервиса, а другие работают над кодом, использующим этот сервис. Обе группы договорились о сервисном API и начали работать отдельно, имея в виду предположения об API…

Как вы думаете, у этой истории будет счастливый конец? Ну, … — может быть 🙂 — есть инструменты, которые могут помочь достичь этого 🙂 — одним из них является FindBugs , поддерживаемый с JSR-305 (аннотации для обнаружения дефектов программного обеспечения).

Давайте посмотрим на контракт API сервиса:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package com.blogspot.vardlokkur.services;
 
import java.util.List;
 
import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
 
import com.blogspot.vardlokkur.entities.domain.Employer;
 
/**
 * Defines the API contract for the employer service.
 *
 * @author Warlock
 * @since 1.0
 */
public interface EmployerService {
 
    /**
     * @param identifier the employer's identifier
     * @return the employer having specified {@code identifier}, {@code null} if not found
     */
    @CheckForNull Employer withId(@Nonnull Long identifier);
 
    /**
     * @param specification defines which employers should be returned
     * @return the list of employers matching specification
     */
    @Nonnull List thatAre(@Nonnull Specification specification);
 
}

Как видите, к сигнатурам метода службы добавлены аннотации, такие как @ Nonnull или @ CheckForNull . Цель их использования состоит в том, чтобы определить требования к параметрам метода (например, параметр identifier не может быть нулевым ), а ожидания для значений, возвращаемых методами (например, результат метода службы, могут быть нулевыми, и вы должны проверить это в своем коде ).

Ну и что? — спросите вы, — должен ли я сам проверить их в коде или доверить коллегам, что они будут использовать руководящие принципы, определенные этими аннотациями? Конечно, нет 🙂 — никому не доверяйте, используйте инструменты, которые проверят предположения API, такие как FindBugs .

Предположим, что у нас есть следующий сервис API:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package com.blogspot.vardlokkur.test;
 
import org.junit.Before;
import org.junit.Test;
 
import com.blogspot.vardlokkur.services.EmployerService;
import com.blogspot.vardlokkur.services.impl.DefaultEmployerService;
 
/**
 * Employer service test.
 *
 * @author Warlock
 * @since 1.0
 */
public class EmployerServiceTest {
 
    private EmployerService employers;
 
    @Before
    public void before() {
        employers = new DefaultEmployerService();
    }
 
    @Test
    public void test01() {
        Long identifier = null;
        employers.withId(identifier);
    }
 
    @Test
    public void test02() {
        employers.withId(Long.valueOf(1L)).getBusinessName();
    }
 
    @Test
    public void test03() {
        employers.thatAre(null);
    }
}

Давайте попробуем проверить код в соответствии с предположениями API сервиса:

FindBugs проанализирует ваш код и переключится на перспективу FindBugs, показывая потенциальные проблемы:

Null передан для ненулевого параметра
Возможно разыменование нулевого указателя

Аналогичным образом, ребята, пишущие служебный код, могут проверить свою работу в соответствии с определенными допущениями API, например. если вы запустите FindBugs для самой ранней версии реализации сервиса:

01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package com.blogspot.vardlokkur.services.impl;
 
import java.util.List;
 
import com.blogspot.vardlokkur.entities.domain.Employer;
import com.blogspot.vardlokkur.services.EmployerService;
import com.blogspot.vardlokkur.services.Specification;
 
/**
 * Default implementation of {@link EmployerService}.
 *
 * @author Warlock
 * @since 1.0
 */
public class DefaultEmployerService implements EmployerService {
 
    /**
     * {@inheritDoc}
     */
    public Employer withId(Long identifier) {
        return null;
    }
 
    /**
     * {@inheritDoc}
     */
    public List thatAre(Specification specification) {
        return null;
    }
 
}

Следующая ошибка будет найдена:

Как видите, ничто не может спрятаться от FindBugs и его союзника — JSR-305;)

Несколько ссылок на десерт:

Справка: FindBugs и JSR-305 от нашего партнера JCG   Миха? Ja? Tak в блоге Мысли Чернокнижника