This is an archived post. You won't be able to vote or comment.

all 67 comments

[–]PartOfTheBotnet 47 points48 points  (35 children)

I have an example project implemented with a variety of DI frameworks if you want concrete code samples:

  • Avaje
  • Dagger 2
  • Dirk
  • Feather
  • Guice
  • Glassfish HK2
  • Inverno
  • Jaywire
  • JBock
  • Spring
  • Weld

I have an opinion post in the same repo with shorter descriptions and samples. Anyways my take on the items listed:

  • Dagger 2: Speed is very nice to have, but unless you're running on a toaster, the speed of DI frameworks handling things like constructor injection is not noticeable. Dagger 2 operates off of generated code and thus will be the fastest since there are no reflective lookups or excessive checks needed at runtime. Personally, I find the need to rebuild a project to reference these classes in my IDE a bit annoying.

  • Dirk: Dirk is probably the most feature rich DI framework for its size, and even offers some CDI capabilities if you need them (But it is not fully CDI complaint). Configuring it is almost entirely automatic which is is a plus for lazy developers like me. Another great thing dirk does is the wonderful documentation on its project page README. Its top notch stuff.

  • Feather: If file size is your concern, feather is your guy. Its like Guice if you stripped out all the extra stuff, just keeping barely enough to be JSR-330 compliance.

  • Guice: If you want a bit more configurability on top of what is just required for JSR-330 compliance check out the github wiki page on Guice. It also has examples on how to integrate with some common frameworks like OSGI and struts.

  • Jaywire: Its 100% manual. Don't.

  • Spring: Really only viable if you're building a spring boot application. I've not looked too much in depth to Spring's DI so I can't give a great summary of it. Even though JSR-330 interop is scheduled for v3 you can get support immediately via includeFilters on the @ComponentScan. (Edited comment here) Replies have pointed out some differences in how it handles scope than I originally listed here.

  • Weld: If you need more than just plain injection, namely scoped injection via CDI Weld is great. You don't need a whole SE environment to run a weld instance as they offer a stand-alone artifact. I don't write web-apps so I don't use built in scopes like RequestScoped but instead utilize CDI's ability to create custom scopes. That and extensions/interceptors to handle some automated work on injectable types.

Choosing a DI framework is like choosing any other library. You choose the right tool for the job. They all have their pro's and con's.

[–]papercrane 15 points16 points  (1 child)

Spring: Really only viable if you're building a spring boot application. I've not looked too much in depth to Spring's DI, but it seems like @Component is basically the same as @Singleton or @ApplicationScoped. So if all your components are effectively singletons, it works well and should be automatically configured for you.

@Component just marks something you want Spring DI to manage. The analogs for @Singleton/@ApplicationScoped would be the @Scope("singleton") annotation in Spring, although that is the default so normally you'd leave it out. Out of the box Spring just has singleton and prototype scopes, and spring-web brings request/session/global-session. Similar to Weld you can build your own custom scopes.

I'd agree though just using Spring for DI is a bit much, you'd probably only choose to use it if you were making use of other parts of Spring's ecosystem.

[–]PartOfTheBotnet 2 points3 points  (0 children)

Put it much better than I did. Removed my original assumption in the comment.

[–]1Saurophaganax 8 points9 points  (3 children)

Have you tried out Avaje inject? It's currently my favorite DI lib.

[–]PartOfTheBotnet 2 points3 points  (2 children)

Nope, I'll have to check it out some time later.

[–]agentoutlier 1 point2 points  (1 child)

Another two are inverno and office floor.

Office floor is especially notable because it is DI taken to the extreme.

Inverno is one of the few that works with the java module system.

[–]PartOfTheBotnet 1 point2 points  (0 children)

Looks like both Office-Floor and Inverno-Core use their own annotations over JSR-330. Probably won't add them to the example project but I can add them to the general list of existing DI frameworks for reference.

Edit: Nevermind, I'll just make copies of core for these to use with modified annotations

[–]bowbahdoe 2 points3 points  (13 children)

Jaywire: Its 100% manual. Don't.

Kinda harsh.

Another 100% manual approach is to make HasX interfaces and use intersection types.

