Dependency Injection

Content:

  1. Introduction
  2. Concept
  3. Example
  4. Configuration file
  5. Declaration matching
  6. Configuration code
  7. Evaluation

Introduction:

DI (Dependency Injection) knowledge is a must have for most developers today. It is part of the modern developers standard tool set.

Concept:

DI (Dependency Injection) is one way to achieve IoC (Inversion of Control).

IoC (Inversion of Control) means that generic framework code calls custom code (as opposed to traditional model where custom code calls generic library code). The idea goes back to the 70's or 80's, but the concept and the specific term was made popular by Robert Martin and Martin Fowler in 2004.

DI (Dependency Injection) means that objects does create their own dependencies but receive the dependencies (they get injected). The concept became popular with the Java Spring Framework in 2002-2004.

A DI framework is a framework that facilitate easy DI via some sort of configuration/definition, where the DI framework does the actual injection.

DI has some advantages:

DI has at least one disadvantage:

There are different types of DI:

constructor injection
the DI framework injects via constructire arguments
setter injection
the DI framework injects via setter methods
interface injection
special flavor of setter injection where the setter is defined in an interface so injector does not need to know the class it injects into

A key conecpt for DI is object scope or object reusability.

Most DI frameworks allow defining both singleton objects and non-singleton objects. A singleton object means that everybody get injected the same instance. A non-singleton object means that everybody get injected different instances.

A key feature in DI frameworks is the ability to inject recursively. If A need B injected and B need C injected then A will get a B with a C.

Example:

Problem:

To illustrate the techniques we will look at an application skeleton with a data access layer and a data presenter.

Data access later will have two implementations:

Presenter will have two implementations:

The code will just be a skeleton, but it should be sufficient to illustrate the point about DI.

I will mostly use constructor injection.

Common code:

Code shared by all all variants.

Data.java:

package di.common;

public class Data {
    private int f1;
    private String f2;
    public Data(int f1, String f2) {
        this.f1 = f1;
        this.f2 = f2;
    }
    public int getF1() {
        return f1;
    }
    public String getF2() {
        return f2;
    }
}

DataAccessLayer.java:

package di.common;

import java.util.List;

public interface DataAccessLayer {
    public Data getOneRecord(int f1);
    public List<Data> getAllRecords();
}

Presenter.java:

package di.common;

public interface Presenter {
    public void start();
    public void one(Data o);
    public void finish();
}

Application.java:

package di.common;

public interface Application {
    public void run();
}

Util.java:

package di.common;

import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.Properties;

public class Util {
    public static Properties loadProperties(String fnm) {
        try(InputStream is = new FileInputStream(fnm)) {
            Properties props = new Properties(); 
            props.load(is);
            return props;
        } catch (IOException e) {
            return null;
        }
    }
}

jdbc.properties:

db.conurl = jdbc:mysql://localhost/Test
db.un = root
db.pw = 

html.properties:

html.lbl1 = F1
html.lbl2 = F2

Common.cs:

using System;
using System.Collections.Generic;

namespace DI.Common
{
    public class Data
    {
        public int F1 { get; set; }
        public string F2 { get; set; }
    }
    public interface IDataAccessLayer
    {
       Data GetOneRecord(int f1);
       IList<Data> GetAllRecords();
    }
    public interface IPresenter {
       void Start();
       void One(Data o);
       void Finish();
    }
    public interface IApplication
    {
       void Run();
    }
}

common.py:

class Data:
    def __init__(self, f1, f2):
        self.f1 = f1
        self.f2 = f2

common.php:

<?php

class Data {
    public $f1;
    public $f2;
    public function __construct($f1, $f2) {
        $this->f1 = $f1;
        $this->f2 = $f2;
    }
}

interface DataAccessLayer {
    public function getOneRecord($f1);
    public function getAllRecords();
}

interface Presenter {
    public function start();
    public function one($o);
    public function finish();
}

interface Application {
    public function run();
}

?>

Without DI:

Let us start by implementing without any DI. Objects are initialized where they are used.

MockDataAccessLayer.java:

package di.no;

import java.util.ArrayList;
import java.util.List;

import di.common.Data;
import di.common.DataAccessLayer;

public class MockDataAccessLayer implements DataAccessLayer {
    @Override
    public Data getOneRecord(int f1) {
        return new Data(f1, "X" + f1);
    }

    @Override
    public List<Data> getAllRecords() {
        List<Data> res = new ArrayList<Data>();
        res.add(new Data(1, "A"));
        res.add(new Data(2, "BB"));
        res.add(new Data(3, "CCC"));
        return res;
    }
}

DbDataAccessLayer.java:

package di.no;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import di.common.Data;
import di.common.DataAccessLayer;
import di.common.Util;

public class DbDataAccessLayer implements DataAccessLayer {
    private String conurl;
    private String un;
    private String pw;
    public DbDataAccessLayer() {
        Properties props = Util.loadProperties("/work/jdbc.properties");
        this.conurl = props.getProperty("db.conurl");
        this.un = props.getProperty("db.un");
        this.pw = props.getProperty("db.pw");
    }
    @Override
    public Data getOneRecord(int f1) {
        try {
            try(Connection con = DriverManager.getConnection(conurl, un, pw)) {
                PreparedStatement pstmt = con.prepareStatement("SELECT f1,f2 FROM t1 WHERE f1 = ?");
                pstmt.setInt(1,  f1);
                ResultSet rs = pstmt.executeQuery();
                if(rs.next()) {
                    return new Data(rs.getInt(1), rs.getString(2));
                } else {
                    return null;
                }
            }
        } catch (SQLException e) {
            return null;
        }
    }

    @Override
    public List<Data> getAllRecords() {
        try {
            List<Data> res = new ArrayList<Data>();
            try(Connection con = DriverManager.getConnection(conurl, un, pw)) {
                PreparedStatement pstmt = con.prepareStatement("SELECT f1,f2 FROM t1");
                ResultSet rs = pstmt.executeQuery();
                while(rs.next()) {
                    res.add(new Data(rs.getInt(1), rs.getString(2)));
                }
            }
            return res;
        } catch (SQLException e) {
            return null;
        }
    }
}

PlainPresenter.java:

package di.no;

import di.common.Data;
import di.common.Presenter;

public class PlainPresenter implements Presenter {
    @Override
    public void start() {
        // nothing
    }
    @Override
    public void one(Data o) {
        System.out.println(o.getF1() + " " + o.getF2());
    }
    @Override
    public void finish() {
        // nothing
    }
}

HtmlPresenter.java:

package di.no;

import java.util.Properties;

import di.common.Data;
import di.common.Presenter;
import di.common.Util;

public class HtmlPresenter implements Presenter {
    private String lbl1; 
    private String lbl2; 
    public HtmlPresenter() {
        Properties props = Util.loadProperties("/work/html.properties");
        this.lbl1 = props.getProperty("html.lbl1");
        this.lbl2 = props.getProperty("html.lbl2");
    }
    @Override
    public void start() {
        System.out.println("<table>");
        System.out.println("<tr>");
        System.out.println("<th>" + lbl1 + "</th>");
        System.out.println("<th>" + lbl2 + "</th>");
        System.out.println("</tr>");
    }
    @Override
    public void one(Data o) {
        System.out.println("<tr>");
        System.out.println("<td>" + o.getF1() + "</td>");
        System.out.println("<td>" + o.getF2() + "</td>");
        System.out.println("</tr>");
    }
    @Override
    public void finish() {
        System.out.println("</table>");
    }
}

StandardApplication.java:

package di.no;

import di.common.Application;
import di.common.Data;
import di.common.DataAccessLayer;
import di.common.Presenter;

public class StandardApplication implements Application {
    private DataAccessLayer dal;
    private Presenter p;
    public StandardApplication() {
        //this.dal = new MockDataAccessLayer();
        this.dal = new DbDataAccessLayer();
        //this.p = new PlainPresenter();
        this.p = new HtmlPresenter();
    }
    @Override
    public void run() {
        // one
        p.start();
        Data o = dal.getOneRecord(2);
        p.one(o);
        p.finish();
        // all
        p.start();
        for(Data o1 : dal.getAllRecords()) {
            p.one(o1);
        }
        p.finish();
    }
}

Test.java:

package di.no;

import di.common.Application;

public class Test {
    public static void main(String[] args) {
        Application app = new StandardApplication();
        app.run();
    }
}

Program.cs:

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.Common;

using DI.Common;

namespace DI.No
{
    public class MockDataAccessLayer : IDataAccessLayer
    {
        public Data GetOneRecord(int f1)
        {
            return new Data { F1 = f1, F2 = "X" + f1 };
        }
        public IList<Data> GetAllRecords()
        {
            IList<Data> res = new List<Data>();
            res.Add(new Data { F1 = 1, F2 = "A" });
            res.Add(new Data { F1 = 2, F2 = "BB" });
            res.Add(new Data { F1 = 3, F2 = "CCC" });
            return res;
       }
    }
    public class DbDataAccessLayer : IDataAccessLayer
    {
        private string prov;
        private string constr;
        private IDbConnection GetConnection()
        {
            IDbConnection con = DbProviderFactories.GetFactory(prov).CreateConnection();
            con.ConnectionString = constr;
            return con;
        }
        public DbDataAccessLayer()
        {
            this.prov = ConfigurationManager.AppSettings["DBProvider"];
            this.constr = ConfigurationManager.AppSettings["DBConnectionString"];
        }
        public Data GetOneRecord(int f1)
        {
            using(IDbConnection con = GetConnection())
            {
                con.Open();
                IDbCommand cmd = con.CreateCommand();
                cmd.CommandText = "SELECT f1,f2 FROM t1 WHERE f1 = @f1";
                cmd.Connection = con;
                IDbDataParameter pf1 = cmd.CreateParameter();
                pf1.ParameterName = "@f1";
                pf1.DbType = DbType.Int32;
                pf1.Value = f1;
                cmd.Parameters.Add(pf1);
                IDataReader rdr = cmd.ExecuteReader();
                if(rdr.Read())
                {
                    return new Data { F1 = (int)rdr["f1"], F2 = (string)rdr["f2"] };
                }
                else
                {
                    return null;
                }
            }
        }
        public IList<Data> GetAllRecords()
        {
            IList<Data> res = new List<Data>();
            using(IDbConnection con = GetConnection())
            {
                con.Open();
                IDbCommand cmd = con.CreateCommand();
                cmd.CommandText = "SELECT f1,f2 FROM t1";
                cmd.Connection = con;
                IDataReader rdr = cmd.ExecuteReader();
                while(rdr.Read())
                {
                    res.Add(new Data { F1 = (int)rdr["f1"], F2 = (string)rdr["f2"] });
                }
            }
            return res;
       }
    }
    public class PlainPresenter : IPresenter
    {
        public void Start()
        {
            // nothing
        }
        public void One(Data o)
        {
            Console.WriteLine(o.F1 + " " + o.F2);
        }
        public void Finish()
        {
            // nothing
        }
    }
    public class HtmlPresenter : IPresenter
    {
        private String lbl1; 
        private String lbl2; 
        public HtmlPresenter()
        {
            this.lbl1 = ConfigurationManager.AppSettings["Label1"];
            this.lbl2 = ConfigurationManager.AppSettings["Label2"];
        }
        public void Start()
        {
            Console.WriteLine("<table>");
            Console.WriteLine("<tr>");
            Console.WriteLine("<th>" + lbl1 + "</th>");
            Console.WriteLine("<th>" + lbl2 + "</th>");
            Console.WriteLine("</tr>");
        }
        public void One(Data o)
        {
            Console.WriteLine("<tr>");
            Console.WriteLine("<td>" + o.F1 + "</td>");
            Console.WriteLine("<td>" + o.F2 + "</td>");
            Console.WriteLine("</tr>");
        }
        public void Finish()
        {
            Console.WriteLine("</table>");
        }
    }
    public class StandardApplication : IApplication
    {
        private IDataAccessLayer dal;
        private IPresenter p;
        public StandardApplication()
        {
            //this.dal = new MockDataAccessLayer();
            this.dal = new DbDataAccessLayer();
            //this.p = new PlainPresenter();
            this.p = new HtmlPresenter();
        }
        public void Run()
        {
            // one
            p.Start();
            Data o = dal.GetOneRecord(2);
            p.One(o);
            p.Finish();
            // all
            p.Start();
            foreach(Data o1 in dal.GetAllRecords())
            {
                p.One(o1);
            }
            p.Finish();
        }
    }
    public class Program
    {
        public static void Main(string[] args)
        {
            IApplication app = new StandardApplication();
            app.Run();
        }
    }
}

app.config:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <appSettings>
        <add key="DBProvider" value="MySql.Data.MySqlClient" />
        <add key="DBConnectionString" value="Server=localhost;Database=Test;User Id=root;Password=" />
        <add key="Label1" value="F1" />
        <add key="Label2" value="F2" />
     </appSettings>
</configuration>

no.py:

import pymysql

from common import *

class MockDataAccessLayer:
    def get_one_record(self, f1):
        return Data(f1, 'X' + str(f1))
    def get_all_records(self):
        return [ Data(1, 'A'), Data(2, 'BB'), Data(3, 'CCC')]

class DbDataAccessLayer:
    def __init__(self):
        self.con = pymysql.connect(host='localhost',user='root',password='',db='Test')
    def get_one_record(self, f1):
        c = self.con.cursor()
        c.execute('SELECT f2 FROM t1 WHERE f1 = %s', (f1,))
        f2 = c.fetchone()[0]
        c.close()
        return Data(f1, f2)
    def get_all_records(self):
        c = self.con.cursor()
        c.execute('SELECT f1,f2 FROM t1')
        res = []
        for row in c.fetchall():
            res.append(Data(row[0], row[1]))
        c.close()
        return res

class PlainPresenter:
    def start(self):
        pass
    def one(self, o):
        print('%d %s' % (o.f1, o.f2))
    def finish(self):
        pass

class HtmlPresenter:
    def __init__(self):
        self.lbl1 = 'F1'
        self.lbl2 = 'F2'
    def start(self):
        print('<table>')
        print('<tr>')
        print('<th>%s</th>' % (self.lbl1))
        print('<th>%s</th>' % (self.lbl2))
        print('</tr>')
    def one(self, o):
        print('<tr>')
        print('<td>%s</td>' % (o.f1))
        print('<td>%s</td>' % (o.f2))
        print('</tr>')
    def finish(self):
        print('</table>')

class StandardApplication:
    def __init__(self):
        #self.dal = MockDataAccessLayer()
        self.dal = DbDataAccessLayer()
        #self.p = PlainPresenter()
        self.p = HtmlPresenter()
    def run(self):
        self.p.start()
        o = self.dal.get_one_record(2)
        self.p.one(o)
        self.p.finish()
        self.p.start()
        for o1 in  self.dal.get_all_records():
            self.p.one(o1)
        self.p.finish()

app = StandardApplication()
app.run()

no.php:

<?php

require_once 'common.php';

class MockDataAccessLayer implements DataAccessLayer {
    public function getOneRecord($f1) {
        return new Data($f1, 'X' . $f1);    
    }
    public function getAllRecords() {
        $res = array();
        $res[] = new Data(1, 'A');
        $res[] = new Data(2, 'BB');
        $res[] = new Data(3, 'CCC');
        return $res;
    }
}

class DbDataAccessLayer implements DataAccessLayer {
    private $constr;
    private $un;
    private $pw;
    private function get_connection() {
        $con = new PDO($this->constr, $this->un, $this->pw);
        $con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $con->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
        return $con;
    }
    public function __construct($constr, $un, $pw) {
        $this->constr = $constr;
        $this->un = $un;
        $this->pw = $pw;
    }
    public function getOneRecord($f1) {
        
        $con = $this->get_connection();
        $stmt = $con->prepare('SELECT f2 FROM t1 WHERE f1 = :f1');
        $stmt->execute(array(':f1' => $f1));
        if($row = $stmt->fetch()) {
            $f2 = $row['f2'];
            return new Data($f1, $f2);
        } else {
            return null;
        }
    }
    public function getAllRecords() {
        $con = $this->get_connection();
        $stmt = $con->prepare('SELECT f1,f2 FROM t1');
        $stmt->execute(array());
        $res = array();
        while($row = $stmt->fetch()) {
            $f1 = $row['f1'];
            $f2 = $row['f2'];
            $res[] = new Data($f1, $f2);
        }
        return $res;
    }
}

