Dependency injection

From Wikipedia, the free encyclopedia

In software engineering, dependency injection is a technique in which an object receives other objects that it depends on, called dependencies. Typically, the receiving object is called a client and the passed-in ('injected') object is called a service. The code that passes the service to the client is called the injector. Instead of the client specifying which service it will use, the injector tells the client what service to use. The 'injection' refers to the passing of a dependency (a service) into the client that uses it.

The service is made part of the client's state.[1] Passing the service to the client, rather than allowing the client to build or find the service, is the fundamental requirement of the pattern.

The intent behind dependency injection is to achieve separation of concerns of construction and use of objects. This can increase readability and code reuse.

Dependency injection is one form of the broader technique of inversion of control. A client who wants to call some services should not have to know how to construct those services. Instead, the client delegates to external code (the injector). The client is not aware of the injector.[2] The injector passes the services, which might exist or be constructed by the injector itself, to the client. The client then uses the services.

This means the client does not need to know about the injector, how to construct the services, or even which services it is actually using. The client only needs to know the interfaces of the services, because these define how the client may use the services. This separates the responsibility of 'use' from the responsibility of 'construction'.

Overview[]

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.

John Munsch, 28 October 2009.[3][4][5]

Dependency injection solves the following problems:[6]

  • How can a class be independent from the creation of the objects it depends on?
  • How can the way objects are created be specified in separate configuration files?
  • How can an application support different configurations?

Creating objects directly within the class commits the class to particular implementations. This makes it difficult to change the instantiation at runtime, especially in compiled languages where changing the underlying objects can require re-compiling the source code.

Dependency injection separates the creation of a client's dependencies from the client's behavior, which promotes loosely coupled programs[7] and the dependency inversion and single responsibility principles.[3][8] Fundamentally, dependency injection is based on passing parameters to a method.[9]

Dependency injection is an example of the more general concept of inversion of control.

An example of inversion of control without dependency injection is the template method pattern, where polymorphism is achieved through subclassing.[10] Dependency injection implements inversion of control through composition, and is often similar to the strategy pattern. While the strategy pattern is intended for dependencies that are interchangeable throughout an object's lifetime, in dependency injection it may be that only a single instance of a dependency is used.[11] This still achieves polymorphism, but through delegation and composition.

Dependency injection directly contrasts with the service locator pattern, where clients know about the system they use to find dependencies.

Roles[]

Dependency injection involves four roles:

  • the service objects, which contain useful functionality
  • the interfaces by which those services are known to other parts of the code
  • the client object, whose behavior depends on the services it uses
  • the injector, which constructs the services and injects them into the client

As an analogy,

  • service - an electric, gas, hybrid, or diesel car
  • interface - automatic transmission, which ensures the driver does not have to understand details of shifting gears
  • client - a driver who uses the car the same way regardless of the engine
  • injector - the parent who decided what kind of car to buy for the driver

Any object that may be used can be considered a service. Any object that uses other objects can be considered a client. The names relate only to the role the objects play in an injection.

Furthermore, the same object might be both a client (it requires services to be injected) and a service (it is injected into other objects).

Interfaces[]

The interfaces are the types the client expects its dependencies to be.

The client should not know the specific implementation of its dependencies, only its name and API. As a result, the client will not need to change even if what is behind the interface changes.

Dependency injection can work with true interfaces or abstract classes, but also concrete services, though this would violate the dependency inversion principle[12] and sacrifice the dynamic decoupling that enables testing. It is only required that the client never treats its interfaces as concrete by constructing or extending them. If the interface is refactored from a class to an interface type (or vice versa) the client will need to be recompiled.[13] This is significant if the client and services are published separately.

Injection[]

The injector introduces services to the client. Often, it also constructs the services.

Injectors can connect complex object graphs where the same object is both a client at one point and as a service at another. The injector itself may actually be many objects working together, but may not be the client (as this would create a circular dependency). The injector may be referred to as an assembler, provider, container, factory, builder, spring, or construction code.