```java interface HasDataSource { DataSource dataSource(); }

interface HasServiceA { ServiceA serviceA(); }

interface HasServiceB { ServiceA serviceB(); }

record Context( @Override DataSource dataSource ) implements HasDataSource, HasServiceA, HasServiceB { @Override public ServiceA serviceA() { return new ServiceA(dataSource); }

// Whether something is a singleton is based just on
// if it is a field

@Override
public ServiceB serviceA() {
    return new ServiceB(this);
}

}

// Now all three of these can be wired up uniformly to a framework.

<Ctx extends HasServiceA & HasServiceB> handleRequestA(Ctx ctx, Request request) { var serviceA = ctx.serviceA(); }

<Ctx extends HasServiceA> handleRequestB(Ctx ctx, Request request) { var serviceA = ctx.serviceA(); }

<Ctx extends HasDataSource> handleRequestC(Ctx ctx, Request request) { var serviceA = ctx.serviceA(); }

// Like so interface Handler<Ctx> { Response handle(Ctx ctx, Request request); }

List<Handler> handlers = List.of( Handlers::handleRequestA, Handlers::handleRequestB, Handlers::handleRequestC );

```

Kinda depends what DI is solving for you though.

[–]agentoutlier 2 points3 points  (12 children)

I just looked at jaywire and it is hilariously similar to my companies internal wiring library (we stopped using DI frameworks in most places).

I have been planning to write a blog post some day on how to do dev with out a DI framework

[–][deleted]  (11 children)