class PlainPresenter implements Presenter {
    public function start() {
        // nothing
    }
    public function one($o) {
        echo $o->f1 . ' ' . $o->f2 . "\r\n";
    }
    public function finish() {
        // nothing
    }
}

class HtmlPresenter implements Presenter {
    private $lbl1;
    private $lbl2;
    public function __construct($lbl1, $lbl2) {
        $this->lbl1 = $lbl1;
        $this->lbl2 = $lbl2;
}
    public function start() {
        echo "<table>\r\n";
        echo "<tr>\r\n";
        echo "<th>" . $this->lbl1 . "</th>\r\n";
        echo "<th>" . $this->lbl2 . "</th>\r\n";
        echo "</tr>\r\n";
    }
    public function one($o) {
        echo "<tr>\r\n";
        echo "<td>" . $o->f1 . "</td>\r\n";
        echo "<td>" . $o->f2 . "</td>\r\n";
        echo "</tr>\r\n";
    }
    public function finish() {
        echo "</table>\r\n";
    }
}

class StandardApplication implements Application {
    private $dal;
    private $p;
    public function __construct() {
        //$this->dal = new MockDataAccessLayer();
        $this->dal = new DbDataAccessLayer('mysql:host=localhost;dbname=Test', 'root', '');
        //$this->p = new PlainPresenter();
        $this->p = new HtmlPresenter('F1', 'F2');
    }
    public function run() {
        $this->p->start();
        $o = $this->dal->getOneRecord(2);
        $this->p->one($o);
        $this->p->finish();
        $this->p->start();
        foreach($this->dal->getAllRecords() as $o1) {
            $this->p->one($o1);
        }
        $this->p->finish();
    }
}

$app = new StandardApplication();
$app->run();

?>

Very straigth forward code. But if changes are needed then edits need to be made a lot of places in the code.

With DI but without DI framework:

Now let us try implementing by manual DI aka eksplicit sending eveything over eksplicit by handwritten code.

MockDataAccessLayer.java:

package di.traditional;

import java.util.ArrayList;
import java.util.List;

import di.common.Data;
import di.common.DataAccessLayer;

public class MockDataAccessLayer implements DataAccessLayer {
    @Override
    public Data getOneRecord(int f1) {
        return new Data(f1, "X" + f1);
    }

    @Override
    public List<Data> getAllRecords() {
        List<Data> res = new ArrayList<Data>();
        res.add(new Data(1, "A"));
        res.add(new Data(2, "BB"));
        res.add(new Data(3, "CCC"));
        return res;
    }
}

DbDataAccessLayer.java:

package di.traditional;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import di.common.Data;
import di.common.DataAccessLayer;

public class DbDataAccessLayer implements DataAccessLayer {
    private String conurl;
    private String un;
    private String pw;
    public DbDataAccessLayer(String conurl, String un, String pw) {
        super();
        this.conurl = conurl;
        this.un = un;
        this.pw = pw;
    }
    @Override
    public Data getOneRecord(int f1) {
        try {
            try(Connection con = DriverManager.getConnection(conurl, un, pw)) {
                PreparedStatement pstmt = con.prepareStatement("SELECT f1,f2 FROM t1 WHERE f1 = ?");
                pstmt.setInt(1,  f1);
                ResultSet rs = pstmt.executeQuery();
                if(rs.next()) {
                    return new Data(rs.getInt(1), rs.getString(2));
                } else {
                    return null;
                }
            }
        } catch (SQLException e) {
            return null;
        }
    }

    @Override
    public List<Data> getAllRecords() {
        try {
            List<Data> res = new ArrayList<Data>();
            try(Connection con = DriverManager.getConnection(conurl, un, pw)) {
                PreparedStatement pstmt = con.prepareStatement("SELECT f1,f2 FROM t1");
                ResultSet rs = pstmt.executeQuery();
                while(rs.next()) {
                    res.add(new Data(rs.getInt(1), rs.getString(2)));
                }
            }
            return res;
        } catch (SQLException e) {
            return null;
        }
    }
}

PlainPresenter.java:

package di.traditional;

import di.common.Data;
import di.common.Presenter;

public class PlainPresenter implements Presenter {
    @Override
    public void start() {
        // nothing
    }
    @Override
    public void one(Data o) {
        System.out.println(o.getF1() + " " + o.getF2());
    }
    @Override
    public void finish() {
        // nothing
    }
}

HtmlPresenter.java:

package di.traditional;

import di.common.Data;
import di.common.Presenter;

public class HtmlPresenter implements Presenter {
    private String lbl1; 
    private String lbl2; 
    public HtmlPresenter(String lbl1, String lbl2) {
        this.lbl1 = lbl1;
        this.lbl2 = lbl2;
    }
    @Override
    public void start() {
        System.out.println("<table>");
        System.out.println("<tr>");
        System.out.println("<th>" + lbl1 + "</th>");
        System.out.println("<th>" + lbl2 + "</th>");
        System.out.println("</tr>");
    }
    @Override
    public void one(Data o) {
        System.out.println("<tr>");
        System.out.println("<td>" + o.getF1() + "</td>");
        System.out.println("<td>" + o.getF2() + "</td>");
        System.out.println("</tr>");
    }
    @Override
    public void finish() {
        System.out.println("</table>");
    }
}

StandardApplication.java:

package di.traditional;

import di.common.Application;
import di.common.Data;
import di.common.DataAccessLayer;
import di.common.Presenter;

public class StandardApplication implements Application {
    private DataAccessLayer dal;
    private Presenter p;
    public StandardApplication(DataAccessLayer dal, Presenter p) {
        this.dal = dal;
        this.p = p;
    }
    @Override
    public void run() {
        // one
        p.start();
        Data o = dal.getOneRecord(2);
        p.one(o);
        p.finish();
        // all
        p.start();
        for(Data o1 : dal.getAllRecords()) {
            p.one(o1);
        }
        p.finish();
    }
}

Test.java:

package di.traditional;

import java.util.Properties;

import di.common.Application;
import di.common.DataAccessLayer;
import di.common.Presenter;
import di.common.Util;

public class Test {
    public static void main(String[] args) {
        //DataAccessLayer dal = new MockDataAccessLayer();
        Properties props = Util.loadProperties("/work/jdbc.properties");
        String conurl = props.getProperty("db.conurl");
        String un = props.getProperty("db.un");
        String pw = props.getProperty("db.pw");
        DataAccessLayer dal = new DbDataAccessLayer(conurl, un, pw);
        //Presenter p = new PlainPresenter();
        Properties props2 = Util.loadProperties("/work/html.properties");
        String lbl1 = props2.getProperty("html.lbl1");
        String lbl2 = props2.getProperty("html.lbl2");
        Presenter p = new HtmlPresenter(lbl1, lbl2);
        Application app = new StandardApplication(dal, p);
        app.run();
    }
}

Program.cs:

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.Common;

using DI.Common;

namespace DI.Traditional
{
    public class MockDataAccessLayer : IDataAccessLayer
    {
        public Data GetOneRecord(int f1)
        {
            return new Data { F1 = f1, F2 = "X" + f1 };
        }
        public IList<Data> GetAllRecords()
        {
            IList<Data> res = new List<Data>();
            res.Add(new Data { F1 = 1, F2 = "A" });
            res.Add(new Data { F1 = 2, F2 = "BB" });
            res.Add(new Data { F1 = 3, F2 = "CCC" });
            return res;
       }
    }
    public class DbDataAccessLayer : IDataAccessLayer
    {
        private string prov;
        private string constr;
        private IDbConnection GetConnection()
        {
            IDbConnection con = DbProviderFactories.GetFactory(prov).CreateConnection();
            con.ConnectionString = constr;
            return con;
        }
        public DbDataAccessLayer(string prov, string constr)
        {
            this.prov = prov;
            this.constr = constr;
        }
        public Data GetOneRecord(int f1)
        {
            using(IDbConnection con = GetConnection())
            {
                con.Open();
                IDbCommand cmd = con.CreateCommand();
                cmd.CommandText = "SELECT f1,f2 FROM t1 WHERE f1 = @f1";
                cmd.Connection = con;
                IDbDataParameter pf1 = cmd.CreateParameter();
                pf1.ParameterName = "@f1";
                pf1.DbType = DbType.Int32;
                pf1.Value = f1;
                cmd.Parameters.Add(pf1);
                IDataReader rdr = cmd.ExecuteReader();
                if(rdr.Read())
                {
                    return new Data { F1 = (int)rdr["f1"], F2 = (string)rdr["f2"] };
                }
                else
                {
                    return null;
                }
            }
        }
        public IList<Data> GetAllRecords()
        {
            IList<Data> res = new List<Data>();
            using(IDbConnection con = GetConnection())
            {
                con.Open();
                IDbCommand cmd = con.CreateCommand();
                cmd.CommandText = "SELECT f1,f2 FROM t1";
                cmd.Connection = con;
                IDataReader rdr = cmd.ExecuteReader();
                while(rdr.Read())
                {
                    res.Add(new Data { F1 = (int)rdr["f1"], F2 = (string)rdr["f2"] });
                }
            }
            return res;
       }
    }
    public class PlainPresenter : IPresenter
    {
        public void Start()
        {
            // nothing
        }
        public void One(Data o)
        {
            Console.WriteLine(o.F1 + " " + o.F2);
        }
        public void Finish()
        {
            // nothing
        }
    }
    public class HtmlPresenter : IPresenter
    {
        private String lbl1; 
        private String lbl2; 
        public HtmlPresenter(String lbl1, String lbl2)
        {
            this.lbl1 = lbl1;
            this.lbl2 = lbl2;
        }
        public void Start()
        {
            Console.WriteLine("<table>");
            Console.WriteLine("<tr>");
            Console.WriteLine("<th>" + lbl1 + "</th>");
            Console.WriteLine("<th>" + lbl2 + "</th>");
            Console.WriteLine("</tr>");
        }
        public void One(Data o)
        {
            Console.WriteLine("<tr>");
            Console.WriteLine("<td>" + o.F1 + "</td>");
            Console.WriteLine("<td>" + o.F2 + "</td>");
            Console.WriteLine("</tr>");
        }
        public void Finish()
        {
            Console.WriteLine("</table>");
        }
    }
    public class StandardApplication : IApplication
    {
        private IDataAccessLayer dal;
        private IPresenter p;
        public StandardApplication(IDataAccessLayer dal, IPresenter p)
        {
            this.dal = dal;
            this.p = p;
        }
        public void Run()
        {
            // one
            p.Start();
            Data o = dal.GetOneRecord(2);
            p.One(o);
            p.Finish();
            // all
            p.Start();
            foreach(Data o1 in dal.GetAllRecords())
            {
                p.One(o1);
            }
            p.Finish();
        }
    }
    public class Program
    {
        public static void Main(string[] args)
        {
            //IDataAccessLayer dal = new MockDataAccessLayer();
            string prov = ConfigurationManager.AppSettings["DBProvider"];
            string constr = ConfigurationManager.AppSettings["DBConnectionString"];
            IDataAccessLayer dal = new DbDataAccessLayer(prov, constr);
            //IPresenter p = new PlainPresenter();
            string lbl1 = ConfigurationManager.AppSettings["Label1"];
            string lbl2 = ConfigurationManager.AppSettings["Label2"];
            IPresenter p = new HtmlPresenter(lbl1, lbl2);
            IApplication app = new StandardApplication(dal, p);
            app.Run();
        }
    }
}

app.config:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <appSettings>
        <add key="DBProvider" value="MySql.Data.MySqlClient" />
        <add key="DBConnectionString" value="Server=localhost;Database=Test;User Id=root;Password=" />
        <add key="Label1" value="F1" />
        <add key="Label2" value="F2" />
     </appSettings>
    <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.0" />
    </startup>
</configuration>

trad.py:

import pymysql

from common import *

class MockDataAccessLayer:
    def get_one_record(self, f1):
        return Data(f1, 'X' + str(f1))
    def get_all_records(self):
        return [ Data(1, 'A'), Data(2, 'BB'), Data(3, 'CCC')]

class DbDataAccessLayer:
    def __init__(self, con):
        self.con = con
    def get_one_record(self, f1):
        c = self.con.cursor()
        c.execute('SELECT f2 FROM t1 WHERE f1 = %s', (f1,))
        f2 = c.fetchone()[0]
        c.close()
        return Data(f1, f2)
    def get_all_records(self):
        c = self.con.cursor()
        c.execute('SELECT f1,f2 FROM t1')
        res = []
        for row in c.fetchall():
            res.append(Data(row[0], row[1]))
        c.close()
        return res

class PlainPresenter:
    def start(self):
        pass
    def one(self, o):
        print('%d %s' % (o.f1, o.f2))
    def finish(self):
        pass

class HtmlPresenter:
    def __init__(self, lbl1, lbl2):
        self.lbl1 = lbl1
        self.lbl2 = lbl2
    def start(self):
        print('<table>')
        print('<tr>')
        print('<th>%s</th>' % (self.lbl1))
        print('<th>%s</th>' % (self.lbl2))
        print('</tr>')
    def one(self, o):
        print('<tr>')
        print('<td>%s</td>' % (o.f1))
        print('<td>%s</td>' % (o.f2))
        print('</tr>')
    def finish(self):
        print('</table>')

class StandardApplication:
    def __init__(self, dal, p):
        self.dal = dal
        self.p = p
    def run(self):
        self.p.start()
        o = self.dal.get_one_record(2)
        self.p.one(o)
        self.p.finish()
        self.p.start()
        for o1 in  self.dal.get_all_records():
            self.p.one(o1)
        self.p.finish()

#dal = MockDataAccessLayer()
dal = DbDataAccessLayer(pymysql.connect(host='localhost',user='root',password='',db='Test'))
#p = PlainPresenter()
p = HtmlPresenter('F1', 'F2')
app = StandardApplication(dal, p)
app.run()

trad.php:

<?php

require_once 'common.php';

class MockDataAccessLayer implements DataAccessLayer {
    public function getOneRecord($f1) {
        return new Data($f1, 'X' . $f1);    
    }
    public function getAllRecords() {
        $res = array();
        $res[] = new Data(1, 'A');
        $res[] = new Data(2, 'BB');
        $res[] = new Data(3, 'CCC');
        return $res;
    }
}

class DbDataAccessLayer implements DataAccessLayer {
    private $constr;
    private $un;
    private $pw;
    private function get_connection() {
        $con = new PDO($this->constr, $this->un, $this->pw);
        $con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $con->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
        return $con;
    }
    public function __construct($constr, $un, $pw) {
        $this->constr = $constr;
        $this->un = $un;
        $this->pw = $pw;
    }
    public function getOneRecord($f1) {
        
        $con = $this->get_connection();
        $stmt = $con->prepare('SELECT f2 FROM t1 WHERE f1 = :f1');
        $stmt->execute(array(':f1' => $f1));
        if($row = $stmt->fetch()) {
            $f2 = $row['f2'];
            return new Data($f1, $f2);
        } else {
            return null;
        }
    }
    public function getAllRecords() {
        $con = $this->get_connection();
        $stmt = $con->prepare('SELECT f1,f2 FROM t1');
        $stmt->execute(array());
        $res = array();
        while($row = $stmt->fetch()) {
            $f1 = $row['f1'];
            $f2 = $row['f2'];
            $res[] = new Data($f1, $f2);
        }
        return $res;
    }
}

class PlainPresenter implements Presenter {
    public function start() {
        // nothing
    }
    public function one($o) {
        echo $o->f1 . ' ' . $o->f2 . "\r\n";
    }
    public function finish() {
        // nothing
    }
}

class HtmlPresenter implements Presenter {
    private $lbl1;
    private $lbl2;
    public function __construct($lbl1, $lbl2) {
        $this->lbl1 = $lbl1;
        $this->lbl2 = $lbl2;
}
    public function start() {
        echo "<table>\r\n";
        echo "<tr>\r\n";
        echo "<th>" . $this->lbl1 . "</th>\r\n";
        echo "<th>" . $this->lbl2 . "</th>\r\n";
        echo "</tr>\r\n";
    }
    public function one($o) {
        echo "<tr>\r\n";
        echo "<td>" . $o->f1 . "</td>\r\n";
        echo "<td>" . $o->f2 . "</td>\r\n";
        echo "</tr>\r\n";
    }
    public function finish() {
        echo "</table>\r\n";
    }
}