As a discipline, dependency injection separates how objects are constructed from how they are used. As a result, dependency injection frameworks often diminish the importance of the new keyword found in most object-oriented languages. Less strictly, the programmer only directly constructs value objects.[14][15][16][17]

Structure[]

A sample UML class and sequence diagram for the Dependency Injection design pattern.[18]

In the above UML class diagram, the Client class requires ServiceA and ServiceB, but does not instantiate them directly.

Instead, an Injector class creates the objects and injects them into the Client. The Client is independent of which classes are instantiated.
The UML sequence diagram on the right shows the run-time interactions:

  1. The Injector object creates the ServiceA1 and ServiceB1 objects.
  2. The Injector creates the Client object.
  3. The Injector injects the ServiceA1 and ServiceB1 objects into the Client object.

Advantages and disadvantages[]

Advantages[]

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

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

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

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

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.[23]

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.[24]

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.[25]

Disadvantages[]

Criticisms of dependency injection argue that it:

  • Creates clients that demand configuration details, which can be onerous when obvious defaults are available.[23]
  • Makes code difficult to trace because it separates behavior from construction.[23]
  • Is typically implemented with reflection or dynamic programming. This can hinder IDE automation.[26]
  • Typically requires more upfront development effort.[27]
  • Forces complexity out of classes and into the links between classes which might be harder to manage.[28]
  • Encourage dependence on a framework.[28][29][30]

Types of dependency injection[]

There are at least three ways a client can receive a reference to an external module:[31]

  • Constructor injection: The dependencies are provided through a client's class constructor.
  • Setter injection: The client exposes a setter method that the injector uses to inject the dependency.
  • Interface injection: The dependency's interface provides an injector method that will inject the dependency into any client passed to it.

DI frameworks and testing frameworks can use other types of injection.[32]

Some modern testing frameworks do not even require that clients actively accept dependency injection, thus making legacy code testable. In Java, reflection can make private attributes public when testing and thus accept injections by assignment.[33]

Some attempts at Inversion of Control simply substitute one form of dependency for another. As a rule of thumb, if a programmer can look at nothing but the client code and tell what framework is being used, then the client has a hard-coded dependency on the framework.

Without dependency injection[]

In the following Java example, the Client class contains a Service member variable initialized in the constructor. The client controls which service is used and its construction. The client has a hard-coded dependency on ExampleService.

public class Client {
    // Internal reference to the service used by this client
    private ExampleService service;

    // Constructor
    Client() {
        // Specify a specific implementation instead of using dependency injection
        service = new ExampleService();
    }

    // Method that uses the services
    public String greet() {
        return "Hello " + service.getName();
    }
}

We can adjust this example using the techniques described below.

Constructor injection[]

This method requires the client to provide a parameter in a constructor for the dependency. This ensures the client object is always in a valid state, as opposed to having some of its dependencies be null or not set. This can be a first step towards making the client immutable and therefore thread safe. However, on its own, this pattern lacks the flexibility to have its dependencies changed later.

// Constructor
Client(Service service, Service otherService) {
    if (service == null) {
        throw new InvalidParameterException("service must not be null");
    }
    if (otherService == null) {
        throw new InvalidParameterException("otherService must not be null");
    }

    // Save the service references inside this client
    this.service = service;
    this.otherService = otherService;
}

Setter injection[]

This method requires the client to provide a setter method for the dependency. Injectors can then manipulate the dependency references at any time.

This offers flexibility, but it is difficult to ensure that all dependencies are injected before the client is used. Because these injections happen independently, a dependency can be left null simply by the injector failing to call its setter. In contrast, a dependency declared in a constructor means an object cannot be constructed unless the dependency is satisfied.

The following example shows a way for a client to check that injection was completed when used.

// Set the service to be used by this client
public void setService(Service service) {
    this.service = service;
}

// Set the other service to be used by this client
public void setOtherService(Service otherService) {
    this.otherService = otherService;
}

// Check the service references of this client
private void validateState() {
    if (service == null) {
        throw new IllegalStateException("service must not be null");
    }
    if (otherService == null) {
        throw new IllegalStateException("otherService must not be null");
    }
}

// Method that uses the service references
public void doSomething() {
    validateState();
    service.doYourThing();
    otherService.doYourThing();
}

Interface injection[]

With interface injection, dependencies can be 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 (which could just be a setter 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-self 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.

// Service setter interface.
public interface ServiceSetter {
    public void setService(Service service);
}

// Client class
public class Client implements ServiceSetter {
    // Internal reference to the service used by this client.
    private Service service;

    // Set the service that this client is to use.
    @Override
    public void setService(Service service) {
        this.service = service;
    }
}

// Injector class
public class ServiceInjector {
	Set<ServiceSetter> clients;
	public void inject(ServiceSetter client) {
		clients.add(client);
		client.setService(new ServiceFoo());
	}
	public void switchToBar() {
		for (Client client : clients) {
			client.setService(new ServiceBar());
		}
	}
}

// Service classes
public class ServiceFoo implements Service {}
public class ServiceBar implements Service {}

Assembly[]

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

public class Injector {
    public static void main(String[] args) {
        // Build the dependencies first
        Service service = new ExampleService();

        // Inject the service, constructor style
        Client client = new Client(service);

        // Use the objects
        System.out.println(client.greet());
    }	
}

The above example constructs the object graph manually and then invokes it. This injector is not 'pure', as it uses one of the objects it constructs (Client).

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

Frameworks[]

A class diagram of dependency injection containers in the .NET Framework.
Containers such as Ninject or Structure map are commonly used in object-oriented programming languages to achieve inversion of control.

Manual dependency injection becomes a dependency injection framework once the constructing code is no longer custom to the application and is instead universal.[34]

Application frameworks such as Weld, Spring, Guice, Play framework, Salta, Glassfish HK2, Dagger, and Managed Extensibility Framework support dependency injection but are not required to do dependency injection.[35][36]

Frameworks like Spring can construct objects and wire them together before returning a reference to client. Because frameworks like Spring allow assembly details to be externalized in configuration files, all mentions of the concrete ExampleService can be moved from code to configuration data:

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.support.ClassPathXmlApplicationContext;

public class Injector {
	public static void main(String[] args) {
		// -- Assembling objects -- //
		BeanFactory beanfactory = new ClassPathXmlApplicationContext("Beans.xml");
		Client client = (Client) beanfactory.getBean("client");

		// -- Using objects -- //
		System.out.println(client.greet());
	}
}

Even with a long and complex object graph, the only class mentioned in code is the entry point, in this case Client.

 <?xml version="1.0" encoding="UTF-8"?>
 <beans xmlns="http://www.springframework.org/schema/beans"
  xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
  xsi:schemaLocation="http://www.springframework.org/schema/beans
  http://www.springframework.org/schema/beans/spring-beans-3.0.xsd">

    <bean id="service" class="ExampleService">
    </bean>

    <bean id="client" class="Client">
        <constructor-arg value="service" />        
    </bean>
</beans>

Client and Service have not undergone any changes to work with Spring and remain POJOs.[37][38][39] Spring can connect services and clients that are completely ignorant of its existence. By keeping Spring-specific annotations and calls from spreading out among many classes, the system stays only loosely dependent on Spring.[29]

Keeping POJOs pure requires effort. Rather than maintaining complex configuration files, classes can use annotations to let Spring do the hard work using convention over configuration.[40]

import org.springframework.beans.factory.BeanFactory;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class Injector {
	public static void main(String[] args) {
		// Assemble the objects
		BeanFactory beanfactory = new AnnotationConfigApplicationContext(MyConfiguration.class);
		Client client = beanfactory.getBean(Client.class);

		// Use the objects
		System.out.println(client.greet());
	}
}
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.Configuration;

@ComponentScan
public class MyConfiguration {
    @Bean
    public Client client(ExampleService service) {
        return new Client(service);
    }
}
@Component
public class ExampleService {
    public String getName() {
        return "World!";
    }
}

Different injectors (factories, service locators, and dependency injection containers) differ mainly in where they can be used. Moving calls to a factory or a service locator out of the client and into the program root is equivalent to using a dependency injection container.

By removing knowledge of the injector, the client is free of knowledge of the outside world, is left behind. However, any object that uses other objects can be considered a client, including the object that contains the program root. As a result, main in the above example is actually using the service locator pattern. This cannot be avoided because the choice of service implementations must be made somewhere.

Examples[]

AngularJS[]

In AngularJS, there are only three ways a component (object or function) can directly access its dependencies:

  1. The component can create the dependency, typically using the new operator.
  2. The component can look up the dependency, by referring to a global variable.
  3. The component can have the dependency passed to it where it is needed.

The first two options are not optimal because they hard-code the dependency to the component. This is problematic in tests, where it is often desirable to mock dependencies for test isolation.

The third option removes the responsibility of locating the dependency from the component.

function SomeClass(greeter) {
  this.greeter = greeter;
}

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

SomeClass is simply handed the greeter when instantiated. This gives the responsibility of dependency construction to the code that constructs SomeClass.

To manage this, each AngularJS application has an 'injector': a service locator responsible for construction and look-up of dependencies.

// Provide the wiring information in a module
var myModule = angular.module('myModule', []);

// Teach the injector how to build a greeter service. 
// greeter is dependent on the $window service. 
// The greeter service is an object that
// contains a greet method.
myModule.factory('greeter', function($window) {
  return {
    greet: function(text) {
      $window.alert(text);
    }
  };
});

Create a new injector that can provide components defined in the myModule module and request our greeter service from the injector.

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

Asking for dependencies solves the issue of hard coding, but it also means that the injector needs to be passed throughout the application. Passing the injector breaks the Law of Demeter. To remedy this, we use a declarative notation in our HTML templates, to hand the responsibility of creating components over to the injector:

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

When AngularJS compiles the HTML, it processes the ng-controller directive, which in turn asks the injector to create an instance of the controller and its dependencies.

injector.instantiate(MyController);

Because the ng-controller defers to the injector to instantiate the class, it can satisfy the dependencies of MyController without the controller knowing about the injector. The application code simply declares the dependencies it needs, preserving the Law of Demeter.

C#[]

Example of the Constructor injection, Setter injection and Interface injection on C#

using System;

namespace DependencyInjection;

// An interface for the library
interface IGamepadFunctionality
{
    string GetGamepadName();
    void SetVibrationPower(float InPower);
}

// Concrete implementation of the Xbox controller functionality
class XBoxGamepad : IGamepadFunctionality
{
    readonly string GamepadName = "Xbox controller";
    float VibrationPower = 1.0f;
    public string GetGamepadName() => GamepadName;
    public void SetVibrationPower(float InPower) => VibrationPower = Math.Clamp(InPower, 0.0f, 1.0f);

}

// Concrete implementation of the PlayStation controller functionality
class PlaystationJoystick : IGamepadFunctionality
{
    readonly string ControllerName = "PlayStation controller";
    float VibratingPower = 100.0f;
    public string GetGamepadName() => ControllerName;
    public void SetVibrationPower(float InPower) => VibratingPower = Math.Clamp(InPower * 100.0f, 0.0f, 100.0f);
}

// Concrete implementation of the Steam controller functionality
class SteamController : IGamepadFunctionality
{
    readonly string JoystickName = "Steam controller";
    double Vibrating = 1.0;
    public string GetGamepadName() => JoystickName;
    public void SetVibrationPower(float InPower) => Vibrating = Convert.ToDouble(Math.Clamp(InPower, 0.0f, 1.0f));
}

// An interface for gamepad functionality injections
interface IGamepadFunctionalityInjector
{
    void InjectFunctionality(IGamepadFunctionality InGamepadFunctionality);
}

class CGamepad : IGamepadFunctionalityInjector
{
    IGamepadFunctionality _GamepadFunctionality;

    public CGamepad()
    {

    }

    // Constructor injection
    public CGamepad(IGamepadFunctionality InGamepadFunctionality) => _GamepadFunctionality = InGamepadFunctionality;

    // Setter injection
    public void SetGamepadFunctionality(IGamepadFunctionality InGamepadFunctionality) => _GamepadFunctionality = InGamepadFunctionality;

    // Interface injection
    public void InjectFunctionality(IGamepadFunctionality InGamepadFunctionality) => _GamepadFunctionality = InGamepadFunctionality;

    public void Showcase()
    {
        var message = string.Format("We're using the {0} right now, do you want to change the vibrating power?\r\n", _GamepadFunctionality.GetGamepadName());
        Console.WriteLine(message);
    }
}

enum Platforms : byte
{
    Xbox,
    Playstation,
    Steam
}

class CGameEngine
{
    private Platforms _platform;
    private CGamepad _gamepad;

    public void SetPlatform(Platforms inPlatform)
    {
        _platform = inPlatform;
        switch (_platform)
        {
            case Platforms.Xbox:

                // injects dependency on XBoxGamepad class through Constructor Injection
                _gamepad = new CGamepad(new XBoxGamepad());
                break;
            case Platforms.Playstation:
                _gamepad = new CGamepad();

                // injects dependency on PlaystationJoystick class through Setter Injection
                _gamepad.SetGamepadFunctionality(new PlaystationJoystick());
                break;
            case Platforms.Steam:
                _gamepad = new CGamepad();
                // injects dependency on SteamController class through Interface Injection
                _gamepad.InjectFunctionality(new SteamController());
                break;
        }

        _gamepad.Showcase();
    }
}

class Program
{
    static void Main()
    {
        var Engine = new CGameEngine();
        Engine.SetPlatform(Platforms.Steam);
        Engine.SetPlatform(Platforms.Xbox);
        Engine.SetPlatform(Platforms.Playstation);
    }
}

See also[]

References[]

  1. ^ I.T., Titanium. "James Shore: Dependency Injection Demystified". www.jamesshore.com. Retrieved 2015-07-18.
  2. ^ "HollywoodPrinciple". c2.com. Retrieved 2015-07-19.
  3. ^ a b Seeman, Mark (October 2011). Dependency Injection in .NET. Manning Publications. p. 4. ISBN 9781935182504.
  4. ^ "Dependency Injection in NET" (PDF). philkildea.co.uk. p. 4. Retrieved 2015-07-18.
  5. ^ "How to explain dependency injection to a 5-year-old?". stackoverflow.com. Retrieved 2015-07-18.
  6. ^ "The Dependency Injection design pattern - Problem, Solution, and Applicability". w3sDesign.com. Retrieved 2017-08-12.
  7. ^ Seemann, Mark. "Dependency Injection is Loose Coupling". blog.ploeh.dk. Retrieved 2015-07-28.
  8. ^ 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
  9. ^ "Passing Information to a Method or a Constructor (The Java™ Tutorials > Learning the Java Language > Classes and Objects)". docs.oracle.com. Retrieved 2015-07-18.
  10. ^ "Inversion of Control vs Dependency Injection". stackoverflow.com. Retrieved 2015-08-05.
  11. ^ "What is the difference between Strategy pattern and Dependency Injection?". stackoverflow.com. Retrieved 2015-07-18.
  12. ^ "A curry of Dependency Inversion Principle (DIP), Inversion of Control (IoC), Dependency Injection (DI) and IoC Container - CodeProject". www.codeproject.com. 7 February 2013. Retrieved 2015-08-08.
  13. ^ "How to force "program to an interface" without using a java Interface in java 1.6". programmers.stackexchange.com. Retrieved 2015-07-19.
  14. ^ "To "new" or not to "new"…". Retrieved 2015-07-18.
  15. ^ "How to write testable code". www.loosecouplings.com. Retrieved 2015-07-18.
  16. ^ "Writing Clean, Testable Code". www.ethanresnick.com. Retrieved 2015-07-18.
  17. ^ Sironi, Giorgio. "When to inject: the distinction between newables and injectables - Invisible to the eye". www.giorgiosironi.com. Retrieved 2015-07-18.
  18. ^ "The Dependency Injection design pattern - Structure and Collaboration". w3sDesign.com. Retrieved 2017-08-12.
  19. ^ "the urban canuk, eh: On Dependency Injection and Violating Encapsulation Concerns". www.bryancook.net. Retrieved 2015-07-18.
  20. ^ "The Dependency Injection Design Pattern". msdn.microsoft.com. Retrieved 2015-07-18.
  21. ^ a b "The Java Community Process(SM) Program - JSRs: Java Specification Requests - detail JSR# 330". jcp.org. Retrieved 2015-07-18.
  22. ^ "3.1. Dependency injection — Python 3: from None to Machine Learning". Archived from the original on 2020-02-08.
  23. ^ a b c "How Dependency Injection (DI) Works in Spring Java Application Development - DZone Java".
  24. ^ "Dependency injection and inversion of control in Python — Dependency Injector 4.36.2 documentation".
  25. ^ "How to Refactor for Dependency Injection, Part 3: Larger Applications -".
  26. ^ "A quick intro to Dependency Injection: What it is, and when to use it". 18 October 2018.
  27. ^ "Dependency Injection |Professionalqa.com".
  28. ^ a b "What are the downsides to using Dependency Injection?". stackoverflow.com. Retrieved 2015-07-18.
  29. ^ a b "Dependency Injection Inversion - Clean Coder". sites.google.com. Retrieved 2015-07-18.
  30. ^ "Decoupling Your Application From Your Dependency Injection Framework". InfoQ. Retrieved 2015-07-18.
  31. ^ Martin Fowler (2004-01-23). "Inversion of Control Containers and the Dependency Injection pattern - Forms of Dependency Injection". Martinfowler.com. Retrieved 2014-03-22.
  32. ^ "Yan - Dependency Injection Types". Yan.codehaus.org. Archived from the original on 2013-08-18. Retrieved 2013-12-11.
  33. ^ "AccessibleObject (Java Platform SE 7)". docs.oracle.com. Retrieved 2015-07-18.
  34. ^ Riehle, Dirk (2000), Framework Design: A Role Modeling Approach (PDF), Swiss Federal Institute of Technology
  35. ^ "Dependency Injection != using a DI container". www.loosecouplings.com. Retrieved 2015-07-18.
  36. ^ "Black Sheep » DIY-DI » Print". blacksheep.parry.org. Archived from the original on 2015-06-27. Retrieved 2015-07-18.
  37. ^ "Spring Tips: A POJO with annotations is not Plain". Archived from the original on 2015-07-15. Retrieved 2015-07-18.
  38. ^ "Annotations in POJO – a boon or a curse? | Techtracer". 2007-04-07. Retrieved 2015-07-18.
  39. ^ Pro Spring Dynamic Modules for OSGi Service Platforms. APress. 2009-02-17. ISBN 9781430216124. Retrieved 2015-07-06.
  40. ^ "Captain Debug's Blog: Is 'Convention Over Configuration' Going Too Far?". www.captaindebug.com. Retrieved 2015-07-18.

External links[]

Retrieved from ""