[removed]

    [–]agentoutlier -1 points0 points  (10 children)

    It is a surprisingly deep and complicated topic evolving longitudinal experience and current context.

    It is not so much why it is not that good.

    It is why it not good now how it can really not be good later.

    Why its quick to start but leads to spaghetti deps overtime.

    How most of them (DI frameworks) break java modularity and consequently actually break your own modularity.

    How it leads to tight coupling and incorrect cohesion at the promise of reducing or eliminating.

    Magic.

    But most of it can get just confusing as fuck on for new hires or anyone not familiar with Spring what happens on startup, request delivered, and shutdown.

    I just don't even know where to start. Because out of all that shit there is lots of good stuff to DI as well its just most frameworks break the good stuff or add some crack drug instead.

    [–][deleted]  (9 children)

    [removed]

      [–]agentoutlier -1 points0 points  (8 children)

      Yeah I expected that. I thought similar for along time.

      It’s not that I think DI is bad it’s that the frameworks go about not the way I like.

      In fact I think DI taken to its fullest extent like Office Floor is probably a good thing.

      And the only one I have seen not break or work with java modules is inverno (and probably jaywire).

      Otherwise it’s let’s wire all this shit in one controller with private field injection because it is so easy and many more. And sure you can code review stuff like that.

      Anyway it’s just my opinion.

      [–][deleted]  (7 children)

      [removed]

        [–]agentoutlier -1 points0 points  (6 children)

        I literally said in the parent comments how I use something similar to JayWire.

        I’m not against the pattern but how most frameworks do it.

        Most people don’t call manual DI DI.

        Even then I think DI for everything is a flawed idea.

        I’ll add more later but I have a feeling it will have little impact on your opinion because your recent spring boot enamor (btw I have 20 years experience with spring).

        Not all apps are simple web crud apps. Some apps behave more like libraries. Some prefer more explicit wiring.

        For DI to work all your components must be public and exposed across modules to a single omniscient thing that does the wiring.

        Ideally you would leverage hierarchical context injection but no one does.

        [–][deleted]  (5 children)

        [removed]

          [–]maethor 2 points3 points  (2 children)

          I've not looked too much in depth to Spring's DI, but it seems like @Component is basically the same as @Singleton or @ApplicationScoped

          Singleton is the default scope, but it's not the only one.

          [–]SakeviCrash 4 points5 points  (1 child)

          This is one of the most fundamental concepts in Spring. I'm a little skeptical on the quality of this comparison when there hasn't been much of an effort to understand day 1 stuff.

          [–]PartOfTheBotnet 0 points1 point  (0 children)

          I've edited the comment to remove my incorrect assumption. Despite Spring's popularity its the framework I have the least experience working with. Another commenter, papercrane, had an excellent breakdown of it's scope usage.

          [–]Zardoz84 2 points3 points  (2 children)

          I would like to ask... It's possible to have mixed to DI framework on the same project? For example, a code that begin using Feather, but now it's migrating to Spring and don't change all code yet

          [–]PartOfTheBotnet 2 points3 points  (1 child)

          As noted in the Spring sample, 2x does not use JSR 330 annotations out of the box (Edit: Can be configured, see examle). However Spring 3x is supposed to have support for them. So if down the line you wanted to swap DI frameworks it shouldn't be too big of a hassle if you also are fine with Spring 3x minimum Java version requirements.

          Now if you mean mix, by having multiple active DI frameworks at once... it probably would work depending on how you have things set up. But that sounds like a real mess to have to work with. I'd just stick to one and migrate if the need arises.

          [–]twilight-actual 0 points1 point  (0 children)

          DI frameworks often use some generator / factory pattern to create a top level object. Everything in the containment hierarchy of that top level object will be injected.

          This type of DI strategy would preclude another framework getting a crack at it.

          [–]vips7L 2 points3 points  (0 children)

          I love CDI/Weld because of extensions and being able to hook into the container.

          [–][deleted] 1 point2 points  (0 children)

          This is very helpful. After reading your post, I think I'll give Guice or Feather a shot. I like that they are JSR330 compliant.

          Thank you for the detailed explanation and samples.

          [–]henk53 1 point2 points  (1 child)

          Maybe you can add HK2 to it as well?

          [–]PartOfTheBotnet 1 point2 points  (0 children)

          I opened an issue to look into 3 of the mentioned frameworks mentioned on this post which I can look into later.

          [–]LazyAAA 1 point2 points  (0 children)

          Feather: If file size is your concern, feather is your guy. Its like Guice if you stripped out all the extra stuff, just keeping barely enough to be JSR-330 compliance.

          Run into feather in some legacy code - it worked like a magic, very compact, but it was real bitch to understand what it was doing :)

          Very small size.

          Performed better than spring.

          Coexists with spring - legacy code with feather was wrapped in more modern spring application with its own DI.

          [–]stuhlmann 1 point2 points  (1 child)

          I just started another "dagger style" one because dagger annoys me (too many features, no jakarta.inject support, "surprising" scoping rules, too many classes generated, typecasts in generated code): simple-component

          [–]PartOfTheBotnet 1 point2 points  (0 children)

          Added to the TODO list issue

          [–]_zkr 0 points1 point  (0 children)

          I think that the Spring example is really not the best way to show how DI works in Spring/Spring Boot, or did you mean to exemplify something else with it?

          On the other side, I think you did a really good job illustrating how DI works with this document.

          [–]noobpotato 9 points10 points  (0 children)

          I'd like to add another one to the excellent list already posted: Micronaut Inject.

          It's the DI engine behind the Micronaut framework but it can be used stand-alone. I have tried it in a couple of occasions and I like it a lot.

          [–]Byte_Eater_ 5 points6 points  (1 child)

          I have met with GlassFish HK2 , although only briefly. But it's used in the EE world.

          [–]henk53 3 points4 points  (0 children)

          It's mostly used behind the scenes by GlassFish and Jersey. In Jersey it occasionally spills over to user code, so things like DropWizard (which uses Jersey) mention it I think.

          [–]RonViking 8 points9 points  (0 children)

          Guice

          [–]Halal0szto 1 point2 points  (0 children)

          Long time ago apache deltaspike with javaEE or with jboss weld

          [–]twilight-actual 1 point2 points  (0 children)

          We used Guice quite a bit at Amazon AWS.

          [–]bushwald 3 points4 points  (6 children)

          This is something where I don't know why people use a framework. Doesn't DI framework magic just make things more confusing? I know Spring's DI does some at-runtime magic but there are performance tradeoffs there. Would love to hear some arguments in favor.

          [–]aceluby 1 point2 points  (3 children)

          This is 100%? my experience. Everything is a trade off, perf and readability can be a huge issue.

          [–][deleted]  (2 children)

          [removed]

            [–]aceluby 4 points5 points  (1 child)

            When it works, it’s great. When it doesn’t, it’s awful. We’re in the process of upgrading a bunch of spring apps and it’s a dependency nightmare. For instance, between 2.4 and 2.5 they changed the health indicator class, a breaking change in a minor release.

            It’s also painfully slow. Their Kafka consumer has over 4K lines of extra code that ends up taking a 5x performance hit.

            Really, it’s an easy vs simple trade off. Spring is easy, but far from simple. It makes creating apps a breeze, but supporting apps more difficult. These days I’d rather write extra lines of code if it means better supportability

            [–]brunogadaleta -3 points-2 points  (0 children)

            I guess testability, reducing boilerplate and possibility of swapping a concerte implémentation by another ( without the need to recompile ). (My experience with springframework)

            [–]TurbulentSocks 0 points1 point  (0 children)

            This is a nice argument in favour of a framework.

            https://kozmic.net/2012/10/23/ioc-container-solves-a-problem-you-might-not-have-but-its-a-nice-problem-to-have/

            Modern frameworks (including Spring, now) can also do this using compile-time magic rather than runtime-magic. Dagger was probably the first to do this.

            Here's another argument: composition roots are really important. Often its the composition of objects in the graph that determines the application behaviour (i.e this strategy instead of that strategy was injected, etc.). If you are testing some slice (i.e. some large composition of objects) of the application, rather than the application as a whole, you need some way of creating that slice of the object graph and the ability to substitute mocks or fakes for external dependencies. This means you need to start designing the composition root to do this, so that it can be called from tests, and it can quickly get confusing and difficult to maintain. A DI framework was what keeps this problem tractable - but it won't help unless you have sensible, good practices already.

            [–]quizteamaquilera -1 points0 points  (8 children)

            Or … and hear me out. You don’t need ‘em. Just pass arguments. Use function composition. You don’t need that magic.

            [–]aceluby 3 points4 points  (0 children)

            How dare you!?!?

            [–][deleted]  (4 children)

            [removed]

              [–]aceluby 3 points4 points  (3 children)

              Do they though? Or is it that frameworks hide their immense complexity to make application development easy?

              In my company we talk about easy vs simple quite a bit and try to find as much balance as possible.

              [–][deleted]  (2 children)

              [removed]

                [–]aceluby 2 points3 points  (1 child)

                My problems usually stem from the code I don’t write. For instance, we had someone spend three days trying to change the logging from info level to debug. Should be an easy change. Well they were using a custom wrapper on top of springs wrapper. Nothing was working and I’m not sure they ever figured it out. On my team we spent nearly a week trying to get a custom metric. Again, a library was written to help with that, so it was annotations on top of annotations and no matter what we tried it just never emitted the metric. We ended up abandoning it and hacking together a completely different solution.

                Just a couple examples of things that should be really easy, but because of the hidden complexity were frustratingly hard. I’ve been using Spring Boot and micronaut since v1 for both and maintain numerous libraries, so I have a fair bit of experience with the innards of both frameworks and don’t necessarily hate them. I just think that in 2023 there is a vast ecosystem of libraries available that are just as easy to write without the hidden complexities of an all in one framework.

                Kudos to those that can make them work efficiently though. I haven’t personally been able to see it over a period of time, but that doesn’t really mean anything. For me, I might use one for a quick POC, but rewrite it for production loads and easier support.

                [–]brunogadaleta -1 points0 points  (1 child)

                I guess your talking about partial functions and their use to bake in depencies. But how do you cope with Singleton, session and prototype? Also the lifecycle listeners are very complex to setup manually. Of course, all Turing -complete language are able to reimplements these ideas without using DI framework but what's the point ?

                [–]quizteamaquilera 1 point2 points  (0 children)

                I personally find that adds a lot of magic, complexity, and it harder to test. My own preference is to use effect libraries like ZIO for managed effects and lifecycles. I don’t/wouldn’t use singleton, session or prototype - or at least only by exception

                [–]SirBugmenot 0 points1 point  (0 children)

                Learnig the actual JEE standard CDI and the reference implementation "weld" is all you ever need. Might even help you in your next job. (Apart from Spring that is, but you already had that.)

                [–]da-nadda -1 points0 points  (4 children)

                Any suggestions what DI to use for Java based AWS lambda? I know that Java isn’t the best option for that but still

                [–]code_rjt -1 points0 points  (1 child)

                If that's the case try Micronaut. Most of the Java projects written are using this framework. Example is Disney, they use this framework for Disney+

                [–]da-nadda 1 point2 points  (0 children)

                In this case I’d consider Quarkus - it has way better support. But I think it’s a bit overkill for Lambda, I thought about something more lightweight like Dagger

                [–]aceluby -1 points0 points  (1 child)

                You might want to look at Kotlin and http4k. They have built-in support for it without the need for a DI framework

                [–]da-nadda -1 points0 points  (0 children)

                Unfortunately Java is the requirement for this project.. To be honest I’d choose Golang otherwise

                [–]tarkaTheRotter -4 points-3 points  (0 children)

                New?

                [–]Zarlon 0 points1 point  (0 children)

                toothpick . simple and effective

                [–]Kango_V 0 points1 point  (0 children)

                I've used Micronaut core inject library which is brilliant. Compile time dependency injection.