Pull to refresh

Comments 11

Позволю себе заметить, что пример с V6082 в Гласфише таки некорректный. Ошибка тут не возникнет никогда, потому как в контейнере правила доступа к сервису несколько другие, чем обычно. Вызывается он через прокси, который для синглтонов делает некоторую дополнительную магию. Так что все найденные вами «10 проблемных мест» конкретно для Гласфиша на самом деле не проблемные.


Лучше выберите какой-нибудь другой пример, там где синглтоны вызываются напрямую.

А можно, пожалуйста, немного подробнее про магию? Глассфишем пользоваться не приходилось, но если, ткнув в потолок, предположить, что DI-контейнер вызывает метод init перед публикацией синглтона или прокси синхронизирует все обращения к isAnnotation, то очень странным выглядит вообще использование DCL. Причём сама реализация (прошу поправить, если не прав) этого паттерна некорректна — что как раз эта диагностика и рассматривает.

Ну тут автор кода скорее всего применил паттерн по привычке, хотя было совершенно незачем. Сет можно было в конструкторе проинициализировать, а не выпендриваться с ленивостью, контейнер в этом случае гарантирует однократность. И зачем он сделал так — непонятно.


Честно говоря, я сам уже давненько в кишках гласфиша не копался, но лет пять тому потратил порядочно времени на разбор как оно себя ведет под отладчиком, потому как писал свой контейнер по мотивам, но попроще.

Так в итоге-то разве ошибка не может возникнуть именно из-за некорректной ленивой инициализации сета, которая тут и не нужна вовсе? Два разных потока одновременно позовут isAnnotation и, пока один заполняет сет, другой возвращает ложно-отрицательный результат, увидев (полу)пустую коллекцию. Другое дело, конечно, если прокси засинхронизирует каждый вызов этого метода, но такой сценарий мне кажется сомнительным. Ну, или если метод гарантированно вызывается всегда из одного и того же потока.

Смысл этого примера, кстати, не в кидании камней в Глассфиш. Он тут исключительно для демонстрации диагностики и очень удачно совпало, что сразу попалось несколько ошибок в одном отрывке кода.

У меня return, кстати, уехал при форматировании не в тот блок, поправил.

Мой поинт в том, что давать пример на коде метода EJB, который выполняется в контейнере — некорректно. Потому что контейнер определяет свои правила вызовов таких методов.


Он как раз для этого и предназначен, чтобы правила переопределять. Весь этот наш набор трёхбуквенных J** аббревиатур, AOP, IOC, и прочая энтерпрайзщина определяют свои паттерны вызовов, блокировок, многопоточности, даже вызовов с другого узла в контексте того же приложения. А вы здесь рассуждаете без учёта всего этого контекста. Но так нельзя. Контекст важен.


Например, некоторые контейнеры умеют делать short-circuit calls в том случае, если экземпляры бинов расположены на одном узле, и вообще не вставляют прокси, а прямо дёргают соответствующий метод. Но это обычно явно настраивается. В обычном же случае всем рулит конкретная реализация прокси, которая может уметь и кешировать объект, и создавать на лету, и делать с ним абсолютно что попало.


Если хочется давать пример, то надо давать его на подходящем материале :) Какой-нить standalone Java app, без контейнеров и их заранее неизвестной логики.

Вы серьёзно вручную просматриваете ВСЕ предупреждения, появившиеся и исчезнувшие после каждого коммита? Это же 95% времени разработчиков займёт, как это организовано?

Да, просматриваются все предупреждения. Иначе никак. Это не так страшно как кажется, но действительно требует время на постоянный анализ изменений.
Как один из этих самых разработчиков, дополню комментарий Андрея попыткой объяснить, почему просмотр диффов — не больно.

Для начала: у нас нет самоцели «выпускать по N диагностик в неделю». Мы пытаемся брать качеством, а не количеством, и хотим, чтобы диагностиками было приятно пользоваться. Это я к тому, что время, затраченное на их реализацию, не столь критично (в разумных пределах, конечно).

Предупреждения почти всегда лежат на поверхности и нечасто требуют сверх-глубокого анализа. Самих диффов тоже не настолько много, насколько может показаться: вполне обыденной ситуацией являются 0-5 срабатываний для специфичных диагностик и 20-40 для более общих в момент отправки диагностики в master (да, по всем проектам селфтестера в сумме). Перед отбором признаков ложных срабатываний может быть и целая куча диффов (200+, например), но эти признаки, опять же, обычно лежат на поверхности и сильно в них всматриваться не приходится. После этого количество диффов начинает уменьшаться чуть ли не экспоненциально.

Основная боль процесса разработки — ожидание этих самых диффов, которое может занимать много времени. Но оно не требует нашего участия, и в это время можно:
  • Позаниматься документацией;
  • Пообщаться с командой;
  • Причесать код;
  • Поработать над другой диагностикой;
  • Отвлечься на написание статьи (вроде этой);
  • Поревьюить код/диффы товарища по команде;
  • И прочее.


Соотношение потраченного времени на просмотр диффов и на всё остальное (кодинг, документация, ...), по ощущениям, где-то 25/75.

Код в примере с EjbComponentAnnotationScanner очень странный: зачем там вообще нужно ленивое создание? Ради набора из 4 коротких и по умолчанию разных строк? Для этого вовсе не обязательно городить HashSet, Arrays.asList() будет легковеснее, а поиск строки в массиве длинной 4 скорее всего будет быстрее, чем вычисление хэш-кода, потом поиск нужной корзины и сравнение с находящейся там строкой. Объявив поле private volatile Set<String> annotations как private final List<String> annotations мы получим гарантированное безопасное создание и отсутствие волатильного доступа при чтении.

В примере с Glassfish мне показалось странным, почему синхронизация идёт на классе, а не на this. Метод-то не статический. И если сделать несколько экземпляров класса, то эти синхронизации будут мешать друг другу. Разве что если сам этот класс — синглтон, и второго экземпляра там точно не будет.

К слову, если в сканере аннотаций ввести ключевое слово volatile в объявление


private Set<String> annotations=null;

то явное присваивание нула (значение поля по умолчанию) нужно выбросить.

Sign up to leave a comment.