Spring Autowired и Builder аннотации - PullRequest
       6

Spring Autowired и Builder аннотации

0 голосов
/ 01 апреля 2020

У меня есть пружинный класс AppUser, который имеет аннотированное поле @Autowired (FriendList) следующим образом:

@Getter
@Setter
@Builder
@Document(collection = "app_users")
@Component
public class AppUser {

  @Id
  @NotBlank(message = ErrorConstants.ANDROID_USER_ACCOUNT_MANAGER_ID_IS_NULL)
  private String androidUserAccountManagerId;

  @NotBlank(message = ErrorConstants.NULL_NAME)
  private String name;

  private Friend bestFriend;

  @Autowired
  @Setter(AccessLevel.NONE)
  private FriendList friendList;

  private boolean manualBestFriendOverride;

  public Optional<Friend> getFriend(String friendName) {
    return friendList.getFriend(friendName);
  }

  public void calculateBestFriend() {
    if (!manualBestFriendOverride) {
      bestFriend = friendList.calculateAndReturnBestFriend();
    }
  }
}

Идея заключалась в том, что при каждом создании нового AppUser он будет @Autowire одинаковым по умолчанию friendList должен начинаться с нуля.

AppUser создается в методе WebController:

  @PostMapping("/{androidUserAccountManagerId}/setNewAppUser/{appUserName}")
  public void setNewAppUser(
      @NotNull @PathVariable String androidUserAccountManagerId,
      @NotNull @PathVariable @Pattern(regexp = "^[A-z]{2,20}$", message = "Incorrect name format!")
          String appUserName) {
    databaseQueryClass.save(
        AppUser.builder()
            .androidUserAccountManagerId(androidUserAccountManagerId)
            .name(appUserName)
            .build());
  }

Однако я получаю исключение нулевого указателя, возникающее при вызове поля FriendList в другие методы. По сути, кажется, что автопроводка не работает в связке со сборщиком AppUser.

После некоторого чтения кажется плохой практикой вызывать new или build и autowire поле в создаваемом классе из-за Io C и Dependency Injection, отсортированные по Spring.

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

Спасибо за вашу помощь заранее.

Класс FriendList для контекста:

@ToString
@EqualsAndHashCode
@Builder
public class FriendList {

  @NotNull private List<Friend> listOfFriends;

  public Optional<Friend> getFriend(String friendName) {
    return listOfFriends.stream().filter(o -> o.getName().equals(friendName)).findAny();
  }

  public Friend calculateAndReturnBestFriend() {
    if(listOfFriends.isEmpty()){
      return null;
    }

    java.util.Collections.sort(listOfFriends);
    return listOfFriends.get(0);
  }

  public boolean addToFriendList(String friendName) {
    if (getFriend(friendName).isPresent()) {
      return false;
    }
    return listOfFriends.add(
        Friend.builder().name(friendName).meetingDates(new TreeSet<>()).meetUpNumber(0).build());
  }
}

РЕДАКТИРОВАТЬ (Извините, что пропустил этот контекст ...)

У меня определен бин в классе AppConfig:

@Configuration
public class AppConfig {

  @Bean
  @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE)
  public FriendList getFriendList() {
    return FriendList.builder().listOfFriends(new ArrayList<>()).build();
  }
}

Ответы [ 2 ]

1 голос
/ 01 апреля 2020

Относительно вашей проблемы @ Autowired можно использовать нового оператора или строителя, если вы позволите контейнеру Spring управлять этим новым компонентом для вас.

Например, вы можете сделать что-то вроде:

@Configuration
public class AppConfig {
    @Bean
    public FriendList firendList() {
        return FriendList.builder()
                      //...
                    .build());
    }
}

И потом использовать это так: @Import(AppConfig.class)

Обратите внимание , что вышеприведенное будет работать только если FriendList создается в начале как значение по умолчанию со всей доступной информацией в этот момент. А также он будет работать только с компонентом AppUser , который управляется Spring. Если вы создаете AppUser с помощью компоновщика в контроллере, вам нужно @Autowired FriendList в контроллере и установить его в компоновщике.

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

Вы также можете использовать @ Autowired в этом сценарии, но это более сложно, и вам нужно будет автоматически связать список друзей:

@Autowired
private List<Friend> friends;

И каждый Друг должен создаваться динамически. Примерно так:

Friend friend = new Friend(); 
context.registerBean(Friend.class, () -> friend);  //context here is an ApplicationContext type aka Spring Container.

Также проверьте этот пример с ObjectProvider из здесь .

0 голосов
/ 02 апреля 2020

Считаете ли вы, что в вашем приложении список друзей AppUser может состоять только из других AppUsers?

В этом случае это особый случай двунаправленных отношений в JPA, когда сущность на обоих концах одинакова.

@Entity
public class AppUser {

    @ManyToOne
    private AppUser parent;

    @OneToMany(mappedBy="parent")
    private Collection<AppUser> children;

}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...