Uh oh!
There was an error while loading. Please reload this page.
Conversation
Now everything (almost) is configurable via DI container and the things remain perfectly the same until now if no DI container is detected. Only one new tiny dependency is added ( From the beginning, we wanted this feature to be as unobtrusive as possible, with as less as possible changes. I think that we can do the code more compact but this is another story. For example for each inject aware field we have a declaration and a lazy initialization getter (and in some cases a setter): @InjectprivateOptional<ErrorHandler> errorHandler = Optional.empty();
publicErrorHandlergetErrorHandler() {
if (!errorHandler.isPresent()) {
errorHandler = Optional.of(newDefaultErrorHandler(this));
}
returnerrorHandler.get();
}
publicvoidsetErrorHandler(ErrorHandlererrorHandler) {
this.errorHandler = Optional.of(errorHandler);
}I prefer something more verbose/light as: @InjectprivateOptional<ErrorHandler> errorHandler;
publicErrorHandlergetErrorHandler() {
returnOptionalUtils.setOnNull(Supplier<ErrorHandler>).get();
}Also the proposed implementation with lazy initialization getters come with some improvements from performance point of view because the objects are initialized only on request. That is all. I will add in my next two comments how I tested with Spring and Guice. If this PR will be accepted, after merge I will update |
For Spring Test, I modified pippo-demo-spring publicclassSpringApplication3extendsControllerApplication {
@InjectprivateList<? extendsController> controllers;
@OverrideprotectedvoidonInit() {
// add routes for static contentaddPublicResourceRoute();
addWebjarsResourceRoute();
addControllers(controllers.toArray(newController[0]));
}
}@Configuration@ComponentScanpublicclassSpringConfiguration3extendsSpringConfiguration {
@BeanpublicContactServicecontactService() {
returnnewInMemoryContactService();
}
@BeanpublicTemplateEnginetemplateEngine() {
returnnewSimpleTemplateEngine();
}
@BeanpublicRouterrouter() {
returnnewCustomRouter();
}
@BeanpublicWebServerwebServer() {
returnnewTjwsServer();
}
@BeanpublicPippoSettingspippoSettings() {
returnnewPippoSettings();
}
@BeanpublicApplicationapplication() {
returnnewSpringApplication3();
}
@BeanpublicPippopippo() {
returnnewPippo(application()).setServer(webServer());
}
}publicclassSpringDemo3 {
publicstaticvoidmain(String[] args) {
ApplicationContextcontext = newAnnotationConfigApplicationContext(SpringConfiguration3.class);
Pippopippo = context.getBean(Pippo.class);
pippo.start();
}
}@Path@ComponentpublicclassContactsControllerextendsController {
@InjectprivateContactServicecontactService;
@InjectprivateTemplateEnginetemplateEngine;
@GETpublicvoidsayHello() {
StringWriterwriter = newStringWriter();
Map<String, Object> model = newHashMap<>();
model.put("name", "Decebal");
templateEngine.renderString("Hello ${name}", model, writer);
getResponse().send(writer.toString());
}
} |
For Guice Test, I modified pippo-demo-guice publicclassGuiceApplication3extendsControllerApplication {
@InjectprivateList<? extendsController> controllers;
@OverrideprotectedvoidonInit() {
// add routes for static contentaddPublicResourceRoute();
addWebjarsResourceRoute();
addControllers(controllers.toArray(newController[0]));
}
}publicclassGuiceModule3extendsAbstractModule {
@Overrideprotectedvoidconfigure() {
bind(ContactService.class).to(InMemoryContactService.class).asEagerSingleton();
bind(Application.class).to(GuiceApplication3.class).asEagerSingleton();
bind(Router.class).to(CustomRouter.class).in(Scopes.SINGLETON);
bind(TemplateEngine.class).to(SimpleTemplateEngine.class).asEagerSingleton();
bind(WebServer.class).to(TjwsServer.class).in(Scopes.SINGLETON);
bind(Pippo.class);
bindOptionalApplication();
bindOptionalControllerApplication();
}
@Singleton@Provides@InjectpublicList<? extendsController> controllers(ContactsControllercontacts) {
returnArrays.asList(contacts);
}
privatevoidbindOptionalApplication() {
OptionalBinder.newOptionalBinder(binder(), ContentTypeEngines.class);
OptionalBinder.newOptionalBinder(binder(), ErrorHandler.class);
OptionalBinder.newOptionalBinder(binder(), HttpCacheToolkit.class);
OptionalBinder.newOptionalBinder(binder(), Languages.class);
OptionalBinder.newOptionalBinder(binder(), Messages.class);
OptionalBinder.newOptionalBinder(binder(), MimeTypes.class);
OptionalBinder.newOptionalBinder(binder(), Router.class);
OptionalBinder.newOptionalBinder(binder(), WebSocketRouter.class);
OptionalBinder.newOptionalBinder(binder(), RequestResponseFactory.class);
OptionalBinder.newOptionalBinder(binder(), RoutePreDispatchListenerList.class);
OptionalBinder.newOptionalBinder(binder(), RoutePostDispatchListenerList.class);
OptionalBinder.newOptionalBinder(binder(), TemplateEngine.class);
OptionalBinder.newOptionalBinder(binder(), newTypeLiteral<RouteHandler<?>>(){});
OptionalBinder.newOptionalBinder(binder(), newTypeLiteral<List<Initializer>>(){});
}
privatevoidbindOptionalControllerApplication() {
OptionalBinder.newOptionalBinder(binder(), ControllerFactory.class);
OptionalBinder.newOptionalBinder(binder(), ControllerInitializationListenerList.class);
OptionalBinder.newOptionalBinder(binder(), ControllerInstantiationListenerList.class);
OptionalBinder.newOptionalBinder(binder(), ControllerInvokeListenerList.class);
OptionalBinder.newOptionalBinder(binder(), newTypeLiteral<List<MethodParameterExtractor>>(){});
}
}publicclassGuiceDemo3 {
publicstaticvoidmain(String[] args) {
Injectorinjector = Guice.createInjector(newGuiceModule3());
Pippopippo = injector.getInstance(Pippo.class);
pippo.start();
}
}@PathpublicclassContactsControllerextendsController {
@InjectprivateContactServicecontactService;
@InjectprivateTemplateEnginetemplateEngine;
@GETpublicvoidsayHello() {
StringWriterwriter = newStringWriter();
Map<String, Object> model = newHashMap<>();
model.put("name", "Decebal");
templateEngine.renderString("Hello ${name}", model, writer);
getResponse().send(writer.toString());
}
}I don't like the complexity of |
mhagnumdw
commented
Nov 20, 2021
Sorry I didn't understand why What if I have dozens of controllers? |
mhagnumdw
commented
Nov 20, 2021
@decebals , will you still make changes or can I test this version in my application? |
decebals
commented
Nov 21, 2021
I think that you can test it. The code is perfect functional from what I see until now. Maybe little adjustments in time. |
Your question is good. In my last (relative big) project I use Pippo with Spring. In Spring this part with gathering all controllers and inject them in application is easy and automatically. All you have to do is to add |
decebals
commented
Nov 21, 2021
In the end I think that I solved the problem in an elegant way using Guice Multibinding and Reflections. publicclassGuiceModule3extendsAbstractModule {
@Overrideprotectedvoidconfigure() {
bind(ContactService.class).to(InMemoryContactService.class).asEagerSingleton();
bind(Application.class).to(GuiceApplication3.class).asEagerSingleton();
bind(Router.class).to(CustomRouter.class).in(Scopes.SINGLETON);
bind(TemplateEngine.class).to(SimpleTemplateEngine.class).asEagerSingleton();
bind(WebServer.class).to(TjwsServer.class).in(Scopes.SINGLETON);
bind(Pippo.class);
bindControllers();
bindOptionalApplication();
bindOptionalControllerApplication();
}
privatevoidbindControllers() {
// retrieve controller classesReflectionsreflections = newReflections(getClass().getPackage().getName());
Set<Class<? extendsController>> controllers = reflections.getSubTypesOf(Controller.class);
// bind found controllersMultibinder<Controller> multibinder = Multibinder.newSetBinder(binder(), Controller.class);
controllers.forEach(controller -> multibinder.addBinding().to(controller));
}
}publicclassGuiceApplication3extendsControllerApplication {
@InjectprivateSet<Controller> controllers;
@OverrideprotectedvoidonInit() {
// add routes for static contentaddPublicResourceRoute();
addWebjarsResourceRoute();
addControllers(controllers.toArray(newController[0]));
}
}I tested with multiple controllers and the result is good. |
mhagnumdw
commented
Nov 22, 2021
I already use the Reflections lib and it is very good! |
mhagnumdw
commented
Nov 22, 2021
My boot is heavily modified, so for now I won't be able to test it thoroughly. But I have good news: with this version my application continues to work normally. |
mhagnumdw
commented
Nov 22, 2021
I'm trying to adapt my application to this model... @decebals , I use the The
|
mhagnumdw
commented
Nov 26, 2021
For me, Guice's dependency injection just only worked like this: @InjectprivateSet<Controller> controllers;obs: |
decebals
commented
Nov 26, 2021
Yes, it's |
decebals
commented
Nov 26, 2021
In #590 (comment), it's |
I inject As I mentioned in #565, the pippo - spring integration is good enough for me and without this PR. This PR is useful when you want to fine tuning the pippo stack from DI (Spring, Guice), entirely. |
I obtained relative ( The code in this case looks like: //@SingletonpublicclassAvajeApplicationextendsControllerApplication {
privateList<Controller> controllers;
// @InjectpublicAvajeApplication(List<Controller> controllers) {
this.controllers = controllers;
}
@OverrideprotectedvoidonInit() {
// add routes for static contentaddPublicResourceRoute();
addWebjarsResourceRoute();
addControllers(controllers.toArray(newController[0]));
}
}@FactorypublicclassAvajeConfiguration {
@BeanpublicContactServicecontactService() {
returnnewInMemoryContactService();
}
@BeanpublicPippoSettingspippoSettings() {
returnnewPippoSettings();
}
// @Bean// public List<Controller> controllers(ContactsController contactsController) {// System.out.println("AvajeConfiguration.controllers");// return Collections.singletonList(contactsController);// }@BeanpublicApplicationapplication(ContactsControllercontactsController, TestControllertestController) {
returnnewAvajeApplication(Arrays.asList(contactsController, testController));
}
// @Bean// public Pippo pippo(Application application, WebServer webServer) {// return new Pippo(application).setServer(webServer);// }
}publicclassAvajeDemo {
publicstaticvoidmain(String[] args) {
BeanScopebeanScope = BeanScope.newBuilder().build();
Pippopippo = beanScope.get(Pippo.class);
pippo.start();
}
}@Path@SingletonpublicclassContactsControllerextendsController {
@InjectContactServicecontactService;
@InjectTemplateEnginetemplateEngine;
@GETpublicvoidindex() {
getResponse().bind("contacts", contactService.getContacts());
getResponse().render("contacts");
}
} |
Hi! I understand that it is possible to inject To explain it better, something like this: Injectorinjector = Guice.createInjector(
newPippoGuiceModule(),
newAppJpaPersistModule("persistenceUnitName", pippoSettings), // <<< need PippoSettings instance herenewAppGuiceModule()
);
GuiceInjector.set(injector);
Pippopippo = injector.getInstance(Pippo.class);
pippo.start();ps: I'm looking for a way to work around this problem. |
mhagnumdw
commented
Nov 26, 2021
We might have abstract controllers, so maybe it's better to avoid bind errors (at least in Guice): Reflectionsreflections = newReflections(getClass().getPackage().getName(), newSubTypesScanner());
Set<Class<? extendsController>> controllers = reflections.getSubTypesOf(Controller.class)
.stream()
.filter(clazz -> clazz.isAnnotationPresent(ro.pippo.controller.Path.class))
.collect(Collectors.toSet())Or some other logic that checks if it's a concrete class. |
decebals
commented
Nov 26, 2021
I don't visualize your implementation. How |
decebals
commented
Nov 26, 2021
What about https://stackoverflow.com/questions/39734343/injecting-a-dependency-into-guice-module? |
mhagnumdw
commented
Nov 26, 2021
Oh, sorry 😅, I forgot to mention it's a class of mine. It's just a wrapper for Guice's It goes something like this: publicclassAppJpaPersistModuleimplementsModule {
privatefinalPippoSettingssettings;
publicJPAGuiceModule(PippoSettingssettings) {
this.settings = settings;
}
@Overridepublicvoidconfigure(Binderbinder) {
JpaPersistModulejpaModule = newJpaPersistModule(Constantes.PU_NAME);
jpaModule.properties( ... ); // TODO: get properties from PippoSettings and add herebinder.install(jpaModule);
}
}ps: But I think I'll change the strategy so I don't need |
mhagnumdw
commented
Nov 28, 2021
@decebals , I use Freemarker and it's not working. The problem is that the To make it work I did:
I configure the Guice module like this: @Overrideprotectedvoidconfigure() {
bind(Application.class).to(PippoApplication.class).asEagerSingleton();
bind(TemplateEngine.class).to(FreemarkerTemplateEngine.class).asEagerSingleton();
// ...
}It would be nice to be able to leave the annotation just on the |
decebals
commented
Nov 29, 2021
@mhagnumdw |
# Conflicts: # pippo-controller-parent/pippo-controller/src/main/java/ro/pippo/controller/ControllerApplication.java
mhagnumdw
commented
Dec 1, 2021
@decebals , please update from master. |
# Conflicts: # pippo-controller-parent/pippo-controller/src/main/java/ro/pippo/controller/ControllerApplication.java
decebals
commented
Dec 1, 2021
Done. |
mhagnumdw
commented
Dec 21, 2021
@decebals , please update from master. |
decebals
commented
Dec 22, 2021
Done |
mhagnumdw
commented
Dec 24, 2021
Currently I register a content type like this: What do you think we also use dependency injection to register In Guice we can use If you agree, could you implement it? So I would validate doing the tests in my application. |
decebals
commented
Dec 28, 2021
Sure, I will do it. Now I am in a mini vacation with family. |
Kudos, SonarCloud Quality Gate passed!
|
@mhagnumdw I don't know if you abandoned the ship but I will write here some of my conclusions :). importjavax.annotation.Nullable;
importjavax.inject.Inject;
classMyClass {
@Inject@NullableprivateGreetinggreeting;
}Both Spring and Guice know how to deal with this combination of annotations. importjavax.annotation.Nullable;
importjavax.inject.Inject;
classMyClass {
privateGreetinggreeting;
@InjectpublicvoidsetGreeting(@NullableGreetinggreeting) {
this.greeting = greeting;
}
}What I don't like is that in all situations (our initial solution based on And this problem is because importorg.springframework.beans.factory.annotation.Autowired;
importcom.google.inject.Inject;
classMyClass {
@Autowired(required = false)
@Inject(optional = true)
privateGreetinggreeting;
}but in this case we must add Everything started from the idea to have something/everything configurable via most popular java IoC (Spring and Guice), but without forcing the pippo developer to use IoC (or a specific IoC). |
mhagnumdw
commented
Feb 6, 2023
Hi @decebals !! I paused this activity for timing reasons. I still think it's a worthwhile activity. But unfortunately, given its magnitude, I won't have time to see it carefully. My application that uses Pippo is only receiving fixes and they are sporadic. |








#554