Home > Mobile >  Create a mixed initialized component for existing fields using lombok and spring
Create a mixed initialized component for existing fields using lombok and spring

Time:10-27

I want to create a spring component that can be partially initialized automatically by spring (all those final fields) and partially initialized manually (the remaining non final fields). So I used lombok and tried a lots of variants but couldn't make it work:

(ex: adding @AllArgsConstructor(access = PACKAGE) will not be instantiated by spring because of Caused by: java.lang.NoSuchMethodException: Task.<init>())

@RequiredArgsConstructor
@Builder (toBuilder=true)
@Component
public class Task {

  private final Repo1Repository repo1;     //<- should be automatically initialized
  private final Repo2Repository repo2;     //<- should be automatically initialized
  private final Repo3Repository repo3;     //<- should be automatically initialized

  private String tableName;          //<- will be manually initialized with the builder
  private boolean special;           //<- will be manually initialized with the builder
  ...
}

Intended usage will be something like:

@RequiredArgsConstructor
@Configuration
public class ConfigurationClass {
  private final Task task; //<- automatically injected

  @Bean
  public void createBean (){
    return task.toBuilder ().tableName("TABLE").special(true).build();
  }
 
  @Bean
  public void createBean2 (){
    return task.toBuilder().tableName("TABLE2").special(true).build();
  }
  ...
}

Using lombok 1.18.24 & spring 5.3.20 (from spring boot 2.6.8)

CodePudding user response:

Although this may be possible somehow, I suggest a different approach.

Right now you are trying to mix two concerns: the modeling of the actual Task class, and creating a templating mechanism for populating instances of Task with default values. You should separate these concerns by explicitly modeling them:

@Builder
public class Task {
  private final Repo1Repository repo1;
  private final Repo2Repository repo2;
  private final Repo3Repository repo3;
  private final String tableName;
  private final boolean special;
}

@AllArgsConstructor
@Component
public class TaskTemplate {
  private final Repo1Repository repo1;
  private final Repo2Repository repo2;
  private final Repo3Repository repo3;

  public Task.TaskBuilder builder() {
    return Task.builder().repo1(repo1).repo2(repo2).repo3(repo3);
  }
}

CodePudding user response:

The best I could come up in the end that will work is the configuration below that manually creates the core bean but I am not happy with duplicating the dependencies (is a similar solution with Jan's)

@RequiredArgsConstructor
@Configuration
public class MyConfiguration {

    private final Repo1Repository repo1;
    private final Repo2Repository repo2;
    private final Repo3Repository repo3;

    @Bean
    public Task task() {
        return new Task(repo1, repo2, repo3);
    }
    ...
}

and the original component is this:

@RequiredArgsConstructor
@AllArgsConstructor(access = PACKAGE)
@Builder (toBuilder = true)
public class Task  {

  private final Repo1Repository repo1;
  private final Repo2Repository repo2;
  private final Repo3Repository repo3;

  private String tableName;
  private boolean special;
  ....
}
  • Related