Pull to refresh

Comments 17

Ещё много изменений произошло в Actuator.

Знаю один из проектов, который ещё не переехал на СБ2 именно из-за Актуатора.


Кстати, раз уж речь зашла от миграции: отдельная и интересная тема — переезд с голого Спринга на Спринг Бут. Так на одном из проектов столкнулись с тем, что перестал работать откат транзакций при выбрасывании проверяемых исключений. Когда-то давно возникла необходимость откатывать спринговую транзакцию при выбрасывании этого исключения. Это было сделано с помощью AnnotationTransactionAttributeSource примерно вот так:


public class CustomAttributeSource extends AnnotationTransactionAttributeSource {
  @Override
  protected TransactionAttribute determineTransactionAttribute(AnnotatedElement element) {
    return new DefaultTransactionAttribute() {
      @Override
      public boolean rollbackOn(Throwable ex) {
        return super.rollbackOn(ex) || ex instanceof CustomCheckedException;
      }
    };
  }
}

На чистом Спринге работало, после переезда на СБ1 транзакции откатываться перестали.

Про Gradle не понял. Ни разу не приходило в голову применить boot плагин для всех подпроектов без разбора… А если там вообще нет java кода?

Зависит от проекта. В нашем случае он состоит только из Java-подпроектов, лишь один из которых не является Spring Boot приложением. А всё, что не на Java, мы держим в других репозиториях.

ещё не переехал на СБ2 именно из-за Актуатора

А что конкретно не дает переехать? Интересно узнавать о новых граблях от коллег по цеху.


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


  • Если не заходит, то стоит покопать в сторону того, как регистрируется CustomAttributeSource: точно ли его видят регистрирующие бины (в том числе самописные), и точно ли эти бины сами активны (намекаю на соответствующие классы *AutoConfiguration).
  • Если же выполнение заходит сюда, то надо убедиться, не обёрнуто ли целевое исключение в какое-либо другое (каковых в Spring'е целая иерархия). В случае обёртки (да и не только в нем) следует проверять на instanceof не само исключение, а его корень, например, через Throwables#getRootCause() из Guava.

Точно не скажу, разговор был формата курилки. Спросил про миграцию на СБ2, вылезло несколько проблем, а поскольку задача была не очень важной, то занимались ей в перерывах между основной работой.


Вот что запомнилось точно — проблемы со Спринг Датой. Начиная с версии 2 изменились названия ключевых методов в JpaRepository:


interface JpaRepository<T, ID> {
  //было
  <T> T findOne(ID id);
  <T> List<T> findAll(Iterable<ID> ids);

  //стало
  <T> Optional<T> findById(ID id);
  <T> List<T> findAllById(Iterable<ID> ids);
}

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


//основа для всех репозиториев
@NoRepositoryBean
public interface BaseJpaRepository<T, ID extends Serializable> extends JpaRepository<T, ID> {
  // определяем метод findOne, аналогичный тёзке, существовавшему в версиях 1.*
  @Deprecated
  T findOne(ID id);

  // тоже для findAll
  @Deprecated
  List<T> findAll(Iterable<ID> ids);
}

public class BaseJpaRepositoryImpl<T, ID extends Serializable> extends SimpleJpaRepository<T, ID> implements BaseJpaRepository<T, ID> {

  private JpaEntityInformation<T, ?> entityInfo;
  private EntityManager entityManager;

  public BaseJpaRepositoryImpl(JpaEntityInformation<T, ?> entityInfo, EntityManager entityManager) {
    super(entityInfo, entityManager);
    this.entityInfo = entityInfo;
    this.entityManager = entityManager;
  }

  //обеспечиваем совместимость поведения
  @Override
  public T findOne(ID id) {
    return findById(id).orElse(null);
  }

  @Override
  public List<T> findAll(Iterable<ID> ids) {
    return findAllById(ids);
  }
}

//какой-нибудь репозиторий
interface SomeRepository extends BaseJpaRepository<SomeEntiy, Lond> {
}

И о чудо! Все ошибки компиляции исчезают, код работает как и прежде, а изменено всего 2 класса, а не 200. Теперь можно неспешно заменять устаревшее АПИ на новое, благо "Идея" заботливо подсветит все вызовы помеченные @Deprecated.

Спасибо, что поделились!
У меня был схожий опыт с JPA, только нужно было отвязать прикладной код от походов в БД статическими методами Play 1.x, чтобы покрыть код unit-тестами без PowerMockito. Впрочем, тогда машстаб вышел более широким — таки пришлось задеть пару сотен классов. К счастью, правки оказались простыми.
Не за что, мне очень понравились ваш доклад и эта статья, по его следам думаю написать статью про Спринг Бут и некоторые его особенности.
Держите в курсе — с удовольствием почитаю и поделюсь с коллегами.
Можно было просто
  default T findOne(ID id) {
    return findById(id).orElse(null);
  }

в BaseJpaRepository добавить.
Можно и так, правда, в этом случае часть методов, объявленных в интерфейсе и тело будет иметь только в интерфейсе, другая же часть будет реализована в классе. ИМХО, лучше уже всё делать единообразно.

Спасибо! Очень понравился стиль изложения!


Честно, устал от косноязычных переводов про спринг бут на Хабре… Эта статья прям как глоток свежего воздуха!

Мне все больше и больше кажется что Spring это котел с ЧОРНОЙ МАГИЕЙ и контролировать ее становится все трудней.

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

Ну вот, пора уже про версию 3 повторить!

Sign up to leave a comment.