简体   繁体   English

Spring Autowired 和 Builder 注释

[英]Spring Autowired and Builder annotations

I have a spring AppUser class that has an @Autowired annotated field (FriendList) as so:我有一个 Spring 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();
    }
  }
}

The idea was that every time a new AppUser was created, it would @Autowire the same default friendList to start from scratch.这个想法是每次创建一个新的 AppUser 时,它都会@Autowire 从头开始​​使用相同的默认朋友列表。

The AppUser is being created in the WebController method: 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());
  }

However, I am getting a null pointer exception thrown from when the friendList field is being called in other methods.但是,当在其他方法中调用friendList 字段时,我收到了一个空指针异常。 Essentially the autowiring doesn't seem to be working in conjunction with the AppUser builder.本质上,自动装配似乎没有与 AppUser 构建器一起工作。

After some reading, it seems this is bad practice to call new or build and autowire a field in the class being instantiated, due to IoC and Dependency Injection being sorted by Spring.经过一些阅读后,由于 IoC 和依赖注入由 Spring 排序,因此在正在实例化的类中调用 new 或 build 并自动装配一个字段似乎是不好的做法。

My question is, how do I get round this design wise?我的问题是,我如何明智地绕过这个设计? Is this possible to do?这是可能的吗? Or should I stop trying to autowire a field in the new instance I have to create?或者我应该停止尝试在我必须创建的新实例中自动装配一个字段? Alternatively is there a way that I can autowire the new user when this endpoint is hit?或者,有没有一种方法可以在遇到此端点时自动连接新用户?

Thanks for your help in advance.提前感谢您的帮助。

The FriendList class for context:用于上下文的 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());
  }
}

EDIT (Sorry I missed this context...)编辑(对不起,我错过了这个上下文......)

I have a bean defined in an AppConfig class:我在 AppConfig 类中定义了一个 bean:

@Configuration
public class AppConfig {

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

Regarding your @Autowired issue, is fine to use a new operator or a builder, as long as you let spring container manage that new bean for you.关于您的@Autowired问题,只要您让 spring 容器为您管理该新 bean,就可以使用新的运算符或构建器。

For example you could do something like:例如,您可以执行以下操作:

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

And afterwards use it like so : @Import(AppConfig.class)然后像这样使用它: @Import(AppConfig.class)

Please note , that the above will work only if the FriendList is created at the beginning like a default value with all the available information at that point.请注意,只有当FriendList像默认值一样在开始时创建时,上面的内容才有效,此时所有可用信息。 And also it will only work on the AppUser component that is managed by spring.而且它只适用于由 spring 管理的AppUser组件。 If you create the AppUser using builder in the controller, you need to @Autowired the FriendList in the controller and set it on builder.如果您在控制器中使用 builder 创建 AppUser,则需要在控制器中 @Autowired FriendList 并将其设置在 builder 上。

On another note if the Friends are dynamically created, and let's say for the sake of the example that if at the beginning you have 10 friends and after 1 hour you will have another 10 friends and you need to see all 20 of them, then you need another approach.另外,如果好友是动态创建的,假设一开始你有 10 个好友,1 小时后你将有另外 10 个好友,你需要查看所有 20 个好友,那么你需要另一种方法。 Easiest way will be store in the Database the friends and just fetch each them each time and set them on the AppUser.最简单的方法是将朋友存储在数据库中,每次只获取每个朋友并将它们设置在 AppUser 上。 Or retrieve them from a Cache Map.或者从缓存映射中检索它们。

You can also use @Autowired in that scenario, but is more complicated and you will need to autowire directly a list of Friends:您也可以在这种情况下使用@Autowired ,但更复杂,您需要直接自动装配好友列表:

@Autowired
private List<Friend> friends;

And each Friend should be created dynamically.每个Friend都应该动态创建。 Something like this像这样的东西

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

Also check this example with ObjectProvider from here .还可以从此处使用ObjectProvider检查此示例。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM