Аргументы против аннотаций

Моя команда переместилась на Spring 3.0, и есть некоторые люди, которые хотят переместить все в аннотации. Я получаю очень плохое чувство в своей кишке (запах кода?), Когда я вижу класс, который имеет такие методы: (просто пример - не все реальные аннотации)

@Transaction
@Method("GET")
@PathElement("time")
@PathElement("date")
@Autowired
@Secure("ROLE_ADMIN")
public void manage(@Qualifier('time')int time) {
...
}

Я только что позади, или все это кажется ужасной идеей для кого-то еще? Вместо того, чтобы использовать OO-концепции, такие как наследование и полиморфизм, все теперь по соглашению или через аннотации. Мне это не нравится. Необходимо перекомпилировать весь код, чтобы изменить то, что конфигурация IMO кажется неправильной. Но похоже, что все (особенно Spring) идет. Должен ли я просто "справиться с этим" или мне нужно отступить и попытаться как можно скорее сохранить наш код как аннотацию?

Ответы

Ответ 1

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

Лично я чувствую то же самое, что и вы, я бы предпочел оставить конфигурацию (например, определения транзакций, элементы пути, URL-адреса, на которые должен быть сопоставлен контроллер и т.д.) вне самой базы кода и во внешнем Spring XML-контекстные файлы.

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

Ответ 2

Возможно, у вас есть проблема с избыточными аннотациями, которые находятся по всему коду. С мета-аннотации могут быть заменены избыточные аннотации, а ваши аннотации по крайней мере DRY.

Из Spring Блог:

