Was ist die Spring Dependency Injection?

Heute werden wir uns das Spring Dependency Injection ansehen. Die Kernkonzepte des Spring Frameworks sind „Dependency Injection“ und „Aspect Oriented Programming“.

Spring Dependency Injection

Dieses Tutorial zielt darauf ab, Details über das Spring Dependency Injection Beispiel sowohl mit annotationbasierter Konfiguration als auch mit XML-Dateibasierter Konfiguration zu liefern. Ich werde auch ein JUnit Beispiel für die Anwendung bereitstellen, da die einfache Testbarkeit einer der Hauptvorteile der Dependency Injection ist. Ich habe ein spring-dependency-injection Maven-Projekt erstellt, dessen Struktur wie das untenstehende Bild aussieht.

Lassen Sie uns jedes der Komponenten einzeln betrachten.

Spring Dependency Injection – Maven-Abhängigkeiten

Ich habe Spring- und JUnit-Maven-Abhängigkeiten in der pom.xml-Datei hinzugefügt, der endgültige pom.xml-Code ist unten.

<project xmlns="https://maven.apache.org/POM/4.0.0" xmlns:xsi="https://www.w3.org/2001/XMLSchema-instance"
	xsi:schemaLocation="https://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
	<modelVersion>4.0.0</modelVersion>
	<groupId>com.journaldev.spring</groupId>
	<artifactId>spring-dependency-injection</artifactId>
	<version>0.0.1-SNAPSHOT</version>

	<dependencies>
		<dependency>
			<groupId>org.springframework</groupId>
			<artifactId>spring-context</artifactId>
			<version>4.0.0.RELEASE</version>
		</dependency>
		<dependency>
			<groupId>junit</groupId>
			<artifactId>junit</artifactId>
			<version>4.8.1</version>
			<scope>test</scope>
		</dependency>
	</dependencies>

</project>

Die aktuelle stabile Version des Spring Frameworks ist 4.0.0.RELEASE und die aktuelle Version von JUnit ist 4.8.1. Wenn Sie andere Versionen verwenden, besteht eine geringe Chance, dass das Projekt einige Änderungen benötigen könnte. Wenn Sie das Projekt erstellen, werden Sie feststellen, dass aufgrund transitiver Abhängigkeiten auch einige andere Jars zu den Maven-Abhängigkeiten hinzugefügt werden, genau wie im obigen Bild.

Service-Klassen

Lassen Sie uns sagen, wir möchten E-Mail-Nachrichten und Twitter-Nachrichten an die Benutzer senden. Für Dependency Injection benötigen wir eine Basisklasse für die Dienste. Daher habe ich die MessageService-Schnittstelle mit einer einzigen Methodendeklaration zum Senden von Nachrichten.

package com.journaldev.spring.di.services;

        public interface MessageService {

            boolean sendMessage(String msg, String rec);
        }

Jetzt werden wir tatsächliche Implementierungsklassen haben, um E-Mail- und Twitter-Nachrichten zu senden.


package com.journaldev.spring.di.services;

        public class EmailService implements MessageService {

            public boolean sendMessage(String msg, String rec) {
                System.out.println("E-Mail gesendet an " + rec + " mit Nachricht=" + msg);
                return true;
            }

        }


package com.journaldev.spring.di.services;

        public class TwitterService implements MessageService {

            public boolean sendMessage(String msg, String rec) {
                System.out.println("Twitter-Nachricht gesendet an " + rec + " mit Nachricht=" + msg);
                return true;
            }

        }

Jetzt, da unsere Dienste bereit sind, können wir zu den Component Classes übergehen, die den Service nutzen werden.

Component Classes

Lassen Sie uns eine Verbrauchsklasse für die oben genannten Dienste schreiben. Wir werden zwei Consumer Classes haben – eine mit Spring-Annotationen für das Autowiring und eine andere ohne Annotation, wobei die Wiring Configurations in der XML-Konfigurationsdatei bereitgestellt werden.

package com.journaldev.spring.di.consumer;

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

        import com.journaldev.spring.di.services.MessageService;

        @Component
        public class MyApplication {

            //feldbasierte Dependency Injection
            //@Autowired
            private MessageService service;
            
            //konstruktorbasierte Dependency Injection    
            //@Autowired
            //public MyApplication(MessageService svc){
            //    this.service=svc;
            //}
            
            @Autowired
            public void setService(MessageService svc){
                this.service=svc;
            }
            
            public boolean processMessage(String msg, String rec){
                //etwas Magie wie Validierung, Logging etc.
                return this.service.sendMessage(msg, rec);
            }
        }

