avaje-inject
avaje-inject copied to clipboard
Support Lazy initialization
How to make Bean is lazy, Maybe we can be using Supplier and at runtime Supplier.get() to inject instance
Inject is already pretty fast for wiring compared to other DI frameworks, for what purpose do you need lazy beans?
I am writing a desktop application using javafx, I have some UI component bean for example: click a button and popup a dialog and the dialog is a bean. Suppose there is a situation: user open application and do not click the button, the dialog bean still in memory, lazy initialize there is a need. For optimize memory usage
Tried using the Provider Interface?
you can wire a bean provider for cases where you don't want to immediately instantiate.
Yeah, I had tried, I think this will make a lot of template code in my application cause there are many UI component need as a bean, if the DI framework support lazy initialization option that will be great. If there is no way to implement lazy initialization, I only have provider interface
you could make a utility method to convert a supplier to a cached value. (btw you can also use Supplier if you like instead of Provider)
public static <T> Supplier<T> memoize(Supplier<T> original) {
return new Supplier<T>() {
Supplier<T> delegate = this::firstTime;
boolean initialized;
public T get() {
return delegate.get();
}
private synchronized T firstTime() {
if (!initialized) {
T value = original.get();
delegate = () -> value;
initialized = true;
}
return delegate.get();
}
};
}
Then you can do:
@Singleton
class MyFXService {
Supplier<MyObject> supplier;
MyFXService(Supplier<MyObject> supplier) {
this.supplier = memoize(supplier);
}
void someMethod() {
var value = supplier.get();
// idk do something
}
}
click a button and popup a dialog and the dialog is a bean.
@DaiYuANg can you show us what your code looks like? I don't know much about JavaFX (nor swing) these days. So the idea of using DI to inject a dialog is something I can't easily visualise in terms of what that code actually looks like.
To be clear I believe we are talking about "Lazy Singleton" (only 1 instance initialised lazily on demand - so a Provider<T> method with memoize to ensure the single instance).
Now currently we treat a @Factory @Bean @Secondary method as a Provider<T> ... and this is around only using secondary dependencies if another dependency doesn't exist (the secondary is only initialised if there isn't a higher priority dependency provided).
So as a following thought, is that maybe we could have a @Factory @Bean @Lazy method to similarly register the method as a Provider<T> (but this provider includes the memoize type logic to ensure only 1 instance). So, if we had that and that method was used to create the Dialog then the remaining question is how is that Provider<Dialog> actually used / wired ... what does the onButtonPress code look like / how does it use the Provider<Dialog> ?
you could make a utility method to convert a supplier to a cached value. (btw you can also use
Supplierif you like instead ofProvider)public static <T> Supplier<T> memoize(Supplier<T> original) { return new Supplier<T>() { Supplier<T> delegate = this::firstTime; boolean initialized; public T get() { return delegate.get(); } private synchronized T firstTime() { if (!initialized) { T value = original.get(); delegate = () -> value; initialized = true; } return delegate.get(); } }; }Then you can do:
@Singleton class MyFXService { Supplier<MyObject> supplier; MyFXService(Supplier<MyObject> supplier) { this.supplier = memoize(supplier); } void someMethod() { var value = supplier.get(); // idk do something } }
Yeah, I tried the Supplier way I think this will make a lot of template code with the same reason, I had to xxx.get() many times
click a button and popup a dialog and the dialog is a bean.
@DaiYuANg can you show us what your code looks like? I don't know much about JavaFX (nor swing) these days. So the idea of using DI to inject a dialog is something I can't easily visualise in terms of what that code actually looks like.
To be clear I believe we are talking about "Lazy Singleton" (only 1 instance initialised lazily on demand - so a
Provider<T>method with memoize to ensure the single instance).Now currently we treat a
@Factory@Bean@Secondarymethod as aProvider<T>... and this is around only using secondary dependencies if another dependency doesn't exist (the secondary is only initialised if there isn't a higher priority dependency provided).So as a following thought, is that maybe we could have a
@Factory@Bean@Lazymethod to similarly register the method as aProvider<T>(but this provider includes the memoize type logic to ensure only 1 instance). So, if we had that and that method was used to create the Dialog then the remaining question is how is thatProvider<Dialog>actually used / wired ... what does the onButtonPress code look like / how does it use theProvider<Dialog>?
Of course, for example: I have preferences instance for my setting view(PreferencesFx from com.dlsc.preferencesfx:preferencesfx-core)
@Factory
public class RootFactory {
@Bean
PreferencesFx preferencesFx() {
return PreferencesFx.of(
SaveClass.class,
Category.of(
"Category Title",
Group.of("Group Title", Setting.of("Setting Title", new SimpleStringProperty()))));
}
@Bean
Preferences preferences() {
return Preferences.systemRoot();
}
}
And then there is a controller
@Singleton
@Slf4j
public class GlobalMenuBarController implements Initializable {
//this is click event handle
public void openPreferences(ActionEvent actionEvent) {
//DIContext is a BeanScope wrapper
val preferencesFx = DIContext.get(PreferencesFx.class);
preferencesFx.show(true);
}
}
In my case, maybe openPreferences never executed but now the PreferencesFx always in memory, In desktop environment, I want decrease memory usage, so I'm thinking about the Lazy initialization is necessary.
Of course, for example:
try adding @Secondary to your factory beans. If I'm reading this right it should lazy load.
Yes, @Secondary should get the lazy initialisation for now, and yes I think we will be able to support this properly by adding a @Lazy to be used like:
@Lazy @Bean
PreferencesFx preferencesFx() {
...
Given this is used via val preferencesFx = DIContext.get(PreferencesFx.class); ... then yes that will use beanScope to obtain the instance - this part will work unchanged, all good here. Just to say, I think the alternative to the programmatic access would be to inject a Provider<PreferencesFx>.
So yes, I think we can add some proper support for this.
@DaiYuANg just saying, 9.12-RC1 is in maven central so you can give this a try
@DaiYuANg just saying, 9.12-RC1 is in maven central so you can give this a try
Hey, thanks so much 😸