class StandardApplication implements Application {
    private $dal;
    private $p;
    public function __construct($dal, $p) {
        $this->dal = $dal;
        $this->p = $p;
    }
    public function run() {
        $this->p->start();
        $o = $this->dal->getOneRecord(2);
        $this->p->one($o);
        $this->p->finish();
        $this->p->start();
        foreach($this->dal->getAllRecords() as $o1) {
            $this->p->one($o1);
        }
        $this->p->finish();
    }
}

//$dal = new MockDataAccessLayer();
$dal = new DbDataAccessLayer('mysql:host=localhost;dbname=Test', 'root', '');
//$p = new PlainPresenter();
$p = new HtmlPresenter('F1', 'F2');
$app = new StandardApplication($dal, $p);
$app->run();

?>

All the glueing is now located at a single place in the code, but that place can easily become very messy for a large application.

Configuration file:

Here the DI is eksplicit defined in a configuration file and the DI framework just construct based on the configuration file.

The open source Spring Framework was created by Rod Johnson in 2002-2003 and immediatetly became a huge success. Since then lots of functionality has been added to Spring and today many Spring pieces besides DI are widely used in the Java world.

The original Spring DI used XML configuration files. And Spring still supports that. Even though annotations introduced in version 2.5 (2007) has become the most popular configuration for Spring.

The configuration is pretty straigth forward.

XML config:

    <bean id="somename" class="somepackage.SomeClass">
        ...
        <constructor-arg index="n" ref="somename"/>
        ...
        <constructor-arg index="m" value="somevalue"/>
        ...
    </bean>

Java code:

SomeType somevar = springcontext.getBean("somename", SomeType.class);

MockDataAccessLayer.java:

package di.springxml;

import java.util.ArrayList;
import java.util.List;

import di.common.Data;
import di.common.DataAccessLayer;

public class MockDataAccessLayer implements DataAccessLayer {
    @Override
    public Data getOneRecord(int f1) {
        return new Data(f1, "X" + f1);
    }

    @Override
    public List<Data> getAllRecords() {
        List<Data> res = new ArrayList<Data>();
        res.add(new Data(1, "A"));
        res.add(new Data(2, "BB"));
        res.add(new Data(3, "CCC"));
        return res;
    }
}

DbDataAccessLayer.java:

package di.springxml;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import di.common.Data;
import di.common.DataAccessLayer;

public class DbDataAccessLayer implements DataAccessLayer {
    private String conurl;
    private String un;
    private String pw;
    public DbDataAccessLayer(String conurl, String un, String pw) {
        super();
        this.conurl = conurl;
        this.un = un;
        this.pw = pw;
    }
    @Override
    public Data getOneRecord(int f1) {
        try {
            try(Connection con = DriverManager.getConnection(conurl, un, pw)) {
                PreparedStatement pstmt = con.prepareStatement("SELECT f1,f2 FROM t1 WHERE f1 = ?");
                pstmt.setInt(1,  f1);
                ResultSet rs = pstmt.executeQuery();
                if(rs.next()) {
                    return new Data(rs.getInt(1), rs.getString(2));
                } else {
                    return null;
                }
            }
        } catch (SQLException e) {
            return null;
        }
    }
    @Override
    public List<Data> getAllRecords() {
        try {
            List<Data> res = new ArrayList<Data>();
            try(Connection con = DriverManager.getConnection(conurl, un, pw)) {
                PreparedStatement pstmt = con.prepareStatement("SELECT f1,f2 FROM t1");
                ResultSet rs = pstmt.executeQuery();
                while(rs.next()) {
                    res.add(new Data(rs.getInt(1), rs.getString(2)));
                }
            }
            return res;
        } catch (SQLException e) {
            return null;
        }
    }
}

PlainPresenter.java:

package di.springxml;

import di.common.Data;
import di.common.Presenter;

public class PlainPresenter implements Presenter {
    @Override
    public void start() {
        // nothing
    }
    @Override
    public void one(Data o) {
        System.out.println(o.getF1() + " " + o.getF2());
    }
    @Override
    public void finish() {
        // nothing
    }
}

HtmlPresenter.java:

package di.springxml;

import di.common.Data;
import di.common.Presenter;

public class HtmlPresenter implements Presenter {
    private String lbl1; 
    private String lbl2; 
    public HtmlPresenter(String lbl1, String lbl2) {
        this.lbl1 = lbl1;
        this.lbl2 = lbl2;
    }
    @Override
    public void start() {
        System.out.println("<table>");
        System.out.println("<tr>");
        System.out.println("<th>" + lbl1 + "</th>");
        System.out.println("<th>" + lbl2 + "</th>");
        System.out.println("</tr>");
    }
    @Override
    public void one(Data o) {
        System.out.println("<tr>");
        System.out.println("<td>" + o.getF1() + "</td>");
        System.out.println("<td>" + o.getF2() + "</td>");
        System.out.println("</tr>");
    }
    @Override
    public void finish() {
        System.out.println("</table>");
    }
}

StandardApplication.java:

package di.springxml;

import di.common.Application;
import di.common.Data;
import di.common.DataAccessLayer;
import di.common.Presenter;

public class StandardApplication implements Application {
    private DataAccessLayer dal;
    private Presenter p;
    public StandardApplication(DataAccessLayer dal, Presenter p) {
        this.dal = dal;
        this.p = p;
    }
    @Override
    public void run() {
        // one
        p.start();
        Data o = dal.getOneRecord(2);
        p.one(o);
        p.finish();
        // all
        p.start();
        for(Data o1 : dal.getAllRecords()) {
            p.one(o1);
        }
        p.finish();
    }
}

Test.java:

package di.springxml;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

import di.common.Application;

public class Test {
    public static void main(String[] args) {
        @SuppressWarnings("resource")
        //ApplicationContext ctx = new FileSystemXmlApplicationContext("C:/work/spring-config-test.xml");
        ApplicationContext ctx = new FileSystemXmlApplicationContext("C:/work/spring-config-mysql.xml");
        Application app = ctx.getBean("app", Application.class);
        app.run();
    }
}

spring-config-mysql.xml:

<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 https://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="app" class="di.springxml.StandardApplication">
        <constructor-arg index="0" ref="dal"/>
        <constructor-arg index="1" ref="p"/>
    </bean>
    <bean id="dal" class="di.springxml.DbDataAccessLayer">
        <constructor-arg index="0" value="jdbc:mysql://localhost/Test"/>
        <constructor-arg index="1" value="root"/>
        <constructor-arg index="2" value=""/>
    </bean>
    <bean id="p" class="di.springxml.HtmlPresenter">
        <constructor-arg index="0" value="F1"/>
        <constructor-arg index="1" value="F2"/>
    </bean>
</beans>

Note that the content in the two properties files are merged ino the Spring config file bringing all configuration into a single place.

spring-config-test.xml:

<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 https://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="app" class="di.springxml.StandardApplication">
        <constructor-arg index="0" ref="dal"/>
        <constructor-arg index="1" ref="p"/>
    </bean>
    <bean id="dal" class="di.springxml.MockDataAccessLayer"/>
    <bean id="p" class="di.springxml.PlainPresenter"/>
</beans>

Inst.java:

package di.springxml;

import org.springframework.context.ApplicationContext;
import org.springframework.context.support.FileSystemXmlApplicationContext;

public class Inst {
    public static class C1 {
        private static Object lck = new Object();
        private static int v1 = 0;
        private int v2;
        public C1() {
            synchronized(lck) {
                v1++;
                v2 = v1;
            }
        }
        @Override
        public String toString() {
            return String.format("%d/%d", v2, v1);
        }
    }
    public static class C2 {
        private static Object lck = new Object();
        private static int v1 = 0;
        private int v2;
        public C2() {
            synchronized(lck) {
                v1++;
                v2 = v1;
            }
        }
        @Override
        public String toString() {
            return String.format("%d/%d", v2, v1);
        }
    }
    public static class C3 {
        private static Object lck = new Object();
        private static int v1 = 0;
        private int v2;
        public C3() {
            synchronized(lck) {
                v1++;
                v2 = v1;
            }
        }
        @Override
        public String toString() {
            return String.format("%d/%d", v2, v1);
        }
    }
    public static class C {
        private C1 c1a, c1b;
        private C2 c2a, c2b;
        private C3 c3a, c3b;
        public C(C1 c1a, C1 c1b, C2 c2a, C2 c2b, C3 c3a, C3 c3b) {
            this.c1a = c1a;
            this.c1b = c1b;
            this.c2a = c2a;
            this.c2b = c2b;
            this.c3a = c3a;
            this.c3b = c3b;
        }
        public void test() {
            System.out.printf("Singleton : %s %s\n", c1a, c1b);
            System.out.printf("Prototype : %s %s\n", c2a, c2b);
            System.out.printf("Default   : %s %s\n", c3a, c3b);
        }
    }
    public static void main(String[] args) {
        @SuppressWarnings("resource")
        ApplicationContext ctx = new FileSystemXmlApplicationContext("C:/work/spring-config-inst.xml");
        C o = ctx.getBean("c", C.class);
        o.test();
    }
}

spring-config-inst.xml:

<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 https://www.springframework.org/schema/beans/spring-beans.xsd">
    <bean id="c" class="di.springxml.Inst.C" scope="prototype">
        <constructor-arg index="0" ref="c1"/>
        <constructor-arg index="1" ref="c1"/>
        <constructor-arg index="2" ref="c2"/>
        <constructor-arg index="3" ref="c2"/>
        <constructor-arg index="4" ref="c3"/>
        <constructor-arg index="5" ref="c3"/>
    </bean>
    <bean id="c1" class="di.springxml.Inst.C1" scope="singleton"/>
    <bean id="c2" class="di.springxml.Inst.C2" scope="prototype"/>
    <bean id="c3" class="di.springxml.Inst.C3"/>
</beans>
Singleton : 1/1 1/1
Prototype : 1/2 2/2
Default   : 1/1 1/1

Spring.NET is a port of Spring from Java to .NET that were started back in 2004. But while Spring has become extremely popular in the Java world, then Spring.NET has not caugth on the same way in the .NET world.

Spring.NET is configured in app.config (which is XML) very similar to Java Spring XML configuration.

app.config:

    <object name="somename" type="SomeNamespace.SomeClass">
        ...
        <constructor-arg index="n" ref="somename"/>
        ...
        <constructor-arg index="m" value="somevalue"/>
        ...
    </bean>

Code:

SomeType somevar = springcontext.GetObject<SomeType>("somename");

Program.cs:

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.Common;

using Spring.Context;
using Spring.Context.Support;

using DI.Common;

namespace DI.SpringXml
{
    public class MockDataAccessLayer : IDataAccessLayer
    {
        public Data GetOneRecord(int f1)
        {
            return new Data { F1 = f1, F2 = "X" + f1 };
        }
        public IList<Data> GetAllRecords()
        {
            IList<Data> res = new List<Data>();
            res.Add(new Data { F1 = 1, F2 = "A" });
            res.Add(new Data { F1 = 2, F2 = "BB" });
            res.Add(new Data { F1 = 3, F2 = "CCC" });
            return res;
       }
    }
    public class DbDataAccessLayer : IDataAccessLayer
    {
        private string prov;
        private string constr;
        private IDbConnection GetConnection()
        {
            IDbConnection con = DbProviderFactories.GetFactory(prov).CreateConnection();
            con.ConnectionString = constr;
            return con;
        }
        public DbDataAccessLayer(string prov, string constr)
        {
            this.prov = prov;
            this.constr = constr;
        }
        public Data GetOneRecord(int f1)
        {
            using(IDbConnection con = GetConnection())
            {
                con.Open();
                IDbCommand cmd = con.CreateCommand();
                cmd.CommandText = "SELECT f1,f2 FROM t1 WHERE f1 = @f1";
                cmd.Connection = con;
                IDbDataParameter pf1 = cmd.CreateParameter();
                pf1.ParameterName = "@f1";
                pf1.DbType = DbType.Int32;
                pf1.Value = f1;
                cmd.Parameters.Add(pf1);
                IDataReader rdr = cmd.ExecuteReader();
                if(rdr.Read())
                {
                    return new Data { F1 = (int)rdr["f1"], F2 = (string)rdr["f2"] };
                }
                else
                {
                    return null;
                }
            }
        }
        public IList<Data> GetAllRecords()
        {
            IList<Data> res = new List<Data>();
            using(IDbConnection con = GetConnection())
            {
                con.Open();
                IDbCommand cmd = con.CreateCommand();
                cmd.CommandText = "SELECT f1,f2 FROM t1";
                cmd.Connection = con;
                IDataReader rdr = cmd.ExecuteReader();
                while(rdr.Read())
                {
                    res.Add(new Data { F1 = (int)rdr["f1"], F2 = (string)rdr["f2"] });
                }
            }
            return res;
       }
    }
    public class PlainPresenter : IPresenter
    {
        public void Start()
        {
            // nothing
        }
        public void One(Data o)
        {
            Console.WriteLine(o.F1 + " " + o.F2);
        }
        public void Finish()
        {
            // nothing
        }
    }
    public class HtmlPresenter : IPresenter
    {
        private String lbl1; 
        private String lbl2; 
        public HtmlPresenter(String lbl1, String lbl2)
        {
            this.lbl1 = lbl1;
            this.lbl2 = lbl2;
        }
        public void Start()
        {
            Console.WriteLine("<table>");
            Console.WriteLine("<tr>");
            Console.WriteLine("<th>" + lbl1 + "</th>");
            Console.WriteLine("<th>" + lbl2 + "</th>");
            Console.WriteLine("</tr>");
        }
        public void One(Data o)
        {
            Console.WriteLine("<tr>");
            Console.WriteLine("<td>" + o.F1 + "</td>");
            Console.WriteLine("<td>" + o.F2 + "</td>");
            Console.WriteLine("</tr>");
        }
        public void Finish()
        {
            Console.WriteLine("</table>");
        }
    }
    public class StandardApplication : IApplication
    {
        private IDataAccessLayer dal;
        private IPresenter p;
        public StandardApplication(IDataAccessLayer dal, IPresenter p)
        {
            this.dal = dal;
            this.p = p;
        }
        public void Run()
        {
            // one
            p.Start();
            Data o = dal.GetOneRecord(2);
            p.One(o);
            p.Finish();
            // all
            p.Start();
            foreach(Data o1 in dal.GetAllRecords())
            {
                p.One(o1);
            }
            p.Finish();
        }
    }
    public class C1
    {
        private static object lck = new object();
        private static int v1 = 0;
        private int v2;
        public C1()
        {
            lock(lck)
            {
                v1++;
                v2 = v1;
            }
        }
        public override string ToString()
        {
            return string.Format("{0}/{1}", v2, v1);
        }
    }
    public class C2
    {
        private static object lck = new object();
        private static int v1 = 0;
        private int v2;
        public C2()
        {
            lock(lck)
            {
                v1++;
                v2 = v1;
            }
        }
        public override string ToString()
        {
            return string.Format("{0}/{1}", v2, v1);
        }
    }
    public class C3
    {
        private static object lck = new object();
        private static int v1 = 0;
        private int v2;
        public C3()
        {
            lock(lck)
            {
                v1++;
                v2 = v1;
            }
        }
        public override string ToString()
        {
            return string.Format("{0}/{1}", v2, v1);
        }
    }
    public class C
    {
        private C1 c1a, c1b;
        private C2 c2a, c2b;
        private C3 c3a, c3b;
        public C(C1 c1a, C1 c1b, C2 c2a, C2 c2b, C3 c3a, C3 c3b)
        {
            this.c1a = c1a;
            this.c1b = c1b;
            this.c2a = c2a;
            this.c2b = c2b;
            this.c3a = c3a;
            this.c3b = c3b;
        }
        public void Test()
        {
            Console.WriteLine("Singleton : {0} {1}", c1a, c1b);
            Console.WriteLine("Prototype : {0} {1}", c2a, c2b);
            Console.WriteLine("Default   : {0} {1}", c3a, c3b);
        }
    }
    public class Program
    {
        public static void Main(string[] args)
        {
            IApplicationContext ctx = ContextRegistry.GetContext();
            IApplication app = ctx.GetObject<IApplication>("app");
            app.Run();
            C o = ctx.GetObject<C>("c");
            o.Test();
        }
    }
}

app.config:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
   <configSections>
        <sectionGroup name="spring">
            <section name="context" type="Spring.Context.Support.ContextHandler, Spring.Core" />
            <section name="objects" type="Spring.Context.Support.DefaultSectionHandler, Spring.Core" />
        </sectionGroup>
    </configSections>
    <spring>
        <context>
            <resource uri="config://spring/objects" />
        </context>
        <objects xmlns="http://www.springframework.net">
            <object name="app" type="DI.SpringXml.StandardApplication">
               <constructor-arg index="0" ref="dal" />
               <constructor-arg index="1" ref="p" />
            </object>
            <!--
            <object name="dal" type="DI.SpringXml.MockDataAccessLayer" />
            <object name="p" type="DI.SpringXml.PlainPresenter" />
            -->
            <object name="dal" type="DI.SpringXml.DbDataAccessLayer">
               <constructor-arg index="0" value="MySql.Data.MySqlClient" />
               <constructor-arg index="1" value="Server=localhost;Database=Test;User Id=root;Password=" />
            </object>
            <object name="p" type="DI.SpringXml.HtmlPresenter">
               <constructor-arg index="0" value="F1" />
               <constructor-arg index="1" value="F2" />
            </object>
            <object name="c" type="DI.SpringXml.C" singleton="false">
                <constructor-arg index="0" ref="c1"/>
                <constructor-arg index="1" ref="c1"/>
                <constructor-arg index="2" ref="c2"/>
                <constructor-arg index="3" ref="c2"/>
                <constructor-arg index="4" ref="c3"/>
                <constructor-arg index="5" ref="c3"/>
            </object>
            <object name="c1" type="DI.SpringXml.C1" singleton="true"/>
            <object name="c2" type="DI.SpringXml.C2" singleton="false"/>
            <object name="c3" type="DI.SpringXml.C3"/>
        </objects>
    </spring>
</configuration>

Note that the content in the two properties files are merged ino the Spring configuration bringing all configuration into a single place.

Output:

...
Singleton : 1/1 1/1
Prototype : 1/2 2/2
Default   : 1/1 1/1

Declaration matching:

Here the DI happens via annotations/attributes on places where DI is needed and annotations/attributes on something that can bs used for DI and the DI framerwork construct based on what matches.

The Spring Framework was created in 2002-2003 using XML configuration files. Java 5 (2004) introduced annotations in the Java language. And Spring 2.5 (2007) added annotations to Spring. And today annotations are much more common that XML configuration for Spring.

The concept is simple.

Classes that can be injected are marked as:

...
@Component("somename")
public class SomeClass {
    ...
}

Fields to be injected are marked as:

    ...
    @Autowired
    private SomeType fieldname;
    ...

The matching is done as:

AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
ctx.scan("some.package");
ctx.scan("some.other.package");
ctx.refresh();
SomeType var = ctx.getBean(SomeType.class);

MockDataAccessLayer.java:

package di.springann.mock;

import java.util.ArrayList;
import java.util.List;

import org.springframework.stereotype.Component;

import di.common.Data;
import di.common.DataAccessLayer;

@Component("dataAccessLayer")
public class MockDataAccessLayer implements DataAccessLayer {
    @Override
    public Data getOneRecord(int f1) {
        return new Data(f1, "X" + f1);
    }

    @Override
    public List<Data> getAllRecords() {
        List<Data> res = new ArrayList<Data>();
        res.add(new Data(1, "A"));
        res.add(new Data(2, "BB"));
        res.add(new Data(3, "CCC"));
        return res;
    }
}

DbDataAccessLayer.java:

package di.springann.db;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

import di.common.Data;
import di.common.DataAccessLayer;

@PropertySource("file:/work/jdbc.properties")
@Component("dataAccessLayer")
public class DbDataAccessLayer implements DataAccessLayer {
    @Value("${db.conurl}")
    private String conurl;
    @Value("${db.un}")
    private String un;
    @Value("${db.pw}")
    private String pw;
    @Override
    public Data getOneRecord(int f1) {
        try {
            try(Connection con = DriverManager.getConnection(conurl, un, pw)) {
                PreparedStatement pstmt = con.prepareStatement("SELECT f1,f2 FROM t1 WHERE f1 = ?");
                pstmt.setInt(1,  f1);
                ResultSet rs = pstmt.executeQuery();
                if(rs.next()) {
                    return new Data(rs.getInt(1), rs.getString(2));
                } else {
                    return null;
                }
            }
        } catch (SQLException e) {
            return null;
        }
    }

    @Override
    public List<Data> getAllRecords() {
        try {
            List<Data> res = new ArrayList<Data>();
            try(Connection con = DriverManager.getConnection(conurl, un, pw)) {
                PreparedStatement pstmt = con.prepareStatement("SELECT f1,f2 FROM t1");
                ResultSet rs = pstmt.executeQuery();
                while(rs.next()) {
                    res.add(new Data(rs.getInt(1), rs.getString(2)));
                }
            }
            return res;
        } catch (SQLException e) {
            return null;
        }
    }
}

Note how Spring annotations and property files integrate.

PlainPresenter.java:

package di.springann.plain;

import org.springframework.stereotype.Component;

import di.common.Data;
import di.common.Presenter;

@Component("presenter")
public class PlainPresenter implements Presenter {
    @Override
    public void start() {
        // nothing
    }
    @Override
    public void one(Data o) {
        System.out.println(o.getF1() + " " + o.getF2());
    }
    @Override
    public void finish() {
        // nothing
    }
}

HtmlPresenter.java:

package di.springann.html;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.PropertySource;
import org.springframework.stereotype.Component;

import di.common.Data;
import di.common.Presenter;

@PropertySource("file:/work/html.properties")
@Component("presenter")
public class HtmlPresenter implements Presenter {
    @Value("${html.lbl1}")
    private String lbl1; 
    @Value("${html.lbl2}")
    private String lbl2; 
    @Override
    public void start() {
        System.out.println("<table>");
        System.out.println("<tr>");
        System.out.println("<th>" + lbl1 + "</th>");
        System.out.println("<th>" + lbl2 + "</th>");
        System.out.println("</tr>");
    }
    @Override
    public void one(Data o) {
        System.out.println("<tr>");
        System.out.println("<td>" + o.getF1() + "</td>");
        System.out.println("<td>" + o.getF2() + "</td>");
        System.out.println("</tr>");
    }
    @Override
    public void finish() {
        System.out.println("</table>");
    }
}

Note how Spring annotations and property files integrate.

StandardApplication.java:

package di.springann.std;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import di.common.Application;
import di.common.Data;
import di.common.DataAccessLayer;
import di.common.Presenter;

@Component("application")
public class StandardApplication implements Application {
    @Autowired
    private DataAccessLayer dal;
    @Autowired
    private Presenter p;
    @Override
    public void run() {
        // one
        p.start();
        Data o = dal.getOneRecord(2);
        p.one(o);
        p.finish();
        // all
        p.start();
        for(Data o1 : dal.getAllRecords()) {
            p.one(o1);
        }
        p.finish();
    }
}

Test.java:

package di.springann;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.stereotype.Component;

import di.common.Application;

@Component
public class Test {
    public static void main(String[] args) {
        @SuppressWarnings("resource")
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
        //ctx.scan("di.springann.mock");
        ctx.scan("di.springann.db");
        //ctx.scan("di.springann.plain");
        ctx.scan("di.springann.html");
        ctx.scan("di.springann.std");
        ctx.refresh();
        Application app = ctx.getBean(Application.class);
        app.run();
    }
}

Note that even though the example scan different packages for different setups, then in real life it may be more common scanning same package but with different jar files in classpath.

Inst.java:

package di.springann.inst;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Component;

public class Inst {
    @Component("c1")
    @Scope("singleton")
    public static class C1 {
        private static Object lck = new Object();
        private static int v1 = 0;
        private int v2;
        public C1() {
            synchronized(lck) {
                v1++;
                v2 = v1;
            }
        }
        @Override
        public String toString() {
            return String.format("%d/%d", v2, v1);
        }
    }
    @Component("c2")
    @Scope("prototype")
    public static class C2 {
        private static Object lck = new Object();
        private static int v1 = 0;
        private int v2;
        public C2() {
            synchronized(lck) {
                v1++;
                v2 = v1;
            }
        }
        @Override
        public String toString() {
            return String.format("%d/%d", v2, v1);
        }
    }
    @Component("c3")
    public static class C3 {
        private static Object lck = new Object();
        private static int v1 = 0;
        private int v2;
        public C3() {
            synchronized(lck) {
                v1++;
                v2 = v1;
            }
        }
        @Override
        public String toString() {
            return String.format("%d/%d", v2, v1);
        }
    }
    @Component("c")
    public static class C {
        @Autowired
        private C1 c1a, c1b;
        @Autowired
        private C2 c2a, c2b;
        @Autowired
        private C3 c3a, c3b;
        public void test() {
            System.out.printf("Singleton : %s %s\n", c1a, c1b);
            System.out.printf("Prototype : %s %s\n", c2a, c2b);
            System.out.printf("Default   : %s %s\n", c3a, c3b);
        }
    }
    public static void main(String[] args) {
        @SuppressWarnings("resource")
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
        ctx.scan("di.springann.inst");
        ctx.refresh();
        C o = ctx.getBean("c", C.class);
        o.test();
    }

}

Output:

Singleton : 1/1 1/1
Prototype : 1/2 2/2
Default   : 1/1 1/1

Inspired by the success of Spring and Spring annotations Java standardized DI annotations in 2009 with JSR 330.

Both Spring and Google Guice supports JSR 330 annotations.

Note that JSR 330 define the annotations used to define what get injected where, but it does not define how to bootstrap and configure the DI framework.

Classes that can be injected are marked as:

...
@Named("somename")
public class SomeClass {
    ...
}

Fields to be injected are marked as:

    ...
    @Inject
    private SomeType fieldname;
    ...

The matching is done DI framework specific.

MockDataAccessLayer.java:

package di.stdann.mock;

import java.util.ArrayList;
import java.util.List;

import org.springframework.stereotype.Component;

import di.common.Data;
import di.common.DataAccessLayer;

@Component("dataAccessLayer")
public class MockDataAccessLayer implements DataAccessLayer {
    @Override
    public Data getOneRecord(int f1) {
        return new Data(f1, "X" + f1);
    }

    @Override
    public List<Data> getAllRecords() {
        List<Data> res = new ArrayList<Data>();
        res.add(new Data(1, "A"));
        res.add(new Data(2, "BB"));
        res.add(new Data(3, "CCC"));
        return res;
    }
}

DbDataAccessLayer.java:

package di.stdann.db;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import javax.inject.Named;

import di.common.Data;
import di.common.DataAccessLayer;
import di.common.Util;

@Named("dataAccessLayer")
public class DbDataAccessLayer implements DataAccessLayer {
    private String conurl;
    private String un;
    private String pw;
    public DbDataAccessLayer() {
        Properties props = Util.loadProperties("/work/jdbc.properties");
        conurl = props.getProperty("db.conurl");
        un = props.getProperty("db.un");
        pw = props.getProperty("db.pw");
    }
    @Override
    public Data getOneRecord(int f1) {
        try {
            try(Connection con = DriverManager.getConnection(conurl, un, pw)) {
                PreparedStatement pstmt = con.prepareStatement("SELECT f1,f2 FROM t1 WHERE f1 = ?");
                pstmt.setInt(1,  f1);
                ResultSet rs = pstmt.executeQuery();
                if(rs.next()) {
                    return new Data(rs.getInt(1), rs.getString(2));
                } else {
                    return null;
                }
            }
        } catch (SQLException e) {
            return null;
        }
    }

    @Override
    public List<Data> getAllRecords() {
        try {
            List<Data> res = new ArrayList<Data>();
            try(Connection con = DriverManager.getConnection(conurl, un, pw)) {
                PreparedStatement pstmt = con.prepareStatement("SELECT f1,f2 FROM t1");
                ResultSet rs = pstmt.executeQuery();
                while(rs.next()) {
                    res.add(new Data(rs.getInt(1), rs.getString(2)));
                }
            }
            return res;
        } catch (SQLException e) {
            return null;
        }
    }
}

PlainPresenter.java:

package di.stdann.plain;

import javax.inject.Named;

import di.common.Data;
import di.common.Presenter;

@Named("presenter")
public class PlainPresenter implements Presenter {
    @Override
    public void start() {
        // nothing
    }
    @Override
    public void one(Data o) {
        System.out.println(o.getF1() + " " + o.getF2());
    }
    @Override
    public void finish() {
        // nothing
    }
}

HtmlPresenter.java:

package di.stdann.html;

import java.util.Properties;

import javax.inject.Named;

import di.common.Data;
import di.common.Presenter;
import di.common.Util;

@Named("presenter")
public class HtmlPresenter implements Presenter {
    private String lbl1; 
    private String lbl2; 
    public HtmlPresenter() {
        Properties props = Util.loadProperties("/work/html.properties");
        lbl1 = props.getProperty("html.lbl1");
        lbl2 = props.getProperty("html.lbl2");
    }
    @Override
    public void start() {
        System.out.println("<table>");
        System.out.println("<tr>");
        System.out.println("<th>" + lbl1 + "</th>");
        System.out.println("<th>" + lbl2 + "</th>");
        System.out.println("</tr>");
    }
    @Override
    public void one(Data o) {
        System.out.println("<tr>");
        System.out.println("<td>" + o.getF1() + "</td>");
        System.out.println("<td>" + o.getF2() + "</td>");
        System.out.println("</tr>");
    }
    @Override
    public void finish() {
        System.out.println("</table>");
    }
}

StandardApplication.java:

package di.stdann.std;

import javax.inject.Inject;
import javax.inject.Named;

import di.common.Application;
import di.common.Data;
import di.common.DataAccessLayer;
import di.common.Presenter;

@Named("application")
public class StandardApplication implements Application {
    @Inject
    private DataAccessLayer dal;
    @Inject
    private Presenter p;
    @Override
    public void run() {
        // one
        p.start();
        Data o = dal.getOneRecord(2);
        p.one(o);
        p.finish();
        // all
        p.start();
        for(Data o1 : dal.getAllRecords()) {
            p.one(o1);
        }
        p.finish();
    }
}

TestSpring.java:

package di.stdann;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;
import org.springframework.stereotype.Component;

import di.common.Application;

@Component
public class TestSpring {
    public static void main(String[] args) {
        @SuppressWarnings("resource")
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
        //ctx.scan("di.stdann.mock");
        ctx.scan("di.stdann.db");
        //ctx.scan("di.stdann.plain");
        ctx.scan("di.stdann.html");
        ctx.scan("di.stdann.std");
        ctx.refresh();
        Application app = ctx.getBean(Application.class);
        app.run();
    }
}

Note that even though the example scan different packages for different setups, then in real life it may be more common scanning same package but with different jar files in classpath.

TestGuice.java:

package di.stdann;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;

import di.common.Application;
import di.common.DataAccessLayer;
import di.common.Presenter;
import di.stdann.db.DbDataAccessLayer;
import di.stdann.html.HtmlPresenter;
//import di.stdann.mock.MockDataAccessLayer;
//import di.stdann.plain.PlainPresenter;
import di.stdann.std.StandardApplication;

public class TestGuice {
    public static class MyModule extends AbstractModule {
        @Override
        protected void configure() {
            bind(Application.class).to(StandardApplication.class);
            //bind(DataAccessLayer.class).to(MockDataAccessLayer.class);
            bind(DataAccessLayer.class).to(DbDataAccessLayer.class);
            //bind(Presenter.class).to(PlainPresenter.class);
            bind(Presenter.class).to(HtmlPresenter.class);
        }
    }
    public static void main(String[] args) {
        Injector inj = Guice.createInjector(new MyModule());
        Application app = inj.getInstance(Application.class);
        app.run();
    }
}

Inst.java:

package di.stdann.inst;

import javax.inject.Inject;
import javax.inject.Named;
import javax.inject.Singleton;

public class Inst {
    @Named("c1")
    @Singleton
    public static class C1 {
        private static Object lck = new Object();
        private static int v1 = 0;
        private int v2;
        public C1() {
            synchronized(lck) {
                v1++;
                v2 = v1;
            }
        }
        @Override
        public String toString() {
            return String.format("%d/%d", v2, v1);
        }
    }
    @Named("c2")
    public static class C2 {
        private static Object lck = new Object();
        private static int v1 = 0;
        private int v2;
        public C2() {
            synchronized(lck) {
                v1++;
                v2 = v1;
            }
        }
        @Override
        public String toString() {
            return String.format("%d/%d", v2, v1);
        }
    }
    @Named("c")
    public static class C {
        @Inject
        private C1 c1a, c1b;
        @Inject
        private C2 c2a, c2b;
        public void test() {
            System.out.printf("Singleton : %s %s\n", c1a, c1b);
            System.out.printf("Default   : %s %s\n", c2a, c2b);
        }
    }
}

InstSpring.java:

package di.stdann.inst;

import org.springframework.context.annotation.AnnotationConfigApplicationContext;

public class InstSpring extends Inst {
    public static void main(String[] args) {
        @SuppressWarnings("resource")
        AnnotationConfigApplicationContext ctx = new AnnotationConfigApplicationContext();
        ctx.scan("di.stdann.inst");
        ctx.refresh();
        C o = ctx.getBean("c", C.class);
        o.test();
    }
}

Output:

Singleton : 1/1 1/1
Default   : 1/1 1/1

InstGuice.java:

package di.stdann.inst;

import com.google.inject.Guice;
import com.google.inject.Injector;

public class InstGuice extends Inst {
    public static void main(String[] args) {
        Injector inj = Guice.createInjector();
        C o = inj.getInstance(C.class);
        o.test();
    }
}

Output:

Singleton : 1/1 1/1
Default   : 1/2 2/2

We note that JSR 330 only guarantee singleton scope to be available. The rest is implementation specific.

Microsoft MEF (Managed Extensibility Framework) was presented as an extension framework, but it was really a DI framework. It was introduced with .NET 4.0 (2010), but it never evolved much and are de facto EOL today.

Classes that can be injected are marked as:

...
[Export]
public class SomeClass
{
    ...
}

Fields to be injected are marked as:

    ...
    [Import]
    private SomeType fieldname;
    ...

The matching is done as:

AggregateCatalog cat = new AggregateCatalog();
cat.Catalogs.Add(somecatalog);
cat.Catalogs.Add(somepthercatalog);
CompositionContainer cont = new CompositionContainer(cat);
SomeInterface var = new SomeClass();
cont.SatisfyImportsOnce(var);

Program.cs:

using System;
using System.Collections.Generic;
using System.ComponentModel.Composition;
using System.ComponentModel.Composition.Hosting;
using System.Configuration;
using System.Data;
using System.Data.Common;

using DI.Common;

namespace DI.MEF
{
    [Export(typeof(IDataAccessLayer))]
    public class MockDataAccessLayer : IDataAccessLayer
    {
        public Data GetOneRecord(int f1)
        {
            return new Data { F1 = f1, F2 = "X" + f1 };
        }
        public IList<Data> GetAllRecords()
        {
            IList<Data> res = new List<Data>();
            res.Add(new Data { F1 = 1, F2 = "A" });
            res.Add(new Data { F1 = 2, F2 = "BB" });
            res.Add(new Data { F1 = 3, F2 = "CCC" });
            return res;
       }
    }
    [Export(typeof(IDataAccessLayer))]
    public class DbDataAccessLayer : IDataAccessLayer
    {
        [Import("Prov")]
        public string Prov { get; set; }
        [Import("ConStr")]
        public string ConStr { get; set; }
        private IDbConnection GetConnection()
        {
            IDbConnection con = DbProviderFactories.GetFactory(Prov).CreateConnection();
            con.ConnectionString = ConStr;
            return con;
        }
        public Data GetOneRecord(int f1)
        {
            using(IDbConnection con = GetConnection())
            {
                con.Open();
                IDbCommand cmd = con.CreateCommand();
                cmd.CommandText = "SELECT f1,f2 FROM t1 WHERE f1 = @f1";
                cmd.Connection = con;
                IDbDataParameter pf1 = cmd.CreateParameter();
                pf1.ParameterName = "@f1";
                pf1.DbType = DbType.Int32;
                pf1.Value = f1;
                cmd.Parameters.Add(pf1);
                IDataReader rdr = cmd.ExecuteReader();
                if(rdr.Read())
                {
                    return new Data { F1 = (int)rdr["f1"], F2 = (string)rdr["f2"] };
                }
                else
                {
                    return null;
                }
            }
        }
        public IList<Data> GetAllRecords()
        {
            IList<Data> res = new List<Data>();
            using(IDbConnection con = GetConnection())
            {
                con.Open();
                IDbCommand cmd = con.CreateCommand();
                cmd.CommandText = "SELECT f1,f2 FROM t1";
                cmd.Connection = con;
                IDataReader rdr = cmd.ExecuteReader();
                while(rdr.Read())
                {
                    res.Add(new Data { F1 = (int)rdr["f1"], F2 = (string)rdr["f2"] });
                }
            }
            return res;
       }
    }
    [Export(typeof(IPresenter))]
    public class PlainPresenter : IPresenter
    {
        public void Start()
        {
            // nothing
        }
        public void One(Data o)
        {
            Console.WriteLine(o.F1 + " " + o.F2);
        }
        public void Finish()
        {
            // nothing
        }
    }
    [Export(typeof(IPresenter))]
    public class HtmlPresenter : IPresenter
    {
        [Import("Lbl1")]
        public String Lbl1 { get; set; }
        [Import("Lbl2")]
        public String Lbl2 { get; set; }
        public void Start()
        {
            Console.WriteLine("<table>");
            Console.WriteLine("<tr>");
            Console.WriteLine("<th>" + Lbl1 + "</th>");
            Console.WriteLine("<th>" + Lbl2 + "</th>");
            Console.WriteLine("</tr>");
        }
        public void One(Data o)
        {
            Console.WriteLine("<tr>");
            Console.WriteLine("<td>" + o.F1 + "</td>");
            Console.WriteLine("<td>" + o.F2 + "</td>");
            Console.WriteLine("</tr>");
        }
        public void Finish()
        {
            Console.WriteLine("</table>");
        }
    }
    public class StandardApplication : IApplication
    {
        [Import]
        private IDataAccessLayer dal;
        [Import]
        private IPresenter p;
        public void Run()
        {
            // one
            p.Start();
            Data o = dal.GetOneRecord(2);
            p.One(o);
            p.Finish();
            // all
            p.Start();
            foreach(Data o1 in dal.GetAllRecords())
            {
                p.One(o1);
            }
            p.Finish();
        }
    }
    [Export]
    [PartCreationPolicy(CreationPolicy.Shared)]
    public class C1
    {
        private static object lck = new object();
        private static int v1 = 0;
        private int v2;
        public C1()
        {
            lock(lck)
            {
                v1++;
                v2 = v1;
            }
        }
        public override string ToString()
        {
            return string.Format("{0}/{1}", v2, v1);
        }
    }
    [Export]
    [PartCreationPolicy(CreationPolicy.NonShared)]
    public class C2
    {
        private static object lck = new object();
        private static int v1 = 0;
        private int v2;
        public C2()
        {
            lock(lck)
            {
                v1++;
                v2 = v1;
            }
        }
        public override string ToString()
        {
            return string.Format("{0}/{1}", v2, v1);
        }
    }
    [Export]
    public class C3
    {
        private static object lck = new object();
        private static int v1 = 0;
        private int v2;
        public C3()
        {
            lock(lck)
            {
                v1++;
                v2 = v1;
            }
        }
        public override string ToString()
        {
            return string.Format("{0}/{1}", v2, v1);
        }
    }
    public class C
    {
        [Import]
        private C1 c1a, c1b;
        [Import]
        private C2 c2a, c2b;
        [Import]
        private C3 c3a, c3b;
        public void Test()
        {
            Console.WriteLine("Shared    : {0} {1}", c1a, c1b);
            Console.WriteLine("NonShared : {0} {1}", c2a, c2b);
            Console.WriteLine("Default   : {0} {1}", c3a, c3b);
        }
    }
    public class Program
    {
        public class DbConfig
        {
            [Export("Prov")]
            public string Prov = ConfigurationManager.AppSettings["DBProvider"];
            [Export("ConStr")]
            public string ConStr = ConfigurationManager.AppSettings["DBConnectionString"];
        }
        public class HtmlConfig
        {
            [Export("Lbl1")]
            public String Lbl1 = "F1";
            [Export("Lbl2")]
            public String Lbl2 = "F1";
        }
        public static void Main(string[] args)
        {
            AggregateCatalog cat = new AggregateCatalog();
            //cat.Catalogs.Add(new TypeCatalog(typeof(MockDataAccessLayer), typeof(PlainPresenter)));
            cat.Catalogs.Add(new TypeCatalog(typeof(DbDataAccessLayer), typeof(DbConfig), typeof(HtmlPresenter), typeof(HtmlConfig)));
            cat.Catalogs.Add(new TypeCatalog(typeof(C1), typeof(C2), typeof(C3), typeof(C)));
            CompositionContainer cont = new CompositionContainer(cat);
            IApplication app = new StandardApplication();
            cont.SatisfyImportsOnce(app);
            app.Run();
            C o = new C();
            cont.SatisfyImportsOnce(o);
            o.Test();
        }
    }
}

Note that even though the example use TypeCatalog's with explicit types, then in the real world it may be more common to use DirectoryCatalog's with directories containing assemblies.

app.config:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <appSettings>
        <add key="DBProvider" value="MySql.Data.MySqlClient" />
        <add key="DBConnectionString" value="Server=localhost;Database=Test;User Id=root;Password=" />
     </appSettings>
</configuration>

Output:

...
Shared    : 1/1 1/1
NonShared : 1/2 2/2
Default   : 1/1 1/1

MEF was never popular in the .NET world, but it actually have some very cool features in how it matches up things.

Configuration code:

Here the DI is defined as rules in code and the DI framework just construct based on the rules.

Google Guice is an open source DI framework for Java created by Google in 2007.

Configuration and use works like:

public class MyModule extends AbstractModule {
    @Override
    protected void configure() {
        bind(SomeInterface.class).to(SomeClass.class);
        bind(SomeOtherInterface.class).to(SomeOtherClass.class);
    }
}
...
Injector inj = Guice.createInjector(new MyModule());
SomeInterface var = inj.getInstance(SomeInterface.class);

MockDataAccessLayer.java:

package di.guice;

import java.util.ArrayList;
import java.util.List;

import di.common.Data;
import di.common.DataAccessLayer;

public class MockDataAccessLayer implements DataAccessLayer {
    @Override
    public Data getOneRecord(int f1) {
        return new Data(f1, "X" + f1);
    }

    @Override
    public List<Data> getAllRecords() {
        List<Data> res = new ArrayList<Data>();
        res.add(new Data(1, "A"));
        res.add(new Data(2, "BB"));
        res.add(new Data(3, "CCC"));
        return res;
    }
}

DbDataAccessLayer.java:

package di.guice;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.List;
import java.util.Properties;

import di.common.Data;
import di.common.DataAccessLayer;
import di.common.Util;

public class DbDataAccessLayer implements DataAccessLayer {
    private String conurl;
    private String un;
    private String pw;
    public DbDataAccessLayer() {
        Properties props = Util.loadProperties("/work/jdbc.properties");
        conurl = props.getProperty("db.conurl");
        un = props.getProperty("db.un");
        pw = props.getProperty("db.pw");
    }
    @Override
    public Data getOneRecord(int f1) {
        try {
            try(Connection con = DriverManager.getConnection(conurl, un, pw)) {
                PreparedStatement pstmt = con.prepareStatement("SELECT f1,f2 FROM t1 WHERE f1 = ?");
                pstmt.setInt(1,  f1);
                ResultSet rs = pstmt.executeQuery();
                if(rs.next()) {
                    return new Data(rs.getInt(1), rs.getString(2));
                } else {
                    return null;
                }
            }
        } catch (SQLException e) {
            return null;
        }
    }
    @Override
    public List<Data> getAllRecords() {
        try {
            List<Data> res = new ArrayList<Data>();
            try(Connection con = DriverManager.getConnection(conurl, un, pw)) {
                PreparedStatement pstmt = con.prepareStatement("SELECT f1,f2 FROM t1");
                ResultSet rs = pstmt.executeQuery();
                while(rs.next()) {
                    res.add(new Data(rs.getInt(1), rs.getString(2)));
                }
            }
            return res;
        } catch (SQLException e) {
            return null;
        }
    }
}

PlainPresenter.java:

package di.guice;

import di.common.Data;
import di.common.Presenter;

public class PlainPresenter implements Presenter {
    @Override
    public void start() {
        // nothing
    }
    @Override
    public void one(Data o) {
        System.out.println(o.getF1() + " " + o.getF2());
    }
    @Override
    public void finish() {
        // nothing
    }
}

HtmlPresenter.java:

package di.guice;

import java.util.Properties;

import di.common.Data;
import di.common.Presenter;
import di.common.Util;

public class HtmlPresenter implements Presenter {
    private String lbl1; 
    private String lbl2; 
    public HtmlPresenter() {
        Properties props = Util.loadProperties("/work/html.properties");
        lbl1 = props.getProperty("html.lbl1");
        lbl2 = props.getProperty("html.lbl2");
    }
    @Override
    public void start() {
        System.out.println("<table>");
        System.out.println("<tr>");
        System.out.println("<th>" + lbl1 + "</th>");
        System.out.println("<th>" + lbl2 + "</th>");
        System.out.println("</tr>");
    }
    @Override
    public void one(Data o) {
        System.out.println("<tr>");
        System.out.println("<td>" + o.getF1() + "</td>");
        System.out.println("<td>" + o.getF2() + "</td>");
        System.out.println("</tr>");
    }
    @Override
    public void finish() {
        System.out.println("</table>");
    }
}

StandardApplication.java:

package di.guice;

import com.google.inject.Inject;

import di.common.Application;
import di.common.Data;
import di.common.DataAccessLayer;
import di.common.Presenter;

public class StandardApplication implements Application {
    @Inject
    private DataAccessLayer dal;
    @Inject
    private Presenter p;
    @Override
    public void run() {
        // one
        p.start();
        Data o = dal.getOneRecord(2);
        p.one(o);
        p.finish();
        // all
        p.start();
        for(Data o1 : dal.getAllRecords()) {
            p.one(o1);
        }
        p.finish();
    }
}

Test.java:

package di.guice;

import com.google.inject.AbstractModule;
import com.google.inject.Guice;
import com.google.inject.Injector;

import di.common.Application;
import di.common.DataAccessLayer;
import di.common.Presenter;

public class Test {
    public static class MyModule extends AbstractModule {
        @Override
        protected void configure() {
            bind(Application.class).to(StandardApplication.class);
            //bind(DataAccessLayer.class).to(MockDataAccessLayer.class);
            bind(DataAccessLayer.class).to(DbDataAccessLayer.class);
            //bind(Presenter.class).to(PlainPresenter.class);
            bind(Presenter.class).to(HtmlPresenter.class);
        }
    }
    public static void main(String[] args) {
        Injector inj = Guice.createInjector(new MyModule());
        Application app = inj.getInstance(Application.class);
        app.run();
    }
}

Inst.java:

package di.guice;

import com.google.inject.Guice;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Singleton;

public class Inst {
    @Singleton
    public static class C1 {
        private static Object lck = new Object();
        private static int v1 = 0;
        private int v2;
        public C1() {
            synchronized(lck) {
                v1++;
                v2 = v1;
            }
        }
        @Override
        public String toString() {
            return String.format("%d/%d", v2, v1);
        }
    }
    public static class C2 {
        private static Object lck = new Object();
        private static int v1 = 0;
        private int v2;
        public C2() {
            synchronized(lck) {
                v1++;
                v2 = v1;
            }
        }
        @Override
        public String toString() {
            return String.format("%d/%d", v2, v1);
        }
    }
    public static class C {
        @Inject
        private C1 c1a, c1b;
        @Inject
        private C2 c2a, c2b;
        public void test() {
            System.out.printf("Singleton : %s %s\n", c1a, c1b);
            System.out.printf("Default   : %s %s\n", c2a, c2b);
        }
    }
    public static void main(String[] args) {
        Injector inj = Guice.createInjector();
        C o = inj.getInstance(C.class);
        o.test();
    }
}