Einige wichtige Punkte über die MyApplication-Klasse:

  • Die @Component-Annotation wird der Klasse hinzugefügt, sodass die Spring-Framework diese Klasse beim Scannen nach Komponenten als Komponente behandelt. Die @Component-Annotation kann nur auf die Klasse angewendet werden und ihre Retention Policy ist Laufzeit.
  • Die @Autowired-Annotation wird verwendet, um Spring zu signalisieren, dass Autowiring erforderlich ist. Diese kann auf Felder, Konstruktoren und Methoden angewendet werden.

Für unser Beispiel verwende ich methodenbasierte Dependency Injection. Sie können die Konstruktormethode auskommentieren, um zur Constructor-basierten Dependency Injection zu wechseln.

Lassen Sie uns nun eine ähnliche Klasse ohne Annotationen schreiben.

package com.journaldev.spring.di.consumer;

        import com.journaldev.spring.di.services.MessageService;

        public class MyXMLApplication {

            private MessageService service;

            //konstruktorbasierte Dependency Injection
            //public MyXMLApplication(MessageService svc) {
            //    this.service = svc;
            //}
            
            //setter-basierte Dependency Injection
            public void setService(MessageService svc){
                this.service=svc;
            }

            public boolean processMessage(String msg, String rec) {
                // etwas Magie wie Validierung, Logging etc
                return this.service.sendMessage(msg, rec);
            }
        }

Eine einfache Anwendungsklasse, die den Dienst nutzt. Für XML-basierte Konfiguration können wir entweder konstruktorbasierte Spring Dependency Injection oder methodenbasierte Spring Dependency Injection implementieren. Beachten Sie, dass methodenbasierte und setter-basierte Injektionsansätze gleich sind, es ist nur so, dass einige es setter-basiert nennen und andere es methodenbasiert nennen.

Spring Dependency Injection Konfiguration mit Annotationen

Für eine annotationbasierte Konfiguration müssen wir eine Configurator-Klasse schreiben, die verwendet wird, um die eigentliche Implementierungs-Bean in die Komponenteneigenschaft zu injizieren.

package com.journaldev.spring.di.configuration;

        import org.springframework.context.annotation.Bean;
        import org.springframework.context.annotation.ComponentScan;
        import org.springframework.context.annotation.Configuration;

        import com.journaldev.spring.di.services.EmailService;
        import com.journaldev.spring.di.services.MessageService;

        @Configuration
        @ComponentScan(value={"com.journaldev.spring.di.consumer"})
        public class DIConfiguration {

            @Bean
            public MessageService getMessageService(){
                return new EmailService();
            }
        }

Einige wichtige Punkte zur obigen Klasse sind:

  • Die @Configuration-Annotation wird verwendet, um Spring zu signalisieren, dass es sich um eine Konfigurationsklasse handelt.
  • Die @ComponentScan-Annotation wird zusammen mit der @Configuration-Annotation verwendet, um die Pakete zu spezifizieren, in denen nach Component-Klassen gesucht werden soll.
  • Die @Bean-Annotation wird verwendet, um dem Spring-Framework mitzuteilen, dass diese Methode verwendet werden soll, um die Bean-Implementierung in Component-Klassen zu injizieren.

Testen der Annotation-basierten Spring Dependency Injection

package com.journaldev.spring.di.test;

        import org.springframework.context.annotation.AnnotationConfigApplicationContext;

        import com.journaldev.spring.di.configuration.DIConfiguration;
        import com.journaldev.spring.di.consumer.MyApplication;

        public class ClientApplication {

            public static void main(String[] args) {
                AnnotationConfigApplicationContext context = new AnnotationConfigApplicationContext(DIConfiguration.class);
                MyApplication app = context.getBean(MyApplication.class);
                
                app.processMessage("Hi Pankaj", "pankaj@abc.com");
                
                //schließe den Kontext
                context.close();
            }

        }

AnnotationConfigApplicationContext ist die Implementierung der abstrakten Klasse AbstractApplicationContext und wird für das Autowiring der Dienste zu Komponenten verwendet, wenn Annotationen verwendet werden…

XML-basierte Konfiguration

