Dependency injection

Last updated
Dependency injection is often used alongside specialized frameworks, known as 'containers', to facilitate program composition. DependencyInjectionServiceProvider.png
Dependency injection is often used alongside specialized frameworks, known as 'containers', to facilitate program composition.

In software engineering, dependency injection is a programming technique in which an object or function receives other objects or functions that it requires, as opposed to creating them internally. Dependency injection aims to separate the concerns of constructing objects and using them, leading to loosely coupled programs. [1] [2] [3] The pattern ensures that an object or function that wants to use a given service should not have to know how to construct those services. Instead, the receiving 'client' (object or function) is provided with its dependencies by external code (an 'injector'), which it is not aware of. [4] Dependency injection makes implicit dependencies explicit and helps solve the following problems: [5]

Contents

Dependency injection is often used to keep code in-line with the dependency inversion principle. [6] [7]

In statically typed languages using dependency injection means a client only needs to declare the interfaces of the services it uses, rather than their concrete implementations, making it easier to change which services are used at runtime without recompiling.

Application frameworks often combine dependency injection with inversion of control. Under inversion of control, the framework first constructs an object (such as a controller), and then passes control flow to it. With dependency injection, the framework also instantiates the dependencies declared by the application object (often in the constructor method's parameters), and passes the dependencies into the object. [8]

Dependency injection implements the idea of "inverting control over the implementations of dependencies," which is why certain Java frameworks generically name the concept "inversion of control" (not to be confused with inversion of control flow). [9]

Roles

Dependency injection for five-year-olds

When you go and get things out of the refrigerator for yourself, you can cause problems. You might leave the door open, you might get something Mommy or Daddy don't want you to have. You might even be looking for something we don't even have or which has expired.

What you should be doing is stating a need, "I need something to drink with lunch," and then we will make sure you have something when you sit down to eat something.

John Munsch, 28 October 2009. [2] [10] [11]

Dependency injection involves four roles: services, clients, interfaces and injectors.

Services and clients

A service is any class which contains useful functionality. In turn, a client is any class which uses services. The services that a client requires are the client's dependencies.

Any object can be a service or a client; the names relate only to the role the objects play in an injection. The same object may even be both a client (it uses injected services) and a service (it is injected into other objects). Upon injection, the service is made part of the client's state, available for use. [12]

Interfaces

Clients should not know how their dependencies are implemented, only their names and API. A service which retrieves emails, for instance, may use the IMAP or POP3 protocols behind the scenes, but this detail is likely irrelevant to calling code that merely wants an email retrieved. By ignoring implementation details, clients do not need to change when their dependencies do.

Injectors

The injector, sometimes also called an assembler, container, provider or factory, introduces services to the client.

The role of injectors is to construct and connect complex object graphs, where objects may be both clients and services. The injector itself may be many objects working together, but must not be the client, as this would create a circular dependency.

Because dependency injection separates how objects are constructed from how they are used, it often diminishes the importance of the new keyword found in most object-oriented languages. Because the framework handles creating services, the programmer tends to only directly construct value objects which represents entities in the program's domain (such as an Employee object in a business app or an Order object in a shopping app). [13] [14] [15] [16]

Analogy

As an analogy, cars can be thought of as services which perform the useful work of transporting people from one place to another. Car engines can require gas, diesel or electricity, but this detail is unimportant to the client—a driver—who only cares if it can get them to their destination.

Cars present a uniform interface through their pedals, steering wheels and other controls. As such, which engine they were 'injected' with on the factory line ceases to matter and drivers can switch between any kind of car as needed.

Advantages and disadvantages

Advantages

A basic benefit of dependency injection is decreased coupling between classes and their dependencies. [17] [18]

By removing a client's knowledge of how its dependencies are implemented, programs become more reusable, testable and maintainable. [19]

This also results in increased flexibility: a client may act on anything that supports the intrinsic interface the client expects. [20]

More generally, dependency injection reduces boilerplate code, since all dependency creation is handled by a singular component. [19]

Finally, dependency injection allows concurrent development. Two developers can independently develop classes that use each other, while only needing to know the interface the classes will communicate through. Plugins are often developed by third-parties that never even talk to developers of the original product. [21]

Testing

Many of dependency injection's benefits are particularly relevant to unit-testing.

For example, dependency injection can be used to externalize a system's configuration details into configuration files, allowing the system to be reconfigured without recompilation. Separate configurations can be written for different situations that require different implementations of components. [22]

Similarly, because dependency injection does not require any change in code behavior, it can be applied to legacy code as a refactoring. This makes clients more independent and are easier to unit test in isolation, using stubs or mock objects, that simulate other objects not under test.

This ease of testing is often the first benefit noticed when using dependency injection. [23]

Disadvantages

Critics of dependency injection argue that it:

Types of dependency injection

There are three main ways in which a client can receive injected services: [29]

In some frameworks, clients do not need to actively accept dependency injection at all. In Java, for example, reflection can make private attributes public when testing and inject services directly. [30]

Without dependency injection

In the following Java example, the Client class contains a Service member variable initialized in the constructor. The client directly constructs and controls which service it uses, creating a hard-coded dependency.

publicclassClient{privateServiceservice;Client(){// The dependency is hard-coded.this.service=newExampleService();}}

Constructor injection

The most common form of dependency injection is for a class to request its dependencies through its constructor. This ensures the client is always in a valid state, since it cannot be instantiated without its necessary dependencies.

publicclassClient{privateServiceservice;// The dependency is injected through a constructor.Client(Serviceservice){if(service==null){thrownewIllegalArgumentException("service must not be null");}this.service=service;}}

Setter injection

By accepting dependencies through a setter method, rather than a constructor, clients can allow injectors to manipulate their dependencies at any time. This offers flexibility, but makes it difficult to ensure that all dependencies are injected and valid before the client is used.

publicclassClient{privateServiceservice;// The dependency is injected through a setter method.publicvoidsetService(Serviceservice){if(service==null){thrownewIllegalArgumentException("service must not be null");}this.service=service;}}

Interface injection

With interface injection, dependencies are completely ignorant of their clients, yet still send and receive references to new clients.

In this way, the dependencies become injectors. The key is that the injecting method is provided through an interface.

An assembler is still needed to introduce the client and its dependencies. The assembler takes a reference to the client, casts it to the setter interface that sets that dependency, and passes it to that dependency object which in turn passes a reference to itself back to the client.

For interface injection to have value, the dependency must do something in addition to simply passing back a reference to itself. This could be acting as a factory or sub-assembler to resolve other dependencies, thus abstracting some details from the main assembler. It could be reference-counting so that the dependency knows how many clients are using it. If the dependency maintains a collection of clients, it could later inject them all with a different instance of itself.

publicinterfaceServiceSetter{voidsetService(Serviceservice);}publicclassClientimplementsServiceSetter{privateServiceservice;@OverridepublicvoidsetService(Serviceservice){if(service==null){thrownewIllegalArgumentException("service must not be null");}this.service=service;}}publicclassServiceInjector{privatefinalSet<ServiceSetter>clients=newHashSet<>();publicvoidinject(ServiceSetterclient){this.clients.add(client);client.setService(newExampleService());}publicvoidswitch(){for(Clientclient:this.clients){client.setService(newAnotherExampleService());}}}publicclassExampleServiceimplementsService{}publicclassAnotherExampleServiceimplementsService{}

Assembly

The simplest way of implementing dependency injection is to manually arrange services and clients, typically done at the program's root, where execution begins.

publicclassProgram{publicstaticvoidmain(String[]args){// Build the service.Serviceservice=newExampleService();// Inject the service into the client.Clientclient=newClient(service);// Use the objects.System.out.println(client.greet());}}

Manual construction may be more complex and involve builders, factories, or other construction patterns.

Frameworks

Containers such as Ninject or StructureMap are commonly used in object-oriented programming languages to achieve Dependency Injection and inversion of control. DependencyInjectionServiceProvider.png
Containers such as Ninject or StructureMap are commonly used in object-oriented programming languages to achieve Dependency Injection and inversion of control.

Manual dependency injection is often tedious and error-prone for larger projects, promoting the use of frameworks which automate the process. Manual dependency injection becomes a dependency injection framework once the constructing code is no longer custom to the application and is instead universal. [31] While useful, these tools are not required in order to perform dependency injection. [32] [33]

Some frameworks, like Spring, can use external configuration files to plan program composition:

importorg.springframework.beans.factory.BeanFactory;importorg.springframework.context.ApplicationContext;importorg.springframework.context.support.ClassPathXmlApplicationContext;publicclassInjector{publicstaticvoidmain(String[]args){// Details about which concrete service to use are stored in configuration separate from the program itself.BeanFactorybeanfactory=newClassPathXmlApplicationContext("Beans.xml");Clientclient=(Client)beanfactory.getBean("client");System.out.println(client.greet());}}

Even with a potentially long and complex object graph, the only class mentioned in code is the entry point, in this case Client.Client has not undergone any changes to work with Spring and remains a POJO. [34] [35] [36] By keeping Spring-specific annotations and calls from spreading out among many classes, the system stays only loosely dependent on Spring. [27]

Examples

AngularJS

The following example shows an AngularJS component receiving a greeting service through dependency injection.

functionSomeClass(greeter){this.greeter=greeter;}SomeClass.prototype.doSomething=function(name){this.greeter.greet(name);}

Each AngularJS application contains a service locator responsible for the construction and look-up of dependencies.

// Provide the wiring information in a modulevarmyModule=angular.module('myModule',[]);// Teach the injector how to build a greeter service. // greeter is dependent on the $window service.myModule.factory('greeter',function($window){return{greet:function(text){$window.alert(text);}};});

We can then create a new injector that provides components defined in the myModule module, including the greeter service.

varinjector=angular.injector(['myModule','ng']);vargreeter=injector.get('greeter');

To avoid the service locator antipattern, AngularJS allows declarative notation in HTML templates which delegates creating components to the injector.

<divng-controller="MyController"><buttonng-click="sayHello()">Hello</button></div>
functionMyController($scope,greeter){$scope.sayHello=function(){greeter.greet('Hello World');};}

The ng-controller directive triggers the injector to create an instance of the controller and its dependencies.

C#

This sample provides an example of constructor injection in C#.

usingSystem;namespaceDependencyInjection;// Our client will only know about this interface, not which specific gamepad it is using.interfaceIGamepadFunctionality{stringGetGamepadName();voidSetVibrationPower(floatpower);}// The following services provide concrete implementations of the above interface.classXBoxGamepad:IGamepadFunctionality{floatvibrationPower=1.0f;publicstringGetGamepadName()=>"Xbox controller";publicvoidSetVibrationPower(floatpower)=>this.vibrationPower=Math.Clamp(power,0.0f,1.0f);}classPlaystationJoystick:IGamepadFunctionality{floatvibratingPower=100.0f;publicstringGetGamepadName()=>"PlayStation controller";publicvoidSetVibrationPower(floatpower)=>this.vibratingPower=Math.Clamp(power*100.0f,0.0f,100.0f);}classSteamController:IGamepadFunctionality{doublevibrating=1.0;publicstringGetGamepadName()=>"Steam controller";publicvoidSetVibrationPower(floatpower)=>this.vibrating=Convert.ToDouble(Math.Clamp(power,0.0f,1.0f));}// This class is the client which receives a service.classGamepad{IGamepadFunctionalitygamepadFunctionality;// The service is injected through the constructor and stored in the above field.publicGamepad(IGamepadFunctionalitygamepadFunctionality)=>this.gamepadFunctionality=gamepadFunctionality;publicvoidShowcase(){// The injected service is used.vargamepadName=this.gamepadFunctionality.GetGamepadName();varmessage=$"We're using the {gamepadName} right now, do you want to change the vibrating power?";Console.WriteLine(message);}}classProgram{staticvoidMain(){varsteamController=newSteamController();// We could have also passed in an XboxController, PlaystationJoystick, etc.// The gamepad doesn't know what it's using and doesn't need to.vargamepad=newGamepad(steamController);gamepad.Showcase();}}

Go

Go does not support classes and usually dependency injection is either abstracted by a dedicated library that utilizes reflection or generics (the latter being supported since Go 1.18 [37] ). [38] A simpler example without using dependency injection libraries is illustrated by the following example of an MVC web application.

First, pass the necessary dependencies to a router and then from the router to the controllers:

packagerouterimport("database/sql""net/http""example/controllers/users""github.com/go-chi/chi/v5""github.com/go-chi/chi/v5/middleware""github.com/redis/go-redis/v9""github.com/rs/zerolog")typeRoutingHandlerstruct{// passing the values by pointer further down the call stack// means we won't create a new copy, saving memorylog*zerolog.Loggerdb*sql.DBcache*redis.Clientrouterchi.Router}// connection, logger and cache initialized usually in the main functionfuncNewRouter(log*zerolog.Logger,db*sql.DB,cache*redis.Client,)(r*RoutingHandler){rtr:=chi.NewRouter()return&RoutingHandler{log:log,db:db,cache:cache,router:rtr,}}func(r*RoutingHandler)SetupUsersRoutes(){uc:=users.NewController(r.log,r.db,r.cache)r.router.Get("/users/:name",func(whttp.ResponseWriter,r*http.Request){uc.Get(w,r)})}

Then, you can access the private fields of the struct in any method that is it's pointer receiver, without violating encapsulation.

packageusersimport("database/sql""net/http""example/models""github.com/go-chi/chi/v5""github.com/redis/go-redis/v9""github.com/rs/zerolog")typeControllerstruct{log*zerolog.Loggerstoragemodels.UserStoragecache*redis.Client}funcNewController(log*zerolog.Logger,db*sql.DB,cache*redis.Client)*Controller{return&Controller{log:log,storage:models.NewUserStorage(db),cache:cache,}}func(uc*Controller)Get(whttp.ResponseWriter,r*http.Request){// note that we can also wrap logging in a middleware, this is for demonstration purposesuc.log.Info().Msg("Getting user")userParam:=chi.URLParam(r,"name")varuser*models.User// get the user from the cacheerr:=uc.cache.Get(r.Context(),userParam).Scan(&user)iferr!=nil{uc.log.Error().Err(err).Msg("Error getting user from cache. Retrieving from SQL storage")}user,err=uc.storage.Get(r.Context(),"johndoe")iferr!=nil{uc.log.Error().Err(err).Msg("Error getting user from SQL storage")http.Error(w,"Internal server error",http.StatusInternalServerError)return}}

Finally you can use the database connection initialized in your main function at the data access layer:

packagemodelsimport("database/sql""time")type(UserStoragestruct{conn*sql.DB}Userstruct{Namestring`json:"name" db:"name,primarykey"`JoinedAttime.Time`json:"joined_at" db:"joined_at"`Emailstring`json:"email" db:"email"`})funcNewUserStorage(conn*sql.DB)*UserStorage{return&UserStorage{conn:conn,}}func(us*UserStorage)Get(namestring)(user*User,errerror){// assuming 'name' is a unique keyquery:="SELECT * FROM users WHERE name = $1"iferr:=us.conn.QueryRow(query,name).Scan(&user);err!=nil{returnnil,err}returnuser,nil}

See also

Related Research Articles

<span class="mw-page-title-main">Jakarta EE</span> Set of specifications extending Java SE

Jakarta EE, formerly Java Platform, Enterprise Edition and Java 2 Platform, Enterprise Edition (J2EE), is a set of specifications, extending Java SE with specifications for enterprise features such as distributed computing and web services. Jakarta EE applications are run on reference runtimes, which can be microservices or application servers, which handle transactions, security, scalability, concurrency and management of the components they are deploying.

Memcached is a general-purpose distributed memory-caching system. It is often used to speed up dynamic database-driven websites by caching data and objects in RAM to reduce the number of times an external data source must be read. Memcached is free and open-source software, licensed under the Revised BSD license. Memcached runs on Unix-like operating systems and on Microsoft Windows. It depends on the libevent library.

Web development is the work involved in developing a website for the Internet or an intranet. Web development can range from developing a simple single static page of plain text to complex web applications, electronic businesses, and social network services. A more comprehensive list of tasks to which Web development commonly refers, may include Web engineering, Web design, Web content development, client liaison, client-side/server-side scripting, Web server and network security configuration, and e-commerce development.

In software engineering, inversion of control (IoC) is a design principle in which custom-written portions of a computer program receive the flow of control from a generic framework. The term "inversion" is historical: a software architecture with this design "inverts" control as compared to procedural programming. In procedural programming, a program's custom code calls reusable libraries to take care of generic tasks, but with inversion of control, it is the framework that calls the custom code.

A web framework (WF) or web application framework (WAF) is a software framework that is designed to support the development of web applications including web services, web resources, and web APIs. Web frameworks provide a standard way to build and deploy web applications on the World Wide Web. Web frameworks aim to automate the overhead associated with common activities performed in web development. For example, many web frameworks provide libraries for database access, templating frameworks, and session management, and they often promote code reuse. Although they often target development of dynamic web sites, they are also applicable to static websites.

IBM Storage Protect is a data protection platform that gives enterprises a single point of control and administration for backup and recovery. It is the flagship product in the IBM Spectrum Protect family.

The object pool pattern is a software creational design pattern that uses a set of initialized objects kept ready to use – a "pool" – rather than allocating and destroying them on demand. A client of the pool will request an object from the pool and perform operations on the returned object. When the client has finished, it returns the object to the pool rather than destroying it; this can be done manually or automatically.

In object-oriented design, the dependency inversion principle is a specific methodology for loosely coupled software modules. When following this principle, the conventional dependency relationships established from high-level, policy-setting modules to low-level, dependency modules are reversed, thus rendering high-level modules independent of the low-level module implementation details. The principle states:

The Spring Framework is an application framework and inversion of control container for the Java platform. The framework's core features can be used by any Java application, but there are extensions for building web applications on top of the Java EE platform. The framework does not impose any specific programming model.. The framework has become popular in the Java community as an addition to the Enterprise JavaBeans (EJB) model. The Spring Framework is free and open source software.

The Doctrine Project is a set of PHP libraries primarily focused on providing persistence services and related functionality. Its most commonly known projects are the object–relational mapper (ORM) and the database abstraction layer it is built on top of.

Web2py is an open-source web application framework written in the Python programming language. Web2py allows web developers to program dynamic web content using Python. Web2py is designed to help reduce tedious web development tasks, such as developing web forms from scratch, although a web developer may build a form from scratch if required.


The service locator pattern is a design pattern used in software development to encapsulate the processes involved in obtaining a service with a strong abstraction layer. This pattern uses a central registry known as the "service locator", which on request returns the information necessary to perform a certain task. Proponents of the pattern say the approach simplifies component-based applications where all dependencies are cleanly listed at the beginning of the whole application design, consequently making traditional dependency injection a more complex way of connecting objects. Critics of the pattern argue that it is an anti-pattern which obscures dependencies and makes software harder to test.

Redis is a source-available, in-memory storage, used as a distributed, in-memory key–value database, cache and message broker, with optional durability. Because it holds all data in memory and because of its design, Redis offers low-latency reads and writes, making it particularly suitable for use cases that require a cache. Redis is the most popular NoSQL database, and one of the most popular databases overall. Redis is used in companies like Twitter, Airbnb, Tinder, Yahoo, Adobe, Hulu, Amazon and OpenAI.

<span class="mw-page-title-main">Couchbase Server</span> Open-source NoSQL database

Couchbase Server, originally known as Membase, is a source-available, distributed multi-model NoSQL document-oriented database software package optimized for interactive applications. These applications may serve many concurrent users by creating, storing, retrieving, aggregating, manipulating and presenting data. In support of these kinds of application needs, Couchbase Server is designed to provide easy-to-scale key-value, or JSON document access, with low latency and high sustainability throughput. It is designed to be clustered from a single machine to very large-scale deployments spanning many machines.

MyBatis is a Java persistence framework that couples objects with stored procedures or SQL statements using an XML descriptor or annotations.

<span class="mw-page-title-main">ColdBox Platform</span> Web application framework

ColdBox is a free, open-source, conventions-based, modular web application framework intended for building enterprise applications with ColdFusion (CFML) using a Hierarchical MVC approach.

In database management systems (DBMS), a prepared statement, parameterized statement, or parameterized query is a feature where the database pre-compiles SQL code and stores the results, separating it from data. Benefits of prepared statements are:

Elliptics is a distributed key–value data storage with open source code. By default it is a classic distributed hash table (DHT) with multiple replicas put in different groups. Elliptics was created to meet requirements of multi-datacenter and physically distributed storage locations when storing huge amount of medium and large files.

Amazon ElastiCache is a fully managed in-memory data store and cache service by Amazon Web Services (AWS). The service improves the performance of web applications by retrieving information from managed in-memory caches, instead of relying entirely on slower disk-based databases. ElastiCache supports two open-source in-memory caching engines: Memcached and Redis.

<span class="mw-page-title-main">Valkey</span> Freely available in-memory key–value database

Valkey is an open-source in-memory storage, used as a distributed, in-memory key–value database, cache and message broker, with optional durability. Because it holds all data in memory and because of its design, Valkey offers low-latency reads and writes, making it particularly suitable for use cases that require a cache. Valkey is the successor to Redis, the most popular NoSQL database, and one of the most popular databases overall. Valkey or its predecessor Redis are used in companies like Twitter, Airbnb, Tinder, Yahoo, Adobe, Hulu, Amazon and OpenAI.

References

  1. Seemann, Mark. "Dependency Injection is Loose Coupling". blog.ploeh.dk. Retrieved 2015-07-28.
  2. 1 2 Seeman, Mark (October 2011). Dependency Injection in .NET. Manning Publications. p. 4. ISBN   9781935182504.
  3. Niko Schwarz, Mircea Lungu, Oscar Nierstrasz, “Seuss: Decoupling responsibilities from static methods for fine-grained configurability”, Journal of Object Technology, Volume 11, no. 1 (April 2012), pp. 3:1-23
  4. "HollywoodPrinciple". c2.com. Retrieved 2015-07-19.
  5. "The Dependency Injection design pattern - Problem, Solution, and Applicability". w3sDesign.com. Retrieved 2017-08-12.
  6. Erez, Guy (2022-03-09). "Dependency Inversion vs. Dependency Injection". Medium. Retrieved 2022-12-06.
  7. Mathews, Sasha (2021-03-25). "You are Simply Injecting a Dependency, Thinking that You are Following the Dependency Inversion…". Medium. Retrieved 2022-12-06.
  8. "Spring IoC Container" . Retrieved 2023-05-23.
  9. Fowler, Martin. "Inversion of Control Containers and the Dependency Injection pattern". MartinFowler.com. Retrieved 4 June 2023.
  10. "Dependency Injection in NET" (PDF). philkildea.co.uk. p. 4. Archived from the original (PDF) on 2015-07-21. Retrieved 2015-07-18.
  11. "How to explain dependency injection to a 5-year-old?". stackoverflow.com. Retrieved 2015-07-18.
  12. I.T., Titanium. "James Shore: Dependency Injection Demystified". www.jamesshore.com. Retrieved 2015-07-18.
  13. "To "new" or not to "new"…". Archived from the original on 2020-05-13. Retrieved 2015-07-18.
  14. "How to write testable code". www.loosecouplings.com. Retrieved 2015-07-18.
  15. "Writing Clean, Testable Code". www.ethanresnick.com. Retrieved 2015-07-18.
  16. Sironi, Giorgio. "When to inject: the distinction between newables and injectables - Invisible to the eye". www.giorgiosironi.com. Retrieved 2015-07-18.
  17. "the urban canuk, eh: On Dependency Injection and Violating Encapsulation Concerns". www.bryancook.net. Retrieved 2015-07-18.
  18. "The Dependency Injection Design Pattern". msdn.microsoft.com. Retrieved 2015-07-18.
  19. 1 2 "The Java Community Process(SM) Program - JSRs: Java Specification Requests - detail JSR# 330". jcp.org. Retrieved 2015-07-18.
  20. "3.1. Dependency injection — Python 3: from None to Machine Learning". Archived from the original on 2020-02-08.
  21. 1 2 3 "How Dependency Injection (DI) Works in Spring Java Application Development - DZone Java".
  22. "Dependency injection and inversion of control in Python — Dependency Injector 4.36.2 documentation".
  23. "How to Refactor for Dependency Injection, Part 3: Larger Applications".
  24. "A quick intro to Dependency Injection: What it is, and when to use it". 18 October 2018.
  25. "Dependency Injection |Professionalqa.com".
  26. "What are the downsides to using Dependency Injection?". stackoverflow.com. Retrieved 2015-07-18.
  27. 1 2 "Dependency Injection Inversion – Clean Coder". sites.google.com. Retrieved 2015-07-18.
  28. "Decoupling Your Application From Your Dependency Injection Framework". InfoQ. Retrieved 2015-07-18.
  29. Martin Fowler (2004-01-23). "Inversion of Control Containers and the Dependency Injection pattern – Forms of Dependency Injection". Martinfowler.com. Retrieved 2014-03-22.
  30. "AccessibleObject (Java Platform SE 7)". docs.oracle.com. Retrieved 2015-07-18.
  31. Riehle, Dirk (2000), Framework Design: A Role Modeling Approach (PDF), Swiss Federal Institute of Technology
  32. "Dependency Injection != using a DI container". www.loosecouplings.com. Retrieved 2015-07-18.
  33. "Black Sheep » DIY-DI » Print". blacksheep.parry.org. Archived from the original on 2015-06-27. Retrieved 2015-07-18.
  34. "Spring Tips: A POJO with annotations is not Plain". Archived from the original on 2015-07-15. Retrieved 2015-07-18.
  35. "Annotations in POJO – a boon or a curse? | Techtracer". 2007-04-07. Retrieved 2015-07-18.
  36. Pro Spring Dynamic Modules for OSGi Service Platforms. APress. 2009-02-17. ISBN   9781430216124 . Retrieved 2015-07-06.
  37. "Go 1.18 Release Notes - The Go Programming Language". go.dev. Retrieved 2024-04-17.
  38. "Awesome Go – dependency injection". Github. April 17, 2024. Retrieved April 17, 2024.