Output:

Singleton : 1/1 1/1
Default   : 1/2 2/2

NInject is an open source DI framework for .NET created by Nate Kohari in 2007.

Configuration and use works like:

IKernel krnl = new StandardKernel();
krnl.Bind<SomeInterface>().To<SomeClass>();
krnl.Bind<SomeOtherInterface>().To<SomeOtherClass>();
SomeInterface var = krnl.Get<SomeINterface>();

Program.cs:

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.Common;

using Ninject;

using DI.Common;

namespace DI.NInject
{
    public class MockDataAccessLayer : IDataAccessLayer
    {
        public Data GetOneRecord(int f1)
        {
            return new Data { F1 = f1, F2 = "X" + f1 };
        }
        public IList<Data> GetAllRecords()
        {
            IList<Data> res = new List<Data>();
            res.Add(new Data { F1 = 1, F2 = "A" });
            res.Add(new Data { F1 = 2, F2 = "BB" });
            res.Add(new Data { F1 = 3, F2 = "CCC" });
            return res;
       }
    }
    public class DbDataAccessLayer : IDataAccessLayer
    {
        private string prov;
        private string constr;
        private IDbConnection GetConnection()
        {
            IDbConnection con = DbProviderFactories.GetFactory(prov).CreateConnection();
            con.ConnectionString = constr;
            return con;
        }
        public DbDataAccessLayer(String prov, String constr)
        {
            this.prov = prov;
            this.constr = constr;
        }
        public Data GetOneRecord(int f1)
        {
            using(IDbConnection con = GetConnection())
            {
                con.Open();
                IDbCommand cmd = con.CreateCommand();
                cmd.CommandText = "SELECT f1,f2 FROM t1 WHERE f1 = @f1";
                cmd.Connection = con;
                IDbDataParameter pf1 = cmd.CreateParameter();
                pf1.ParameterName = "@f1";
                pf1.DbType = DbType.Int32;
                pf1.Value = f1;
                cmd.Parameters.Add(pf1);
                IDataReader rdr = cmd.ExecuteReader();
                if(rdr.Read())
                {
                    return new Data { F1 = (int)rdr["f1"], F2 = (string)rdr["f2"] };
                }
                else
                {
                    return null;
                }
            }
        }
        public IList<Data> GetAllRecords()
        {
            IList<Data> res = new List<Data>();
            using(IDbConnection con = GetConnection())
            {
                con.Open();
                IDbCommand cmd = con.CreateCommand();
                cmd.CommandText = "SELECT f1,f2 FROM t1";
                cmd.Connection = con;
                IDataReader rdr = cmd.ExecuteReader();
                while(rdr.Read())
                {
                    res.Add(new Data { F1 = (int)rdr["f1"], F2 = (string)rdr["f2"] });
                }
            }
            return res;
       }
    }
    public class PlainPresenter : IPresenter
    {
        public void Start()
        {
            // nothing
        }
        public void One(Data o)
        {
            Console.WriteLine(o.F1 + " " + o.F2);
        }
        public void Finish()
        {
            // nothing
        }
    }
    public class HtmlPresenter : IPresenter
    {
        private String lbl1; 
        private String lbl2; 
        public HtmlPresenter(String lbl1, String lbl2)
        {
            this.lbl1 = lbl1;
            this.lbl2 = lbl2;
        }
        public void Start()
        {
            Console.WriteLine("<table>");
            Console.WriteLine("<tr>");
            Console.WriteLine("<th>" + lbl1 + "</th>");
            Console.WriteLine("<th>" + lbl2 + "</th>");
            Console.WriteLine("</tr>");
        }
        public void One(Data o)
        {
            Console.WriteLine("<tr>");
            Console.WriteLine("<td>" + o.F1 + "</td>");
            Console.WriteLine("<td>" + o.F2 + "</td>");
            Console.WriteLine("</tr>");
        }
        public void Finish()
        {
            Console.WriteLine("</table>");
        }
    }
    public class StandardApplication : IApplication
    {
        private IDataAccessLayer dal;
        private IPresenter p;
        public StandardApplication(IDataAccessLayer dal, IPresenter p)
        {
            this.dal = dal;
            this.p = p;
        }
        public void Run()
        {
            // one
            p.Start();
            Data o = dal.GetOneRecord(2);
            p.One(o);
            p.Finish();
            // all
            p.Start();
            foreach(Data o1 in dal.GetAllRecords())
            {
                p.One(o1);
            }
            p.Finish();
        }
    }
    public class C1
    {
        private static object lck = new object();
        private static int v1 = 0;
        private int v2;
        public C1()
        {
            lock(lck)
            {
                v1++;
                v2 = v1;
            }
        }
        public override string ToString()
        {
            return string.Format("{0}/{1}", v2, v1);
        }
    }
    public class C2
    {
        private static object lck = new object();
        private static int v1 = 0;
        private int v2;
        public C2()
        {
            lock(lck)
            {
                v1++;
                v2 = v1;
            }
        }
        public override string ToString()
        {
            return string.Format("{0}/{1}", v2, v1);
        }
    }
    public class C3
    {
        private static object lck = new object();
        private static int v1 = 0;
        private int v2;
        public C3()
        {
            lock(lck)
            {
                v1++;
                v2 = v1;
            }
        }
        public override string ToString()
        {
            return string.Format("{0}/{1}", v2, v1);
        }
    }
    public class C
    {
        private C1 c1a, c1b;
        private C2 c2a, c2b;
        private C3 c3a, c3b;
        public C(C1 c1a, C1 c1b, C2 c2a, C2 c2b, C3 c3a, C3 c3b)
        {
            this.c1a = c1a;
            this.c1b = c1b;
            this.c2a = c2a;
            this.c2b = c2b;
            this.c3a = c3a;
            this.c3b = c3b;
        }
        public void Test()
        {
            Console.WriteLine("Singleton : {0} {1}", c1a, c1b);
            Console.WriteLine("Transient : {0} {1}", c2a, c2b);
            Console.WriteLine("Default   : {0} {1}", c3a, c3b);
        }
    }
    public class Program
    {
        public static void Main(string[] args)
        {
            IKernel krnl = new StandardKernel();
            krnl.Bind<IApplication>().To<StandardApplication>();
            //krnl.Bind<IDataAccessLayer>().To<MockDataAccessLayer>();
            krnl.Bind<IDataAccessLayer>().To<DbDataAccessLayer>().WithConstructorArgument("prov", ConfigurationManager.AppSettings["DBProvider"]).WithConstructorArgument("constr", ConfigurationManager.AppSettings["DBConnectionString"]);
            //krnl.Bind<IPresenter>().To<PlainPresenter>();
            krnl.Bind<IPresenter>().To<HtmlPresenter>().WithConstructorArgument("lbl1", "F1").WithConstructorArgument("lbl2", "F2");
            krnl.Bind<C1>().To<C1>().InSingletonScope();
            krnl.Bind<C2>().To<C2>().InTransientScope();
            krnl.Bind<C3>().To<C3>();
            krnl.Bind<C>().To<C>();
            IApplication app = krnl.Get<IApplication>();
            app.Run();
            C o = krnl.Get<C>();
            o.Test();
        }
    }
}

app.config:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <appSettings>
        <add key="DBProvider" value="MySql.Data.MySqlClient" />
        <add key="DBConnectionString" value="Server=localhost;Database=Test;User Id=root;Password=" />
     </appSettings>
</configuration>

Output:

...
Singleton : 1/1 1/1
Transient : 1/2 2/2
Default   : 1/2 2/2

Castle Winsor is an open source DI framework that is part of the Castle family of open source projects. It was created in 2004 by Hamilton de Oliveira.

Configuration and use works like:

WindsorContainer cont = new WindsorContainer();
cont.Register(Component.For<SomeInterface>().ImplementedBy<SomeClass>());
cont.Register(Component.For<SomeOtherInterface>().ImplementedBy<SomeOtherClass>());
SomeInterface var = cont.Resolve<SomeInterface>();

Program.cs:

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.Common;

using Castle.MicroKernel.Registration;
using Castle.Windsor;

using DI.Common;

namespace DI.Windsor
{
    public class MockDataAccessLayer : IDataAccessLayer
    {
        public Data GetOneRecord(int f1)
        {
            return new Data { F1 = f1, F2 = "X" + f1 };
        }
        public IList<Data> GetAllRecords()
        {
            IList<Data> res = new List<Data>();
            res.Add(new Data { F1 = 1, F2 = "A" });
            res.Add(new Data { F1 = 2, F2 = "BB" });
            res.Add(new Data { F1 = 3, F2 = "CCC" });
            return res;
       }
    }
    public class DbDataAccessLayer : IDataAccessLayer
    {
        private string prov;
        private string constr;
        private IDbConnection GetConnection()
        {
            IDbConnection con = DbProviderFactories.GetFactory(prov).CreateConnection();
            con.ConnectionString = constr;
            return con;
        }
        public DbDataAccessLayer(String prov, String constr)
        {
            this.prov = prov;
            this.constr = constr;
        }
        public Data GetOneRecord(int f1)
        {
            using(IDbConnection con = GetConnection())
            {
                con.Open();
                IDbCommand cmd = con.CreateCommand();
                cmd.CommandText = "SELECT f1,f2 FROM t1 WHERE f1 = @f1";
                cmd.Connection = con;
                IDbDataParameter pf1 = cmd.CreateParameter();
                pf1.ParameterName = "@f1";
                pf1.DbType = DbType.Int32;
                pf1.Value = f1;
                cmd.Parameters.Add(pf1);
                IDataReader rdr = cmd.ExecuteReader();
                if(rdr.Read())
                {
                    return new Data { F1 = (int)rdr["f1"], F2 = (string)rdr["f2"] };
                }
                else
                {
                    return null;
                }
            }
        }
        public IList<Data> GetAllRecords()
        {
            IList<Data> res = new List<Data>();
            using(IDbConnection con = GetConnection())
            {
                con.Open();
                IDbCommand cmd = con.CreateCommand();
                cmd.CommandText = "SELECT f1,f2 FROM t1";
                cmd.Connection = con;
                IDataReader rdr = cmd.ExecuteReader();
                while(rdr.Read())
                {
                    res.Add(new Data { F1 = (int)rdr["f1"], F2 = (string)rdr["f2"] });
                }
            }
            return res;
       }
    }
    public class PlainPresenter : IPresenter
    {
        public void Start()
        {
            // nothing
        }
        public void One(Data o)
        {
            Console.WriteLine(o.F1 + " " + o.F2);
        }
        public void Finish()
        {
            // nothing
        }
    }
    public class HtmlPresenter : IPresenter
    {
        private String lbl1; 
        private String lbl2; 
        public HtmlPresenter(String lbl1, String lbl2)
        {
            this.lbl1 = lbl1;
            this.lbl2 = lbl2;
        }
        public void Start()
        {
            Console.WriteLine("<table>");
            Console.WriteLine("<tr>");
            Console.WriteLine("<th>" + lbl1 + "</th>");
            Console.WriteLine("<th>" + lbl2 + "</th>");
            Console.WriteLine("</tr>");
        }
        public void One(Data o)
        {
            Console.WriteLine("<tr>");
            Console.WriteLine("<td>" + o.F1 + "</td>");
            Console.WriteLine("<td>" + o.F2 + "</td>");
            Console.WriteLine("</tr>");
        }
        public void Finish()
        {
            Console.WriteLine("</table>");
        }
    }
    public class StandardApplication : IApplication
    {
        private IDataAccessLayer dal;
        private IPresenter p;
        public StandardApplication(IDataAccessLayer dal, IPresenter p)
        {
            this.dal = dal;
            this.p = p;
        }
        public void Run()
        {
            // one
            p.Start();
            Data o = dal.GetOneRecord(2);
            p.One(o);
            p.Finish();
            // all
            p.Start();
            foreach(Data o1 in dal.GetAllRecords())
            {
                p.One(o1);
            }
            p.Finish();
        }
    }
    public class C1
    {
        private static object lck = new object();
        private static int v1 = 0;
        private int v2;
        public C1()
        {
            lock(lck)
            {
                v1++;
                v2 = v1;
            }
        }
        public override string ToString()
        {
            return string.Format("{0}/{1}", v2, v1);
        }
    }
    public class C2
    {
        private static object lck = new object();
        private static int v1 = 0;
        private int v2;
        public C2()
        {
            lock(lck)
            {
                v1++;
                v2 = v1;
            }
        }
        public override string ToString()
        {
            return string.Format("{0}/{1}", v2, v1);
        }
    }
    public class C3
    {
        private static object lck = new object();
        private static int v1 = 0;
        private int v2;
        public C3()
        {
            lock(lck)
            {
                v1++;
                v2 = v1;
            }
        }
        public override string ToString()
        {
            return string.Format("{0}/{1}", v2, v1);
        }
    }
    public class C
    {
        private C1 c1a, c1b;
        private C2 c2a, c2b;
        private C3 c3a, c3b;
        public C(C1 c1a, C1 c1b, C2 c2a, C2 c2b, C3 c3a, C3 c3b)
        {
            this.c1a = c1a;
            this.c1b = c1b;
            this.c2a = c2a;
            this.c2b = c2b;
            this.c3a = c3a;
            this.c3b = c3b;
        }
        public void Test()
        {
            Console.WriteLine("Singleton : {0} {1}", c1a, c1b);
            Console.WriteLine("Transient : {0} {1}", c2a, c2b);
            Console.WriteLine("Default   : {0} {1}", c3a, c3b);
        }
    }
    public class Program
    {
        public static void Main(string[] args)
        {
            WindsorContainer cont = new WindsorContainer();
            cont.Register(Component.For<IApplication>().ImplementedBy<StandardApplication>());
            //cont.Register(Component.For<IDataAccessLayer>().ImplementedBy<MockDataAccessLayer>());
            cont.Register(Component.For<IDataAccessLayer>().ImplementedBy<DbDataAccessLayer>().DependsOn(Dependency.OnValue("prov", ConfigurationManager.AppSettings["DBProvider"]), Dependency.OnValue("constr", ConfigurationManager.AppSettings["DBConnectionString"])));
            //cont.Register(Component.For<IPresenter>().ImplementedBy<PlainPresenter>());
            cont.Register(Component.For<IPresenter>().ImplementedBy<HtmlPresenter>().DependsOn(Dependency.OnValue("lbl1", "F1"), Dependency.OnValue("lbl2", "F2")));
            IApplication app = cont.Resolve<IApplication>();
            app.Run();
            cont.Register(Component.For<C1>().LifestyleSingleton());
            cont.Register(Component.For<C2>().LifestyleTransient());
            cont.Register(Component.For<C3>());
            cont.Register(Component.For<C>());
            C o = cont.Resolve<C>();
            o.Test();
        }
    }
}

app.config:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <appSettings>
        <add key="DBProvider" value="MySql.Data.MySqlClient" />
        <add key="DBConnectionString" value="Server=localhost;Database=Test;User Id=root;Password=" />
     </appSettings>
</configuration>

Output:

...
Singleton : 1/1 1/1
Transient : 1/2 2/2
Default   : 1/1 1/1

Microsoft Unity was a DI framework first released as an application block in Microsoft Enterprise Library in 2008.

In 2015 it became an open source project.

Configuration and use works like:

UnityContainer cont = new UnityContainer();
cont.RegisterType<SomeInterface, SomeClass>();
cont.RegisterType<SomeOtherInterface, SomeOtherClass>();
SomeInterface var = cont.Resolve<SomeInterface>();

Program.cs:

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.Common;

using Unity;
using Unity.Injection;
using Unity.Lifetime;

using DI.Common;