Wir werden eine Spring-Konfigurationsdatei mit den unten stehenden Daten erstellen, der Dateiname kann beliebig sein. applicationContext.xml-Code:

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

        <!-- 
            <bean id="MyXMLApp" class="com.journaldev.spring.di.consumer.MyXMLApplication">
                <constructor-arg>
                    <bean class="com.journaldev.spring.di.services.TwitterService" />
                </constructor-arg>
            </bean>
        -->
            <bean id="twitter" class="com.journaldev.spring.di.services.TwitterService"></bean>
            <bean id="MyXMLApp" class="com.journaldev.spring.di.consumer.MyXMLApplication">
                <property name="service" ref="twitter"></property>
            </bean>
        </beans>

Beachten Sie, dass das obige XML Konfigurationen für sowohl konstruktorbasierte als auch setter-basierte Spring Dependency Injection enthält…

Testen der XML-basierten Konfiguration

package com.journaldev.spring.di.test;

        import org.springframework.context.support.ClassPathXmlApplicationContext;

        import com.journaldev.spring.di.consumer.MyXMLApplication;

        public class ClientXMLApplication {

            public static void main(String[] args) {
                ClassPathXmlApplicationContext context = new ClassPathXmlApplicationContext(
                        "applicationContext.xml");
                MyXMLApplication app = context.getBean(MyXMLApplication.class);

                app.processMessage("Hi Pankaj", "pankaj@abc.com");

                // schließe den Kontext
                context.close();
            }

        }

ClassPathXmlApplicationContext wird verwendet, um das ApplicationContext-Objekt durch Bereitstellung des Standorts der Konfigurationsdateien zu erhalten…

Spring Dependency Injection JUnit Testfall

Einer der großen Vorteile der Dependency Injection in Spring ist die einfache Verwendung von Mock-Serviceklassen anstelle von tatsächlichen Diensten. Daher habe ich alle Lerninhalte von oben kombiniert und alles in einer einzigen JUnit 4 Testklasse für Dependency Injection in Spring geschrieben.

package com.journaldev.spring.di.test;

        import org.junit.Assert;
        import org.junit.After;
        import org.junit.Before;
        import org.junit.Test;
        import org.springframework.context.annotation.AnnotationConfigApplicationContext;
        import org.springframework.context.annotation.Bean;
        import org.springframework.context.annotation.ComponentScan;
        import org.springframework.context.annotation.Configuration;

        import com.journaldev.spring.di.consumer.MyApplication;
        import com.journaldev.spring.di.services.MessageService;

        @Configuration
        @ComponentScan(value="com.journaldev.spring.di.consumer")
        public class MyApplicationTest {
            
            private AnnotationConfigApplicationContext context = null;

            @Bean
            public MessageService getMessageService() {
                return new MessageService(){

                    public boolean sendMessage(String msg, String rec) {
                        System.out.println("Mock-Service");
                        return true;
                    }
                    
                };
            }

            @Before
            public void setUp() throws Exception {
                context = new AnnotationConfigApplicationContext(MyApplicationTest.class);
            }
            
            @After
            public void tearDown() throws Exception {
                context close();
            }

            @Test
            public void test() {
                MyApplication app = context.getBean(MyApplication.class);
                Assert.assertTrue(app.processMessage("Hi Pankaj", "pankaj@abc.com"));
            }

        }

Die Klasse ist mit @Configuration und @ComponentScan Annotationen annotiert, weil die getMessageService() Methode die Mock-Implementierung von MessageService zurückgibt. Deshalb ist getMessageService() mit der @Bean-Annotation annotiert. Da ich die MyApplication-Klasse teste, die mit Annotationen konfiguriert ist, verwende ich AnnotationConfigApplicationContext und erstelle dessen Objekt in der setUp()-Methode. Der Kontext wird in der tearDown()-Methode geschlossen. Die test()-Methode holt einfach das Component Object aus dem Kontext und testet es. Sie fragen sich vielleicht, wie das Spring Framework das Autowiring durchführt und Methoden aufruft, die dem Spring Framework unbekannt sind. Dies wird mit der intensiven Verwendung von Java Reflection erreicht, die wir nutzen können, um das Verhalten der Klassen zur Laufzeit zu analysieren und zu modifizieren.

Kostenlosen Account erstellen

Registrieren Sie sich jetzt und erhalten Sie Zugang zu unseren Cloud Produkten.

Das könnte Sie auch interessieren: