@Component
public class CommitManager {
// ...
@Autowired
private final FilePatcher filePatcher;
// ...
}
@Component
public class FilePatcher {
private Path directory;
public FilePatcher(Path directory) {
this.directory = directory;
}
// ...
}
Есть ли альтернативы, кроме как отпустить идею с определением FilePatcher как бина?
@Lazy
.// Обычный класс. Не компонент.
public class FilePatcher {
private final Path directory;
public FilePatcher(Path directory) {
this.directory = directory;
}
public void hello() {
System.out.println(directory);
}
}
@Component
public class CommitManager {
private final FilePatcher filePatcher;
// Spring сгенерирует объект-заглшку. Реальный бин будет запрошен из
// спринг контекста при первом вызове любого метода объекта-заглушки.
public CommitManager(@Lazy FilePatcher filePatcher) {
this.filePatcher = filePatcher;
}
public void callFilePatcher() {
filePatcher.hello();
}
}
@Component
public class RuntimeSetter {
private final ApplicationContext ctx;
private final CommitManager cm;
public RuntimeSetter(ApplicationContext ctx, CommitManager cm) {
this.ctx = ctx;
this.cm = cm;
}
@Scheduled(initialDelay = 500, fixedDelay = Long.MAX_VALUE)
public void set() {
// Спустя 500 мс становится известен path.
// В этот момент создаём бин FilePatcher вручную.
var factory = (BeanDefinitionRegistry) ctx.getAutowireCapableBeanFactory();
var gbd = new GenericBeanDefinition();
gbd.setBeanClass(FilePatcher.class);
var cav = new ConstructorArgumentValues();
cav.addGenericArgumentValue(Path.of("hello_world"));
gbd.setConstructorArgumentValues(cav);
factory.registerBeanDefinition("filePatcher", gbd);
}
@Scheduled(initialDelay = 1000, fixedDelay = Long.MAX_VALUE)
public void run() {
// Спустя 1000 мс вызываем код, который триггерит резолв lazy бина.
cm.callFilePatcher();
}
}
public class CtxInit implements ApplicationContextInitializer<ConfigurableApplicationContext> {
@Override
public void initialize(ConfigurableApplicationContext applicationContext) {
applicationContext.getEnvironment()
.getPropertySources()
.addLast(new PropertySource<String>("pathPropertyResolver") {
private volatile String path;
{
var t = new Thread(() -> {
try {
// Спустя 500 мс после старта становится
// известен path.
Thread.sleep(500);
path = "hello_world";
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
});
t.start();
}
@Override
public Object getProperty(String name) {
// Наш проперти называется myLazyPath.
if ("myLazyPath".equals(name)) {
return path;
}
return null;
}
});
}
}
@SpringBootApplication
@EnableScheduling
public class ApplicationMain {
public static void main(String[] args) {
var sa = new SpringApplication(ApplicationMain.class);
sa.addInitializers(new CtxInit());
sa.run(args);
}
}
@Component
public class FilePatcher {
private final Path directory;
public FilePatcher(@Value("${myLazyPath}") Path directory) {
this.directory = directory;
}
public void hello() {
System.out.println(directory);
}
}
@Component
public class CommitManager {
private final FilePatcher filePatcher;
public CommitManager(@Lazy FilePatcher filePatcher) {
this.filePatcher = filePatcher;
}
public void callFilePatcher() {
filePatcher.hello();
}
}
@Component
public class RuntimeRunner {
private final CommitManager cm;
public RuntimeRunner(CommitManager cm) {
this.cm = cm;
}
@Scheduled(initialDelay = 1000, fixedDelay=Long.MAX_VALUE)
public void run() {
// Спустя 1000 мс вызываем код, который триггерит резолв lazy бина.
cm.callFilePatcher();
}
}