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;
....
}