@Service
@Scope("request")
@Transactional(rollbackFor=Exception.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyService {
}

@MyService
public class RewardsService {
…
}

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

Ответ 3

Я также скептически относился к аннотациям, но, видя их в использовании, они могут быть замечательными. Они также могут быть использованы.

Главное, чтобы помнить об аннотации, это то, что они статичны. Они не могут меняться во время выполнения. Любой другой способ настройки (xml, самоописание в коде, независимо от того) не страдает от этого. Я видел, что люди здесь, у SO, имеют проблемы с Spring с точки зрения наличия тестовой среды при инъецировании тестовых конфигураций и необходимости сбрасывать ее на XML, чтобы сделать это.

XML не является полиморфным, унаследованным или чем-либо еще, так что это не шаг назад в этом смысле.

Преимущество аннотаций заключается в том, что он может дать вам более статическую проверку вашей конфигурации и может избежать большого количества подробностей и трудностей с координацией в конфигурациях XML (в основном сохраняя вещи DRY).

Как и в случае с XML, аннотации могут быть использованы. Главное - сбалансировать потребности и преимущества каждого из них. Аннотации, в той степени, в которой они дают вам менее подробный и DRY-код, являются инструментом, который можно использовать.

EDIT: что касается комментария об аннотации, заменяющей интерфейс или абстрактный класс, я думаю, что это может быть разумным на границе рамки. В рамках, предназначенных для использования сотнями, если не тысячами проектов, наличие интерфейса или базового класса может действительно искажать вещи (особенно базовый класс, хотя, если вы можете сделать это с аннотациями, нет причин, по которым вы не могли бы сделать этого это с обычным интерфейсом.

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

Теперь с JUnit4 вы можете иметь разные методы @Before в разных классах иерархии, и они могут быть независимыми друг от друга. Существует не менее DRY способ сделать это без аннотаций.

С точки зрения разработчиков JUnit это катастрофа. Гораздо лучше иметь определенный тип, который вы можете вызвать setUp и teardown. Но фреймворк не существует для удобства разработчика фреймворка, он существует для удобства пользователя фреймворка.

Все это применимо, если вашему коду не нужно заботиться о типе (то есть в вашем примере ничто не будет по-прежнему использовать тип контроллера). Тогда вы даже можете сказать, что реализация интерфейса фреймворка более непроницаема, чем наложение аннотации.

Если, однако, вы собираетесь писать код, чтобы прочитать эту аннотацию в своем собственном проекте, запустите далеко.

Ответ 4

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

Учитывая, что, по моему опыту, конфигурация на основе XML не очень хороша. Поэтому, чтобы процитировать South Park, вы выбираете между гигантским душем и сэндвичем t * rd.

Я думаю, что главное решение, которое вам нужно сделать, - это то, насколько вам удобнее иметь делокацию конфигурации spring (т.е. поддерживать два файла вместо одного), и используете ли вы инструменты или плагины IDE, которые приносят пользу из аннотаций. Еще один важный вопрос заключается в том, будут ли разработчики, которые будут использовать или поддерживать ваш код, действительно понимают аннотации.

Ответ 5

Проверьте эти ответы на похожие вопросы.

Каковы преимущества/недостатки аннотаций (не компилятор) по сравнению с файлами конфигурации xml

Конфигурация Xml и настройка на основе аннотаций

В основном это сводится к: Используйте оба. У обоих есть свои дела. Не используйте аннотации для вещей, которые должны оставаться конфигурируемыми без повторной компиляции всего (особенно того, что может быть у вас должен быть настроен пользователь без необходимости перекомпилировать все)

Ответ 6

Я думаю, что аннотации хороши, если они используются с мерой. Аннотации, подобные @WebService, много работают при развертывании и времени выполнения, но они не вмешиваются в класс. @Cachexxx или @Transactional явно препятствуют созданию прокси и множеству артефактов, но я думаю, что они находятся под контролем.

Вещь начинает испортиться при использовании Hibernate или JPA с аннотациями и CDI. Аннотации вырастают много.

IMO @Service и @Repository являются помехами Spring в вашем коде приложения. Они делают ваше приложение Spring зависимым и только для Spring использования.

Случай Spring Data Graph - это еще одна история. @NodeEntity, например, добавлять методы к классу во время сборки, чтобы сохранить объект домена. Если у вас нет плагинов Eclipse и Spring, вы будете ошибаться, потому что эти методы не существуют в исходном коде.

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

Я думаю, что путь Spring идет не так; главным образом потому, что в некоторых случаях нет другого способа делать такие забавные вещи. Как будто Spring хочет сделать xtreme кодирование и в то же время блокирует разработчиков в Spring framework. Вероятно, язык Java нуждается в другом способе сделать некоторые вещи.

Ответ 7

Аннотации должны использоваться экономно. Они хороши для некоторых, но не для всех. По крайней мере, xml-конфигурационный подход сохраняет конфигурацию в одном файле (или нескольких) вместо распространения по всему месту. Это представило бы (как мне это нравится) организацию дерьмового кода. Вы никогда не увидите полную картину конфигурации, если она распространяется по сотням файлов.

Ответ 8

Я думаю, это зависит от того, когда вы начали программировать. Лично я думаю, что они ужасны. В первую очередь потому, что у них есть некоторые квазиизлучения, которые вы не поймете, если вам не известно об этой аннотации. Таким образом, они образуют новый язык программирования сами по себе и отталкивают вас от POJO. По сравнению с (скажем) обычным старым кодом OO. Вторая причина - они могут помешать компилятору выполнять вашу работу за вас. Если у меня есть большая база кода и вы хотите что-то реорганизовать или переименовать что-то, я бы идеально хотел, чтобы компилятор выбрал все, что нужно изменить, или насколько это возможно. Аннотации должны быть только такими. Аннотации. Не является центральным для поведения вашего кода. Они были разработаны изначально, чтобы быть необязательно опущены при компиляции, которая сообщает вам все, что вам нужно знать.

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

Учитывая выбор, я предпочел бы ужасные старые удаленные/домашние интерфейсы J2EE и т.д. (так критиковали люди из

К сожалению, аннотации модные. Таким образом, вам будет нелегко предотвратить использование вашей команды, если вы не находитесь в обзорах/стандартах кода и т.п. (Также, из моды!)

Я читал, что Stroustup оставил аннотации из С++, опасаясь, что они будут неправильно использованы. Иногда вещи идут в неправильном направлении на протяжении десятилетий, но вы можете надеяться, что все будет в полном объеме вовремя.

Ответ 9

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

Позвольте мне объяснить. Например, я рад, что вы упомянули аннотацию @Transactional. Большинство разработчиков Spring, вероятно, будут знать и использовать @Transactional. Но сколько из этих разработчиков знают, как работает @Transactional? И разве они знают, как создавать и управлять транзакцией, не используя аннотацию @Transactional? Использование @Transactional упрощает использование транзакций в большинстве случаев, но в некоторых случаях, когда мне требуется более мелкомасштабный контроль над транзакцией, он скрывает эти данные от меня. Таким образом, это меч с двойным острием.

Другим примером является @Profile в классах Spring config. В общем случае упрощается указывать, какие профили вы хотите загружать в компонент Spring. Однако, если вам нужна более мощная логика, чем просто указание списка профилей, для которого вы хотите загрузить компонент, вы бы чтобы получить объект Environment самостоятельно и написать функцию для этого. Опять же, большинство разработчиков Spring, вероятно, будут знакомы с @Profile, но побочным эффектом этого является то, что они стали менее знакомы с деталями работы, такими как функция Environment.acceptsProfiles(String... profiles), например.

Наконец, когда аннотации не работают, может быть труднее понять, почему, и вы не можете просто поставить точку останова в аннотации. (Например, если вы забыли @EnableTransactionManagement в своем конфиге, что произойдет?) Вы должны найти обработчик аннотации и отладить это. При подходе к функции вы можете, конечно, просто поставить точку останова в функции.

Ответ 10

В аннотации часто вводятся зависимости, в которых такие зависимости не принадлежат.

У меня есть класс, который по совпадению имеет свойства, которые напоминают атрибуты из таблицы в схеме РСУБД. Класс был создан с учетом этого сопоставления. Очевидно, что существует связь между классом и таблицей, но я счастлив сохранить класс свободным от любых метаданных, объявляющих эти отношения. Правильно ли, что этот класс ссылается на таблицу и ее столбцы в совершенно другой системе? Я, конечно, не возражаю против внешних метаданных, которые связывают эти два и оставляют каждого свободным от понимания другого. Что я получил? Это не так, как если бы метаданные в исходном коде обеспечивали безопасность типов или соответствие отображения. Любой инструмент проверки, который мог бы анализировать аннотации JPA, мог одинаково хорошо анализировать файлы сопоставления спящего режима. Аннотации не помогли.

По одному контракту я создал модуль maven с пакетом реализаций интерфейсов из существующего пакета. К сожалению, этот новый пакет был одним из многих каталогов в монолитной сборке; Я видел это как нечто отдельное от другого кода. Тем не менее, команда использовала сканирование классов, поэтому мне пришлось использовать аннотации, чтобы подключить мой компонент к системе. Здесь мне не нужна централизованная конфигурация; Мне просто нужна внешняя конфигурация. Конфигурация XML не была идеальной, поскольку она объединяла проводки зависимостей с созданием компонентов. Учитывая, что Род Джонсон не верил в компонентное развитие, это было справедливо. Тем не менее, я снова почувствовал, что аннотации не помогли мне.

Противопоставьте это чему-то, что меня не беспокоит: тесты TestNG и JUnit. Я использую аннотации здесь, потому что я пишу этот тест, зная, что я использую TestNG или JUnit. Если я заменю один на другой, я понимаю, что мне придется выполнить дорогостоящий переход, который будет отклоняться от перезаписи тестов.

По какой-то причине я принимаю, что TestNG, JUnit, QUnit, unittest и NUnit принадлежат мои тестовые классы. Ни при каких обстоятельствах ни JPA, ни Hibernate не владеют этими классами доменов, которые, как оказалось, сопоставляются с таблицами. Ни при каких обстоятельствах Spring не владеет моими услугами. Я контролирую свою логическую и физическую упаковку, чтобы изолировать единицы, которые зависят от них. Я хочу обеспечить, чтобы переход от одного не оставил меня калекой из-за всех зависимостей, которые он оставил. Говорить до свидания всегда легче, чем уходить. В какой-то момент необходимо оставить.

Ответ 11

Это 2018 год и этот момент все еще актуален.

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

Аннотации были введены, чтобы сделать язык более декларативным и менее программным. Но если вы перемещаете большую часть функциональности в аннотации, вы фактически переключаете свой код на другой язык (и при этом не очень хороший). Там очень мало проверки во время компиляции. Эта статья делает то же самое: https://blog.softwaremill.com/the-case-against-annotations-4b2fb170ed67

Вся эвристика "переместить все в конфигурацию, чтобы людям не нужно было учиться кодировать" вышла из-под контроля. Инженерные менеджеры не думают.

Исключения:

  • JUnit
  • JAX-RS

Ответ 12

В моем опыте аннотации плохие:

  • Невозможность обеспечить безопасность типов в аннотациях
  • Проблемы сериализации
  • Кросс-компиляция (например, для JavaScript) может быть проблемой.
  • Библиотеки/структуры, требующие аннотации, исключают неаннотированные классы из внешних библиотек.
  • не заменяемый или взаимозаменяемый
  • Ваши проекты в конечном итоге становятся сильно зависимыми от системы, которая требует аннотации

Если бы в Java было что-то вроде "литералов метода", вы могли бы аннотировать класс в соответствующем классе аннотаций. Примерно так: например, javax.persistence и следующий аннотированный класс:

@Entity
class Person
{
    @Column
    private String firstname;
    public String getFirstname() { return firstname; }
    public void setFirstname(String value) { firstname = value; }

    @Column
    private String surname;
    public String getSurname() { return surname; }
    public void setSurname(String value) { surname = value; }

}

Вместо аннотаций я бы предложил такой класс отображения:

class PersonEntity extends Entity<Person> {
    @Override
    public Class<Person> getEntityClass() { return Person.class;}

    @Override
    public Collection<PersistentProperty> getPersistentProperties() {
         LinkedList<PersistentProperty> result = new LinkedList<>();
         result.add(new PersistentProperty<Person>(Person#getFirstname, Person#setFirstname);
         result.add(new PersistentProperty<Person>(Person#getSurname, Person#setSurname);
         return result;
    }
}

Вымышленный знак "#" в этом псевдо-Java-коде представляет литерал метода, который при вызове экземпляра данного класса вызывает соответствующий делегат (подписанный "::" начиная с java 8) этого экземпляра. Класс "PersistentProperty" должен иметь возможность принудительно применять литералы метода, ссылающиеся на данный универсальный аргумент, в данном случае класс Person.

Таким образом, вы получаете больше преимуществ, чем могут предоставить аннотации (например, создание подкласса "аннотирования" -class), и у вас нет ни одного из вышеупомянутых недостатков. У вас также может быть больше подходов к конкретным доменам. Единственные предварительные аннотации имеют то, что с аннотациями вы можете быстро увидеть, забыли ли вы включить свойство/метод. Но это также может быть обработано более кратко и более корректно с лучшей поддержкой метаданных в Java (например, что-то вроде обязательного/необязательного, как в Protocolbuffers)