namespace DI.Unity
{
    public class MockDataAccessLayer : IDataAccessLayer
    {
        public Data GetOneRecord(int f1)
        {
            return new Data { F1 = f1, F2 = "X" + f1 };
        }
        public IList<Data> GetAllRecords()
        {
            IList<Data> res = new List<Data>();
            res.Add(new Data { F1 = 1, F2 = "A" });
            res.Add(new Data { F1 = 2, F2 = "BB" });
            res.Add(new Data { F1 = 3, F2 = "CCC" });
            return res;
       }
    }
    public class DbDataAccessLayer : IDataAccessLayer
    {
        private string prov;
        private string constr;
        private IDbConnection GetConnection()
        {
            IDbConnection con = DbProviderFactories.GetFactory(prov).CreateConnection();
            con.ConnectionString = constr;
            return con;
        }
        public DbDataAccessLayer(String prov, String constr)
        {
            this.prov = prov;
            this.constr = constr;
        }
        public Data GetOneRecord(int f1)
        {
            using(IDbConnection con = GetConnection())
            {
                con.Open();
                IDbCommand cmd = con.CreateCommand();
                cmd.CommandText = "SELECT f1,f2 FROM t1 WHERE f1 = @f1";
                cmd.Connection = con;
                IDbDataParameter pf1 = cmd.CreateParameter();
                pf1.ParameterName = "@f1";
                pf1.DbType = DbType.Int32;
                pf1.Value = f1;
                cmd.Parameters.Add(pf1);
                IDataReader rdr = cmd.ExecuteReader();
                if(rdr.Read())
                {
                    return new Data { F1 = (int)rdr["f1"], F2 = (string)rdr["f2"] };
                }
                else
                {
                    return null;
                }
            }
        }
        public IList<Data> GetAllRecords()
        {
            IList<Data> res = new List<Data>();
            using(IDbConnection con = GetConnection())
            {
                con.Open();
                IDbCommand cmd = con.CreateCommand();
                cmd.CommandText = "SELECT f1,f2 FROM t1";
                cmd.Connection = con;
                IDataReader rdr = cmd.ExecuteReader();
                while(rdr.Read())
                {
                    res.Add(new Data { F1 = (int)rdr["f1"], F2 = (string)rdr["f2"] });
                }
            }
            return res;
       }
    }
    public class PlainPresenter : IPresenter
    {
        public void Start()
        {
            // nothing
        }
        public void One(Data o)
        {
            Console.WriteLine(o.F1 + " " + o.F2);
        }
        public void Finish()
        {
            // nothing
        }
    }
    public class HtmlPresenter : IPresenter
    {
        private String lbl1; 
        private String lbl2; 
        public HtmlPresenter(String lbl1, String lbl2)
        {
            this.lbl1 = lbl1;
            this.lbl2 = lbl2;
        }
        public void Start()
        {
            Console.WriteLine("<table>");
            Console.WriteLine("<tr>");
            Console.WriteLine("<th>" + lbl1 + "</th>");
            Console.WriteLine("<th>" + lbl2 + "</th>");
            Console.WriteLine("</tr>");
        }
        public void One(Data o)
        {
            Console.WriteLine("<tr>");
            Console.WriteLine("<td>" + o.F1 + "</td>");
            Console.WriteLine("<td>" + o.F2 + "</td>");
            Console.WriteLine("</tr>");
        }
        public void Finish()
        {
            Console.WriteLine("</table>");
        }
    }
    public class StandardApplication : IApplication
    {
        private IDataAccessLayer dal;
        private IPresenter p;
        public StandardApplication(IDataAccessLayer dal, IPresenter p)
        {
            this.dal = dal;
            this.p = p;
        }
        public void Run()
        {
            // one
            p.Start();
            Data o = dal.GetOneRecord(2);
            p.One(o);
            p.Finish();
            // all
            p.Start();
            foreach(Data o1 in dal.GetAllRecords())
            {
                p.One(o1);
            }
            p.Finish();
        }
    }
    public class C1
    {
        private static object lck = new object();
        private static int v1 = 0;
        private int v2;
        public C1()
        {
            lock(lck)
            {
                v1++;
                v2 = v1;
            }
        }
        public override string ToString()
        {
            return string.Format("{0}/{1}", v2, v1);
        }
    }
    public class C2
    {
        private static object lck = new object();
        private static int v1 = 0;
        private int v2;
        public C2()
        {
            lock(lck)
            {
                v1++;
                v2 = v1;
            }
        }
        public override string ToString()
        {
            return string.Format("{0}/{1}", v2, v1);
        }
    }
    public class C3
    {
        private static object lck = new object();
        private static int v1 = 0;
        private int v2;
        public C3()
        {
            lock(lck)
            {
                v1++;
                v2 = v1;
            }
        }
        public override string ToString()
        {
            return string.Format("{0}/{1}", v2, v1);
        }
    }
    public class C
    {
        private C1 c1a, c1b;
        private C2 c2a, c2b;
        private C3 c3a, c3b;
        public C(C1 c1a, C1 c1b, C2 c2a, C2 c2b, C3 c3a, C3 c3b)
        {
            this.c1a = c1a;
            this.c1b = c1b;
            this.c2a = c2a;
            this.c2b = c2b;
            this.c3a = c3a;
            this.c3b = c3b;
        }
        public void Test()
        {
            Console.WriteLine("Singleton : {0} {1}", c1a, c1b);
            Console.WriteLine("Transient : {0} {1}", c2a, c2b);
            Console.WriteLine("Default   : {0} {1}", c3a, c3b);
        }
    }
    public class Program
    {
        public static void Main(string[] args)
        {
            UnityContainer cont = new UnityContainer();
            cont.RegisterType<IApplication, StandardApplication>();
            //cont.RegisterType<IDataAccessLayer, MockDataAccessLayer>();
            cont.RegisterType<IDataAccessLayer, DbDataAccessLayer>();
            cont.RegisterType<DbDataAccessLayer>(new InjectionConstructor(new object[] { ConfigurationManager.AppSettings["DBProvider"], ConfigurationManager.AppSettings["DBConnectionString"]}));
            //cont.RegisterType<IPresenter, PlainPresenter>();
            cont.RegisterType<IPresenter, HtmlPresenter>();
            cont.RegisterType<HtmlPresenter>(new InjectionConstructor(new object[] { "F1", "F2" }));
            cont.RegisterType<C1>(new ContainerControlledLifetimeManager());
            cont.RegisterType<C2>(new TransientLifetimeManager());
            cont.RegisterType<C3>();
            cont.RegisterType<C>();
            IApplication app = cont.Resolve<IApplication>();
            app.Run();
            C o = cont.Resolve<C>();
            o.Test();
        }
    }
}

app.config:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <appSettings>
        <add key="DBProvider" value="MySql.Data.MySqlClient" />
        <add key="DBConnectionString" value="Server=localhost;Database=Test;User Id=root;Password=" />
     </appSettings>
</configuration>

Output:

...
Singleton : 1/1 1/1
Transient : 1/2 2/2
Default   : 1/2 2/2

StructureMap open source DI framework was created by Jeremy Miller and released in 2004.

It was officially declared EOL in 2018, but are still used.

Configuration and use works like:

Container cont = new Container(x => {
                                    x.For<SomeInterface>().Use<SomeClass>();
                                    x.For<SomeOtherInterface>().Use<SomeOtherClass>();
                                    });
SomeInterface var = cont.GetInstance<SomeInterface>();

Program.cs:

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.Common;

using StructureMap;

using DI.Common;

namespace DI.StructureMap
{
    public class MockDataAccessLayer : IDataAccessLayer
    {
        public Data GetOneRecord(int f1)
        {
            return new Data { F1 = f1, F2 = "X" + f1 };
        }
        public IList<Data> GetAllRecords()
        {
            IList<Data> res = new List<Data>();
            res.Add(new Data { F1 = 1, F2 = "A" });
            res.Add(new Data { F1 = 2, F2 = "BB" });
            res.Add(new Data { F1 = 3, F2 = "CCC" });
            return res;
       }
    }
    public class DbDataAccessLayer : IDataAccessLayer
    {
        public string Prov { get; set; }
        public string ConStr { get; set; }
        private IDbConnection GetConnection()
        {
            IDbConnection con = DbProviderFactories.GetFactory(Prov).CreateConnection();
            con.ConnectionString = ConStr;
            return con;
        }
        public Data GetOneRecord(int f1)
        {
            using(IDbConnection con = GetConnection())
            {
                con.Open();
                IDbCommand cmd = con.CreateCommand();
                cmd.CommandText = "SELECT f1,f2 FROM t1 WHERE f1 = @f1";
                cmd.Connection = con;
                IDbDataParameter pf1 = cmd.CreateParameter();
                pf1.ParameterName = "@f1";
                pf1.DbType = DbType.Int32;
                pf1.Value = f1;
                cmd.Parameters.Add(pf1);
                IDataReader rdr = cmd.ExecuteReader();
                if(rdr.Read())
                {
                    return new Data { F1 = (int)rdr["f1"], F2 = (string)rdr["f2"] };
                }
                else
                {
                    return null;
                }
            }
        }
        public IList<Data> GetAllRecords()
        {
            IList<Data> res = new List<Data>();
            using(IDbConnection con = GetConnection())
            {
                con.Open();
                IDbCommand cmd = con.CreateCommand();
                cmd.CommandText = "SELECT f1,f2 FROM t1";
                cmd.Connection = con;
                IDataReader rdr = cmd.ExecuteReader();
                while(rdr.Read())
                {
                    res.Add(new Data { F1 = (int)rdr["f1"], F2 = (string)rdr["f2"] });
                }
            }
            return res;
       }
    }
    public class PlainPresenter : IPresenter
    {
        public void Start()
        {
            // nothing
        }
        public void One(Data o)
        {
            Console.WriteLine(o.F1 + " " + o.F2);
        }
        public void Finish()
        {
            // nothing
        }
    }
    public class HtmlPresenter : IPresenter
    {
        public String Lbl1 { get; set; }
        public String Lbl2 { get; set; }
        public void Start()
        {
            Console.WriteLine("<table>");
            Console.WriteLine("<tr>");
            Console.WriteLine("<th>" + Lbl1 + "</th>");
            Console.WriteLine("<th>" + Lbl2 + "</th>");
            Console.WriteLine("</tr>");
        }
        public void One(Data o)
        {
            Console.WriteLine("<tr>");
            Console.WriteLine("<td>" + o.F1 + "</td>");
            Console.WriteLine("<td>" + o.F2 + "</td>");
            Console.WriteLine("</tr>");
        }
        public void Finish()
        {
            Console.WriteLine("</table>");
        }
    }
    public class StandardApplication : IApplication
    {
        private IDataAccessLayer dal;
        private IPresenter p;
        public StandardApplication(IDataAccessLayer dal, IPresenter p)
        {
            this.dal = dal;
            this.p = p;
        }
        public void Run()
        {
            // one
            p.Start();
            Data o = dal.GetOneRecord(2);
            p.One(o);
            p.Finish();
            // all
            p.Start();
            foreach(Data o1 in dal.GetAllRecords())
            {
                p.One(o1);
            }
            p.Finish();
        }
    }
    public class C1
    {
        private static object lck = new object();
        private static int v1 = 0;
        private int v2;
        public C1()
        {
            lock(lck)
            {
                v1++;
                v2 = v1;
            }
        }
        public override string ToString()
        {
            return string.Format("{0}/{1}", v2, v1);
        }
    }
    public class C2
    {
        private static object lck = new object();
        private static int v1 = 0;
        private int v2;
        public C2()
        {
            lock(lck)
            {
                v1++;
                v2 = v1;
            }
        }
        public override string ToString()
        {
            return string.Format("{0}/{1}", v2, v1);
        }
    }
    public class C3
    {
        private static object lck = new object();
        private static int v1 = 0;
        private int v2;
        public C3()
        {
            lock(lck)
            {
                v1++;
                v2 = v1;
            }
        }
        public override string ToString()
        {
            return string.Format("{0}/{1}", v2, v1);
        }
    }
    public class C
    {
        private C1 c1a, c1b;
        private C2 c2a, c2b;
        private C3 c3a, c3b;
        public C(C1 c1a, C1 c1b, C2 c2a, C2 c2b, C3 c3a, C3 c3b)
        {
            this.c1a = c1a;
            this.c1b = c1b;
            this.c2a = c2a;
            this.c2b = c2b;
            this.c3a = c3a;
            this.c3b = c3b;
        }
        public void Test()
        {
            Console.WriteLine("Singleton    : {0} {1}", c1a, c1b);
            Console.WriteLine("AlwaysUnique : {0} {1}", c2a, c2b);
            Console.WriteLine("Default      : {0} {1}", c3a, c3b);
        }
    }
    public class Program
    {
        public static void Main(string[] args)
        {
            Container cont = new Container(x => {
                                                x.For<IApplication>().Use<StandardApplication>();
                                                //x.For<IDataAccessLayer>().Use<MockDataAccessLayer>();
                                                x.For<IDataAccessLayer>().Use<DbDataAccessLayer>().SetProperty(dal => { dal.Prov = ConfigurationManager.AppSettings["DBProvider"]; dal.ConStr = ConfigurationManager.AppSettings["DBConnectionString"];});
                                                //x.For<IPresenter>().Use<PlainPresenter>();
                                                x.For<IPresenter>().Use<HtmlPresenter>().SetProperty(p => { p.Lbl1 = "F1"; p.Lbl2 = "F2"; });
                                                x.For<C1>().Use<C1>().Singleton();
                                                x.For<C2>().Use<C2>().AlwaysUnique();
                                                x.For<C3>().Use<C3>();
                                                x.For<C>().Use<C>();
                                                });
            IApplication app = cont.GetInstance<IApplication>();
            app.Run();
            C o = cont.GetInstance<C>();
            o.Test();
        }
    }
}

app.config:

<?xml version="1.0" encoding="utf-8"?>
<configuration>
    <appSettings>
        <add key="DBProvider" value="MySql.Data.MySqlClient" />
        <add key="DBConnectionString" value="Server=localhost;Database=Test;User Id=root;Password=" />
     </appSettings>
    <startup>
        <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.5" />
    </startup>
</configuration>

Output:

...
Singleton    : 1/1 1/1
AlwaysUnique : 1/2 2/2
Default      : 1/1 1/1

.NET Core added a DI framework that can be added via NuGet called Microsoft.Extensions.DependencyInjection (and it can aactually be used with recent .NET Framework versions as well).

Configuration and use works like:

            IServiceCollection coll = new ServiceCollection();
            coll.AddXxxxxxxxx<SomeInterface, SomeClass>();
            coll.AddXxxxxxxxx<SomeOtherInterface, SomeOtherClass>();
            IServiceProvider cont = coll.BuildServiceProvider();
            SomeInterface var = cont.GetRequiredService<SomeInterface>();

Program.cs:

using System;
using System.Collections.Generic;
using System.Configuration;
using System.Data;
using System.Data.Common;

using Microsoft.Extensions.DependencyInjection;


using DI.Common;

