4. Wstrzykiwanie zależności
Wstrzykiwanie zależności można uznać za konsekwencję odwrócenia kontroli. Zilustrujemy to na nowym przykładzie. Rozważmy następującą trójwarstwową aplikację internetową:
![]() |
Załóżmy, że dostęp do warstwy DAO jest kontrolowany przez omówiony wcześniej interfejs [IArticlesDao] oraz że cała aplikacja jest aplikacją internetową służącą do zakupu artykułów w sieci. Artykuły są zarządzane przez warstwę [Dao]. Zakładamy, że dostęp do warstwy biznesowej jest kontrolowany przez następujący interfejs:
Public Interface IArticlesDomain
' zakup koszyka z artykułami
Sub acheter(ByVal panier As Panier)
' pobieranie listy artykułów
Function getAllArticles() As IList
' pobieranie konkretnego artykułu
Function getArticleById(ByVal idArticle As Integer) As Article
' w celu zarejestrowania błędów
ReadOnly Property erreurs() As ArrayList
End Interface
Nie będziemy się tutaj zagłębiać w znaczenie poszczególnych metod. Wystarczy zauważyć, że metoda [getAllArticles], która ma pobrać listę wszystkich artykułów w sprzedaży, potrzebuje dostępu do danych. Aby je uzyskać, musi zwrócić się do interfejsu [IArticlesDao]. Szkielet klasy implementującej interfejs [IArticlesDomain] mógłby wyglądać następująco:
Imports istia.st.articles.dao
...
Namespace istia.st.articles.domain
Public Class AchatsArticles
Implements IArticlesDomain
'pola prywatne
Private _articlesDao As IArticlesDao
Private _erreurs As ArrayList
' konstruktor
Public Sub New(ByVal articlesDao As IArticlesDao)
_articlesDao = articlesDao
End Sub
...
End Class
End Namespace
Jak już wspomnieliśmy, niektóre metody interfejsu warstwy biznesowej muszą pobierać dane z warstwy [Dao]. Nasza klasa implementacyjna [AchatsArticles] interfejsu [IArticlesDomain] wymaga zatem odwołania do implementacji interfejsu [IArticlesDao]. W powyższym przykładzie odwołaniem tym jest pole prywatne [_articlesDao]. Odwołanie to jest dostarczane w momencie tworzenia obiektu [AchatsArticles].
Załóżmy, że napisano klasę [AchatsArticles] i chcemy ją przetestować za pomocą testu typu [Nunit]. Stwórzmy ten test tak, aby korzystał z pliku konfiguracyjnego Springa:
...
Imports Spring.Objects.Factory.Xml
Imports System.IO
...
<TestFixture()> _
Public Class NunitSpringTestArticlesDomain
' obiekt do testowania
Private articlesDomain As IArticlesDomain
<SetUp()> _
Public Sub init()
' pobieramy instancję konstruktora obiektów Spring
Dim factory As XmlObjectFactory = New XmlObjectFactory(New FileStream("spring-config-domain.xml", FileMode.Open))
' wymagane jest utworzenie instancji obiektu DAO „articles”
articlesDomain = CType(factory.GetObject("articlesdomain"), IArticlesDao)
End Sub
...
End Class
Jaka byłaby zawartość pliku konfiguracyjnego [spring-config-domain.xml] w Spring? Mógłby on wyglądać następująco:
<?xml version="1.0" encoding="iso-8859-1" ?>
<!DOCTYPE objects PUBLIC "-//SPRING//DTD OBJECT//EN"
"http://www.springframework.net/dtd/spring-objects.dtd">
<objects>
<description>Gestion d'une table d'articles</description>
<!-- klasa implementująca interfejs IArticlesDao -->
<object id="articlesdao" type="istia.st.articles.dao.ArticlesDaoPlainODBC, articlesdao">
<constructor-arg index="0">
<value>odbc-firebird-articles</value>
</constructor-arg>
<constructor-arg index="1">
<value>SYSDBA</value>
</constructor-arg>
<constructor-arg index="2">
<value>masterkey</value>
</constructor-arg>
</object>
<!-- klasa implementująca interfejs IArticlesDomain -->
<object id="articlesdomain" type="istia.st.articles.domain.AchatsArticles, articlesdomain">
<constructor-arg index="0">
<ref object="articlesdao" />
</constructor-arg>
</object>
</objects>
Ten plik to ten sam, który został już użyty do instancjonowania singletonu typu [IArticlesDao] z warstwy [Dao], do którego dodano kod służący do instancjonowania singletonu typu [IArticlesDomain] z warstwy biznesowej. W jaki sposób zostanie on skonstruowany?
- Zewnętrzny kod zwraca się do Springa z prośbą o odwołanie do singletonu o nazwie „articlesdomain” w pliku konfiguracyjnym. Tak jest w przypadku metody [init] naszej klasy testowej:
<SetUp()> _
Public Sub init()
' pobieramy instancję generatora obiektów Spring
Dim factory As XmlObjectFactory = New XmlObjectFactory(New FileStream("spring-config-domain.xml", FileMode.Open))
' żądamy instancjonowania obiektu DAO „articles”
articlesDomain = CType(factory.GetObject("articlesdomain"), IArticlesDao)
End Sub
- Spring odnajduje w swoim pliku konfiguracyjnym definicję danego singletonu. Stwierdza, że do utworzenia jego instancji potrzebuje innego singletonu o nazwie „articlesdao”:
<object name="articlesdomain" class="istia.st.articles.domain.AchatsArticles, articlesdomain">
<constructor-arg index="0">
<ref object="articlesdao" />
</constructor-arg>
</object>
- Spring utworzy wówczas instancję singletonu „articlesdao”. Wyjaśniliśmy już, w jaki sposób to robi.
- Po wykonaniu tej czynności Spring może utworzyć instancję singletonu „articlesdomain” i zwrócić odwołanie do niego kodowi, który o to poprosił.
Mechanizm ten można przedstawić schematycznie w następujący sposób:
![]() |
Widać, że Spring zarządzał zależnością singletonu „articlesdomain” względem singletonu „articlesdao”. Aby korzystać ze Springa w ten sposób, musieliśmy stworzyć klasę z konstruktorem, który jako argument przyjmuje singleton, od którego jest zależna:
Imports istia.st.articles.dao
...
Namespace istia.st.articles.domain
Public Class AchatsArticles
Implements IArticlesDomain
'pola prywatne
Private _articlesDao As IArticlesDao
' konstruktor
Public Sub New(ByVal articlesDao As IArticlesDao)
_articlesDao = articlesDao
End Sub
...
End Class
End Namespace
Termin „wstrzykiwanie zależności” obejmuje zarówno:
- specyficzny sposób tworzenia klas przeznaczonych do instancjonowania w oparciu o ich zależności
- sposób, w jaki Spring zarządza tymi zależnościami w momencie instancjonowania tych klas