namespace DI.MSExt
{
    public class MockDataAccessLayer : IDataAccessLayer
    {
        public Data GetOneRecord(int f1)
        {
            return new Data { F1 = f1, F2 = "X" + f1 };
        }
        public IList<Data> GetAllRecords()
        {
            IList<Data> res = new List<Data>();
            res.Add(new Data { F1 = 1, F2 = "A" });
            res.Add(new Data { F1 = 2, F2 = "BB" });
            res.Add(new Data { F1 = 3, F2 = "CCC" });
            return res;
        }
    }
    public class DbDataAccessLayer : IDataAccessLayer
    {
        private string prov;
        private string constr;
        private IDbConnection GetConnection()
        {
            IDbConnection con = DbProviderFactories.GetFactory(prov).CreateConnection();
            con.ConnectionString = constr;
            return con;
        }
        public DbDataAccessLayer(String prov, String constr)
        {
            this.prov = prov;
            this.constr = constr;
        }
        public Data GetOneRecord(int f1)
        {
            using (IDbConnection con = GetConnection())
            {
                con.Open();
                IDbCommand cmd = con.CreateCommand();
                cmd.CommandText = "SELECT f1,f2 FROM t1 WHERE f1 = @f1";
                cmd.Connection = con;
                IDbDataParameter pf1 = cmd.CreateParameter();
                pf1.ParameterName = "@f1";
                pf1.DbType = DbType.Int32;
                pf1.Value = f1;
                cmd.Parameters.Add(pf1);
                IDataReader rdr = cmd.ExecuteReader();
                if (rdr.Read())
                {
                    return new Data { F1 = (int)rdr["f1"], F2 = (string)rdr["f2"] };
                }
                else
                {
                    return null;
                }
            }
        }
        public IList<Data> GetAllRecords()
        {
            IList<Data> res = new List<Data>();
            using (IDbConnection con = GetConnection())
            {
                con.Open();
                IDbCommand cmd = con.CreateCommand();
                cmd.CommandText = "SELECT f1,f2 FROM t1";
                cmd.Connection = con;
                IDataReader rdr = cmd.ExecuteReader();
                while (rdr.Read())
                {
                    res.Add(new Data { F1 = (int)rdr["f1"], F2 = (string)rdr["f2"] });
                }
            }
            return res;
        }
    }
    public class PlainPresenter : IPresenter
    {
        public void Start()
        {
            // nothing
        }
        public void One(Data o)
        {
            Console.WriteLine(o.F1 + " " + o.F2);
        }
        public void Finish()
        {
            // nothing
        }
    }
    public class HtmlPresenter : IPresenter
    {
        private String lbl1;
        private String lbl2;
        public HtmlPresenter(String lbl1, String lbl2)
        {
            this.lbl1 = lbl1;
            this.lbl2 = lbl2;
        }
        public void Start()
        {
            Console.WriteLine("<table>");
            Console.WriteLine("<tr>");
            Console.WriteLine("<th>" + lbl1 + "</th>");
            Console.WriteLine("<th>" + lbl2 + "</th>");
            Console.WriteLine("</tr>");
        }
        public void One(Data o)
        {
            Console.WriteLine("<tr>");
            Console.WriteLine("<td>" + o.F1 + "</td>");
            Console.WriteLine("<td>" + o.F2 + "</td>");
            Console.WriteLine("</tr>");
        }
        public void Finish()
        {
            Console.WriteLine("</table>");
        }
    }
    public class StandardApplication : IApplication
    {
        private IDataAccessLayer dal;
        private IPresenter p;
        public StandardApplication(IDataAccessLayer dal, IPresenter p)
        {
            this.dal = dal;
            this.p = p;
        }
        public void Run()
        {
            // one
            p.Start();
            Data o = dal.GetOneRecord(2);
            p.One(o);
            p.Finish();
            // all
            p.Start();
            foreach (Data o1 in dal.GetAllRecords())
            {
                p.One(o1);
            }
            p.Finish();
        }
    }
    public class C1
    {
        private static object lck = new object();
        private static int v1 = 0;
        private int v2;
        public C1()
        {
            lock (lck)
            {
                v1++;
                v2 = v1;
            }
        }
        public override string ToString()
        {
            return string.Format("{0}/{1}", v2, v1);
        }
    }
    public class C2
    {
        private static object lck = new object();
        private static int v1 = 0;
        private int v2;
        public C2()
        {
            lock (lck)
            {
                v1++;
                v2 = v1;
            }
        }
        public override string ToString()
        {
            return string.Format("{0}/{1}", v2, v1);
        }
    }
    public class C3
    {
        private static object lck = new object();
        private static int v1 = 0;
        private int v2;
        public C3()
        {
            lock (lck)
            {
                v1++;
                v2 = v1;
            }
        }
        public override string ToString()
        {
            return string.Format("{0}/{1}", v2, v1);
        }
    }
    public class C
    {
        private C1 c1a, c1b;
        private C2 c2a, c2b;
        public C(C1 c1a, C1 c1b, C2 c2a, C2 c2b)
        {
            this.c1a = c1a;
            this.c1b = c1b;
            this.c2a = c2a;
            this.c2b = c2b;
        }
        public void Test()
        {
            Console.WriteLine("Singleton : {0} {1}", c1a, c1b);
            Console.WriteLine("Transient : {0} {1}", c2a, c2b);
        }
    }
    public class Program
    {
        public static void Main(string[] args)
        {
            IServiceCollection coll = new ServiceCollection();
            coll.AddSingleton<IApplication, StandardApplication>();
            //coll.AddSingleton<IDataAccessLayer, MockDataAccessLayer>();
            coll.AddSingleton<IDataAccessLayer>(s => ActivatorUtilities.CreateInstance<DbDataAccessLayer>(s, ConfigurationManager.AppSettings["DBProvider"], ConfigurationManager.AppSettings["DBConnectionString"]));
            //coll.AddSingleton<IPresenter, PlainPresenter>();
            coll.AddSingleton<IPresenter>(s => ActivatorUtilities.CreateInstance<HtmlPresenter>(s, "F1", "F2"));
            coll.AddSingleton<C1>();
            coll.AddTransient<C2>();
            coll.AddSingleton<C>();
            IServiceProvider cont = coll.BuildServiceProvider();
            IApplication app = cont.GetRequiredService<IApplication>();
            app.Run();
            C o = cont.GetRequiredService<C>();
            o.Test();
        }
    }
}

app.config:

<?xml version="1.0" encoding="utf-8" ?>
<configuration>
  <appSettings>
    <add key="DBProvider" value="MySql.Data.MySqlClient" />
    <add key="DBConnectionString" value="Server=localhost;Database=Test;User Id=root;Password=" />
  </appSettings>
  <startup> 
     <supportedRuntime version="v4.0" sku=".NETFramework,Version=v4.7.2" />
   </startup>
</configuration>

Output:

...
Singleton : 1/1 1/1
Transient : 1/2 2/2

dependency_injector is an open source Python DI framework. First version was released in 2015.

Usage:

some_factory = providers.Factory(SomeClass,arg1=someval1,arg2=someval2)
some_other_factory = providers.Factory(SomeOtherClass,arg=some_factory)
var = some_other_factory()

di.py:

import collections
import dependency_injector.providers as providers
import pymysql

from common import *          

class MockDataAccessLayer:
    def get_one_record(self, f1):
        return Data(f1, 'X' + str(f1))
    def get_all_records(self):
        return [ Data(1, 'A'), Data(2, 'BB'), Data(3, 'CCC')]

class DbDataAccessLayer:
    def __init__(self, con):
        self.con = con
    def get_one_record(self, f1):
        c = self.con.cursor()
        c.execute('SELECT f2 FROM t1 WHERE f1 = %s', (f1,))
        f2 = c.fetchone()[0]
        c.close()
        return Data(f1, f2)
    def get_all_records(self):
        c = self.con.cursor()
        c.execute('SELECT f1,f2 FROM t1')
        res = []
        for row in c.fetchall():
            res.append(Data(row[0], row[1]))
        c.close()
        return res

class PlainPresenter:
    def start(self):
        pass
    def one(self, o):
        print('%d %s' % (o.f1, o.f2))
    def finish(self):
        pass

class HtmlPresenter:
    def __init__(self, lbl1, lbl2):
        self.lbl1 = lbl1
        self.lbl2 = lbl2
    def start(self):
        print('<table>')
        print('<tr>')
        print('<th>%s</th>' % (self.lbl1))
        print('<th>%s</th>' % (self.lbl2))
        print('</tr>')
    def one(self, o):
        print('<tr>')
        print('<td>%s</td>' % (o.f1))
        print('<td>%s</td>' % (o.f2))
        print('</tr>')
    def finish(self):
        print('</table>')

class StandardApplication:
    def __init__(self, dal, p):
        self.dal = dal
        self.p = p
    def run(self):
        self.p.start()
        o = self.dal.get_one_record(2)
        self.p.one(o)
        self.p.finish()
        self.p.start()
        for o1 in  self.dal.get_all_records():
            self.p.one(o1)
        self.p.finish()

#dal_factory = providers.Factory(MockDataAccessLayer)
dal_factory = providers.Factory(DbDataAccessLayer, pymysql.connect(host='localhost',user='root',password='',db='Test'))
#p_factory = providers.Factory(PlainPresenter)
p_factory = providers.Factory(HtmlPresenter, 'F1', 'F2')
app_factory = providers.Factory(StandardApplication,dal=dal_factory,p=p_factory)

app = app_factory()
app.run()

inst.py:

import collections
import dependency_injector.providers as providers

class C1:
    v1 = 0
    def __init__(self):
        C1.v1 = C1.v1 + 1
        self.v2 = C1.v1
    def __str__(self):
        return '%d/%d' % (self.v2, C1.v1)

class C2:
    v1 = 0
    def __init__(self):
        C2.v1 = C2.v1 + 1
        self.v2 = C2.v1
    def __str__(self):
        return '%d/%d' % (self.v2, C2.v1)

class C:
    def __init__(self, c1a, c1b, c2a, c2b):
        self.c1a = c1a
        self.c1b = c1b
        self.c2a = c2a
        self.c2b = c2b
    def test(self):
        print('Factory   : %s %s' % (self.c1a, self.c1b))
        print('Singleton : %s %s' % (self.c2a, self.c2b))

c1_factory = providers.Factory(C1)
c2_singleton = providers.Singleton(C2)
c_factory = providers.Factory(C,c1a=c1_factory,c1b=c1_factory,c2a=c2_singleton,c2b=c2_singleton)

o = c_factory()
o.test()

Output:

Factory   : 1/2 2/2
Singleton : 1/1 1/1

PHP-DI is an open source PHP DI framework first released around 2012.

Usage:

$builder = new ContainerBuilder();
$builder->addDefinitions([
                         SomeInterface::class => create(SomeClass::class)->constructor(get(SomeOtherInterface::class)),
                         SomeOtherInterface::class => create(SomeOther::class)->constructor(val1, val2)
                         ]);
$cont = $builder->build();
$var = $cont->get(SomeInterface::class);

di.php:

<?php
spl_autoload_register(function ($clznam) {
    include $clznam . '.php';
});
    
require_once 'common.php';

class MockDataAccessLayer implements DataAccessLayer {
    public function getOneRecord($f1) {
        return new Data($f1, 'X' . $f1);    
    }
    public function getAllRecords() {
        $res = array();
        $res[] = new Data(1, 'A');
        $res[] = new Data(2, 'BB');
        $res[] = new Data(3, 'CCC');
        return $res;
    }
}

class DbDataAccessLayer implements DataAccessLayer {
    private $constr;
    private $un;
    private $pw;
    private function get_connection() {
        $con = new PDO($this->constr, $this->un, $this->pw);
        $con->setAttribute(PDO::ATTR_ERRMODE, PDO::ERRMODE_EXCEPTION);
        $con->setAttribute(PDO::ATTR_DEFAULT_FETCH_MODE, PDO::FETCH_ASSOC);
        return $con;
    }
    public function __construct($constr, $un, $pw) {
        $this->constr = $constr;
        $this->un = $un;
        $this->pw = $pw;
    }
    public function getOneRecord($f1) {
        
        $con = $this->get_connection();
        $stmt = $con->prepare('SELECT f2 FROM t1 WHERE f1 = :f1');
        $stmt->execute(array(':f1' => $f1));
        if($row = $stmt->fetch()) {
            $f2 = $row['f2'];
            return new Data($f1, $f2);
        } else {
            return null;
        }
    }
    public function getAllRecords() {
        $con = $this->get_connection();
        $stmt = $con->prepare('SELECT f1,f2 FROM t1');
        $stmt->execute(array());
        $res = array();
        while($row = $stmt->fetch()) {
            $f1 = $row['f1'];
            $f2 = $row['f2'];
            $res[] = new Data($f1, $f2);
        }
        return $res;
    }
}

class PlainPresenter implements Presenter {
    public function start() {
        // nothing
    }
    public function one($o) {
        echo $o->f1 . ' ' . $o->f2 . "\r\n";
    }
    public function finish() {
        // nothing
    }
}

class HtmlPresenter implements Presenter {
    private $lbl1;
    private $lbl2;
    public function __construct($lbl1, $lbl2) {
        $this->lbl1 = $lbl1;
        $this->lbl2 = $lbl2;
    }
    public function start() {
        echo "<table>\r\n";
        echo "<tr>\r\n";
        echo "<th>" . $this->lbl1 . "</th>\r\n";
        echo "<th>" . $this->lbl2 . "</th>\r\n";
        echo "</tr>\r\n";
    }
    public function one($o) {
        echo "<tr>\r\n";
        echo "<td>" . $o->f1 . "</td>\r\n";
        echo "<td>" . $o->f2 . "</td>\r\n";
        echo "</tr>\r\n";
    }
    public function finish() {
        echo "</table>\r\n";
    }
}

class StandardApplication implements Application {
    private $dal;
    private $p;
    public function __construct(DataAccessLayer $dal, Presenter $p) {
        $this->dal = $dal;
        $this->p = $p;
    }
    public function run() {
        $this->p->start();
        $o = $this->dal->getOneRecord(2);
        $this->p->one($o);
        $this->p->finish();
        $this->p->start();
        foreach($this->dal->getAllRecords() as $o1) {
            $this->p->one($o1);
        }
        $this->p->finish();
    }
}

include 'DI/functions.php';
    
use DI\ContainerBuilder;
use function DI\create;
use function DI\get;

$builder = new ContainerBuilder();
$builder->addDefinitions([
                         Application::class => create(StandardApplication::class)->constructor(get(DataAccessLayer::class), get(Presenter::class)),
                         //DataAccessLayer::class => create(MockDataAccessLayer::class),
                         DataAccessLayer::class => create(DbDataAccessLayer::class)->constructor(get('constr'), get('un'), get('pw')),
                         'constr' => 'mysql:host=localhost;dbname=Test',
                         'un' => 'root',
                         'pw' => '',
                         //Presenter::class => create(PlainPresenter::class),
                         Presenter::class => create(HtmlPresenter::class)->constructor(get('lbl1'), get('lbl2')),
                         'lbl1' => 'F1',
                         'lbl2' => 'F2'
                         ]);
$cont = $builder->build();
$app = $cont->get(Application::class);
$app->run();

?>

inst.php:

<?php
spl_autoload_register(function ($clznam) {
    include $clznam . '.php';
});

 class C1 {
    private static $v1 = 0;
    private $v2;
    public function __construct() {
        C1::$v1++;
        $this->v2 = C1::$v1;
    }
    public function __toString() {
        return sprintf('%d/%d', $this->v2, C1::$v1);
    }
 }

 class C2 {
     private static $v1 = 0;
     private $v2;
     public function __construct() {
         C2::$v1++;
         $this->v2 = C2::$v1;
     }
     public function __toString() {
         return sprintf('%d/%d', $this->v2, C2::$v1);
     }
 }
 
 class C2Factory {
     public function createC2() {
         return new C2();
     }
 }
 
 class C {
     private $c1a;
     private $c1b;
     private $c2a;
     private $c2b;
     public function __construct(C1 $c1a, C1 $c1b, C2Factory $c2fa, C2Factory $c2fb) {
         $this->c1a = $c1a;
         $this->c1b = $c1b;
         $this->c2a = $c2fa->createC2();
         $this->c2b = $c2fb->createC2();
     }
     public function test() {
         echo sprintf("Default : %s %s\r\n", $this->c1a, $this->c1b);
         echo sprintf("Factory : %s %s\r\n", $this->c2a, $this->c2b);
     }
 }

include 'DI/functions.php';
 
use DI\ContainerBuilder;
use function DI\create;
use function DI\get;

$builder = new ContainerBuilder();
$builder->addDefinitions([C::class => create(C::class)->constructor(get(C1::class), get(C1::class), get(C2Factory::class), get(C2Factory::class)),
                          C1::class => create(C1::class),
                          C2Factory::class => create(C2Factory::class)
                         ]);
$cont = $builder->build();
$o = $cont->get(C::class);
$o->test();

?>

Output:

Default : 1/1 1/1
Factory : 1/2 2/2

Previous versions of PHP-DI had ->scope(Scope::SINGLETON) and ->scope(Scope::PROTOTYPE), but current version does not. Instead one need to inject a factory.

Evaluation:

Two natural questions arise:

It is an observable fact that DI is widely used in the Java and .NET worlds, but not so much in script languages.

Whether to use DI or not to use DI is a subjective question.

For Java and .NET programming I will recommend using DI moderately. DI provide value to glue components/pieces together in a decoupled manner. I would avoid using DI for everything (socalled "new free" programming) as that will make the code less readable for no additional benefits.

For Java I will recommend using JSR 330 annotations as I am a true standards guy. As implementation Spring is pretty obvious as it is extremely likely that something else in the application requires Spring.

For .NET I will recommend Unity or Core/MS Extension DI. There are many frameworks available for .NET and most of them work the same way, but these are very standard in approach and backed by MS.

For scripting languages I would avoid DI. The decoupling provided by DI is not as valuable in such dynamic languages.

Article history:

Version Date Description
1.0 June 13th 2020 Initial version
1.1 October 9th 2020 Add Core/MS Extension DI

Other articles:

See list of all articles here

Comments:

Please send comments to Arne Vajhøj